diff --git a/DEPS b/DEPS
index 12f6c891..6852a58 100644
--- a/DEPS
+++ b/DEPS
@@ -299,15 +299,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'c8f54c1bc565b8a7eced0a77088b228efdc70c2c',
+  'skia_revision': 'ac2d5ceecb588e3be2a798896b92d27c91ad6461',
   # 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': '3005b00df2a95266a853252e20e78f637adc5c2f',
+  'v8_revision': '84b3ee97fdd8f2df1e477b0cc79b893707da21b1',
   # 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': '8357b6a24cae20d24058cfef3277066c4993a1ae',
+  'angle_revision': '2d61c576f0dd8a1a5ef5adeefe19101c5ea64eb7',
   # 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': '47e5e3d994ddcc2319cec110ce670ccc1baa3223',
+  'crossbench_revision': '35e8177a7d7594203c543fe3875fd587b949fca2',
   # 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': '28fbee169c8a26c6c2ed2ec2550465fe3d697a34',
+  'devtools_frontend_revision': 'cd650159ff9210d83ed1e1aaf02b1c071d5123b3',
   # 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': '968646ae6a99b1751d5a7a4298e3bf6976123326',
+  'dawn_revision': '5b595fdbcfc1d0d79354ab74f675c564273874c7',
   # 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,11 +523,11 @@
   # 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': 'cff6ae8a80960ceca4d83cf6efa271222d2e7d85',
+  'compiler_rt_revision': '16c0e42913a45357f0e557dcc0b7055129c78af6',
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
-  'libcxx_revision':       'a9cc573e7c591795d11b72d8323ba0e573b55bd6',
+  'libcxx_revision':       'a01c02c9d4acbdae3b7e8a2f3ee58579a9c29f96',
 
   # GN CIPD package version.
   'gn_version': 'git_revision:487f8353f15456474437df32bb186187b0940b45',
@@ -1486,7 +1486,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '11c3547fd4f0706067ad840ff190038861085741',
+    '4326f348545fb5ef5cbb59389e7e3a903d2320eb',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1978,7 +1978,7 @@
 
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '18580cf9c407a3c23aa072b9d745832516de699e',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'fab0a4296b8830c569a4dc82285bfb58a5c2fca8',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -2703,7 +2703,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/r8',
-              'version': 'sxgLKZyJNZMVi8cot5yvRzqCFQxX_HbbR0psaeto5RcC',
+              'version': 'Is-oa6b7OQ1cVHjvG42iz-Lha-F3MBxBBIyC_eTMTg4C',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -3027,7 +3027,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'EaqAC4qcuy5Hq-7VdbO8W5ruTjiF684Mwn0dHuKfXmUC',
+        'version': '6CJPIYRk3LvYL2TI2hdXpgt3mj7_1EbDw3FRFg1WIgEC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4424,7 +4424,7 @@
 
   'src/chrome/browser/glic/resources/internal': {
       'url': Var('chrome_git') + '/chrome/browser/glic/resources/internal.git' + '@' +
-        'df684ddfd9137b4a3ee649314a02724adcbb29fd',
+        'aeb59fb12f8363ded7b2f1052956e538808c5daf',
       'condition': 'checkout_src_internal',
   },
 
@@ -4680,7 +4680,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        'c32f19290092f9592d3be32943becfab3f52a9cd',
+        '6753a19d2e211ea27988a2603073395d0469a202',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 2e6b1c9..33d8df5 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -1109,10 +1109,6 @@
 
   java_strings_grd("strings_grd") {
     grd_file = "java/strings/android_webview_strings.grd"
-    outputs = [ "values/android_webview_strings.xml" ] +
-              process_file_template(
-                  android_bundle_locales_as_resources,
-                  [ "values-{{source_name_part}}/android_webview_strings.xml" ])
   }
 }
 
diff --git a/android_webview/browser/aw_contents_io_thread_client.cc b/android_webview/browser/aw_contents_io_thread_client.cc
index cc104ae..6572108 100644
--- a/android_webview/browser/aw_contents_io_thread_client.cc
+++ b/android_webview/browser/aw_contents_io_thread_client.cc
@@ -614,4 +614,12 @@
                                                                java_object_);
 }
 
+bool AwContentsIoThreadClient::ShouldIncludeCookiesOnIntercept() const {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  JNIEnv* env = AttachCurrentThread();
+  return Java_AwContentsIoThreadClient_shouldIncludeCookiesInIntercept(
+      env, java_object_);
+}
+
 }  // namespace android_webview
diff --git a/android_webview/browser/aw_contents_io_thread_client.h b/android_webview/browser/aw_contents_io_thread_client.h
index 65c26646..ada2f113 100644
--- a/android_webview/browser/aw_contents_io_thread_client.h
+++ b/android_webview/browser/aw_contents_io_thread_client.h
@@ -148,6 +148,9 @@
   // Retrieve the SafeBrowsingEnabled setting value of this AwContents.
   bool GetSafeBrowsingEnabled() const;
 
+  // Enables getting and setting cookies as part of shouldInterceptRequest.
+  bool ShouldIncludeCookiesOnIntercept() const;
+
  private:
   base::android::ScopedJavaGlobalRef<jobject> java_object_;
 };
diff --git a/android_webview/browser/aw_field_trials.cc b/android_webview/browser/aw_field_trials.cc
index 23367a41..94dade9 100644
--- a/android_webview/browser/aw_field_trials.cc
+++ b/android_webview/browser/aw_field_trials.cc
@@ -217,8 +217,6 @@
 
   // FedCM is not yet supported on WebView.
   aw_feature_overrides.DisableFeature(::features::kFedCm);
-  aw_feature_overrides.DisableFeature(
-      blink::features::kFedCmWithStorageAccessAPI);
 
   // TODO(crbug.com/40272633): Web MIDI permission prompt for all usage.
   aw_feature_overrides.DisableFeature(blink::features::kBlockMidiByDefault);
diff --git a/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc b/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc
index 99bd0d70..9083a06 100644
--- a/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc
+++ b/android_webview/browser/network_service/aw_proxying_url_loader_factory.cc
@@ -1173,6 +1173,13 @@
   bool third_party_cookie_policy =
       global_cookie_policy && io_thread_client->ShouldAcceptThirdPartyCookies();
 
+  // If we are handling an external protocol, we skip providing the cookie
+  // manager. In this case, it will not be bound so we move on.
+  // We should also only provide cookies if cookies are enabled.
+  bool include_cookies_on_intercept =
+      cookie_manager_.is_bound() && global_cookie_policy &&
+      io_thread_client->ShouldIncludeCookiesOnIntercept();
+
   // WebView treats cookie access on a per request basis and so we have to
   // essentially let the rest of the network stack know if we want to allow
   // unpartitioned cookie access or not.
@@ -1205,12 +1212,9 @@
     }
   }
 
-  // If we are handling an external protocol, we skip providing the cookie
-  // manager. In this case, it will not be bound so we move on.
-  OptionalGetCookie get_cookie_header = std::nullopt;
-  OptionalSetCookie set_cookie_header = std::nullopt;
-  if (base::FeatureList::IsEnabled(features::kWebViewInterceptedCookieHeader) &&
-      cookie_manager_.is_bound()) {
+  OptionalGetCookie get_cookie_header;
+  OptionalSetCookie set_cookie_header;
+  if (include_cookies_on_intercept) {
     get_cookie_header = base::BindRepeating(
         &AwProxyingURLLoaderFactory::GetCookieHeader, base::Unretained(this));
     set_cookie_header = base::BindRepeating(
@@ -1299,12 +1303,8 @@
               }
             }
 
-            // TODO(crbug.com/384986095): Provide real cookie values
-            std::string cookie_line = "";
-            if (base::FeatureList::IsEnabled(
-                    features::kWebViewInterceptedCookieHeaderReadWrite)) {
-              cookie_line = net::CanonicalCookie::BuildCookieLine(cookies);
-            }
+            std::string cookie_line =
+                net::CanonicalCookie::BuildCookieLine(cookies);
             std::move(callback).Run(cookie_line);
             UMA_HISTOGRAM_TIMES(
                 "Android.WebView.ShouldInterceptRequest.GetCookieHeader."
@@ -1329,13 +1329,9 @@
       GetPartitionKey(isolation_info, request), net::CookieSourceType::kHTTP,
       &returned_status);
 
-  // TODO(crbug.com/384986095): Provide real cookie values
-  if (cookie && base::FeatureList::IsEnabled(
-                    features::kWebViewInterceptedCookieHeaderReadWrite)) {
     cookie_manager_->SetCanonicalCookie(*cookie, request.url,
                                         net::CookieOptions::MakeAllInclusive(),
                                         base::DoNothing());
-  }
 
   UMA_HISTOGRAM_TIMES(
       "Android.WebView.ShouldInterceptRequest.SetCookieHeader.TimeToRun",
diff --git a/android_webview/common/aw_features.cc b/android_webview/common/aw_features.cc
index fb4f26ef..529cbb2a 100644
--- a/android_webview/common/aw_features.cc
+++ b/android_webview/common/aw_features.cc
@@ -224,29 +224,6 @@
              "WebViewRenderDocument",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// When enabled, WebView performs normal processing work for cookie request
-// headers and response headers for the shouldInterceptRequest API. However,
-// whether the app is provided the cookie jar contents is controlled by
-// WebViewInterceptedCookieHeaderReadWrite. Whether Set-Cookie headers
-// affect the cookie jar is also controlled by
-// WebViewInterceptedCookieHeaderReadWrite. When that flag is disabled,
-// set-cookie headers are ignored and the response headers passed to the
-// app remain unchanged.
-BASE_FEATURE(kWebViewInterceptedCookieHeader,
-             "WebViewInterceptedCookieHeader",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
-// When enabled in conjunction with WebViewInterceptedCookieHeader flag, the
-// cookie header in the request headers will be included for
-// shouldInterceptRequest. Also, the set-cookie header in the response headers
-// will be processed and stored in the cookie jar for shouldInterceptRequest.
-// When disabled while WebViewInterceptedCookieHeader is enabled, the response
-// headers passed to the app remain unchanged. Also, the set-cookie
-// header has no effect on the cookie jar.
-BASE_FEATURE(kWebViewInterceptedCookieHeaderReadWrite,
-             "WebViewInterceptedCookieHeaderReadWrite",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 // When enabled, if the developer hasn't overridden shouldInterceptRequest
 // (or provided the async version), we short circuit (return no response)
 // on the IO thread instead of calling the (empty) method on a background
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
index b72a7ac..06605b7 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
@@ -7,7 +7,6 @@
 import android.Manifest;
 import android.app.compat.CompatChanges;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.os.Build;
@@ -91,9 +90,6 @@
     private static final String ASSET_PATH_WORKAROUND_HISTOGRAM_NAME =
             "Android.WebView.AssetPathWorkaroundUsed.StartChromiumLocked";
 
-    private static final String REGISTER_RESOURCE_PATHS_HISTOGRAM_NAME =
-            "Android.WebView.RegisterResourcePathsAvailable";
-
     public static class WebViewStartUpDiagnostics {
         private final Object mLock = new Object();
 
@@ -477,24 +473,6 @@
                                 });
                     }
 
-                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
-                        PostTask.postTask(
-                                TaskTraits.BEST_EFFORT,
-                                () -> {
-                                    try {
-                                        Resources.class.getDeclaredMethod(
-                                                "registerResourcePaths",
-                                                String.class,
-                                                ApplicationInfo.class);
-                                        RecordHistogram.recordBooleanHistogram(
-                                                REGISTER_RESOURCE_PATHS_HISTOGRAM_NAME, true);
-                                    } catch (NoSuchMethodException e) {
-                                        RecordHistogram.recordBooleanHistogram(
-                                                REGISTER_RESOURCE_PATHS_HISTOGRAM_NAME, false);
-                                    }
-                                });
-                    }
-
                     AwCrashyClassUtils.maybeCrashIfEnabled();
                     // Must happen right after Chromium initialization is complete.
                     mInitState.set(INIT_FINISHED);
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 844d7db6..9ac8ef5 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -3351,8 +3351,6 @@
         // instead attached to a FullScreenView.
         mAwViewMethods.onAttachedToWindow();
 
-        if (mDisplayCutoutController != null) mDisplayCutoutController.onAttachedToWindow();
-
         mAwFrameMetricsListener =
                 AwFrameMetricsListener.maybeCreate(
                         mContainerView, mWindowAndroid.getWindowAndroid());
@@ -4650,6 +4648,8 @@
                 StylusWritingSettingsState.getInstance().registerObserver(mStylusWritingController);
             }
 
+            if (mDisplayCutoutController != null) mDisplayCutoutController.onAttachedToWindow();
+
             mAwWindowCoverageTracker =
                     AwWindowCoverageTracker.getOrCreateForRootView(
                             AwContents.this, mContainerView.getRootView());
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsIoThreadClient.java b/android_webview/java/src/org/chromium/android_webview/AwContentsIoThreadClient.java
index 1191fb29..37f9c04 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsIoThreadClient.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsIoThreadClient.java
@@ -53,4 +53,7 @@
 
     @CalledByNative
     public void onLoadResource(@JniType("std::string") String url) {}
+
+    @CalledByNative
+    public abstract boolean shouldIncludeCookiesInIntercept();
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsIoThreadClientImpl.java b/android_webview/java/src/org/chromium/android_webview/AwContentsIoThreadClientImpl.java
index 7cb142c..f4f22d9 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentsIoThreadClientImpl.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentsIoThreadClientImpl.java
@@ -86,6 +86,11 @@
     }
 
     @Override
+    public boolean shouldIncludeCookiesInIntercept() {
+        return mSettings.getIncludeCookiesOnIntercept();
+    }
+
+    @Override
     public void onLoadResource(String url) {
         mContentsClient.getCallbackHelper().postOnLoadResource(url);
     }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwServiceWorkerController.java b/android_webview/java/src/org/chromium/android_webview/AwServiceWorkerController.java
index d5b53d1..ae5d368 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwServiceWorkerController.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwServiceWorkerController.java
@@ -7,33 +7,33 @@
 import android.content.Context;
 
 import androidx.annotation.GuardedBy;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import org.chromium.android_webview.common.Lifetime;
 import org.chromium.android_webview.safe_browsing.AwSafeBrowsingConfigHelper;
 import org.chromium.build.annotations.DoNotInline;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.components.embedder_support.util.WebResourceResponseInfo;
 
 /** Manages clients and settings for Service Workers. */
 @Lifetime.Profile
+@NullMarked
 public class AwServiceWorkerController {
 
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
+    @Nullable
     private AwServiceWorkerClient mServiceWorkerClient;
 
     @DoNotInline // Native stores this as a weak reference.
-    @NonNull
     private final AwContentsIoThreadClient mServiceWorkerIoThreadClient;
 
-    @NonNull private final ShouldInterceptRequestMediator mShouldInterceptRequestMediator;
-    @NonNull private final AwServiceWorkerSettings mServiceWorkerSettings;
-    @NonNull private final AwBrowserContext mBrowserContext;
+    private final ShouldInterceptRequestMediator mShouldInterceptRequestMediator;
+    private final AwServiceWorkerSettings mServiceWorkerSettings;
+    private final AwBrowserContext mBrowserContext;
 
-    public AwServiceWorkerController(
-            @NonNull Context applicationContext, @NonNull AwBrowserContext browserContext) {
+    public AwServiceWorkerController(Context applicationContext, AwBrowserContext browserContext) {
         mBrowserContext = browserContext;
         mServiceWorkerSettings = new AwServiceWorkerSettings(applicationContext, mBrowserContext);
         mShouldInterceptRequestMediator = new ServiceWorkerShouldInterceptRequestMediator();
@@ -105,6 +105,11 @@
         public boolean getSafeBrowsingEnabled() {
             return AwSafeBrowsingConfigHelper.getSafeBrowsingEnabledByManifest();
         }
+
+        @Override
+        public boolean shouldIncludeCookiesInIntercept() {
+            return mServiceWorkerSettings.getIncludeCookiesOnIntercept();
+        }
     }
 
     private class ServiceWorkerShouldInterceptRequestMediator
diff --git a/android_webview/java/src/org/chromium/android_webview/AwServiceWorkerSettings.java b/android_webview/java/src/org/chromium/android_webview/AwServiceWorkerSettings.java
index dfff669..01c138c 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwServiceWorkerSettings.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwServiceWorkerSettings.java
@@ -45,6 +45,7 @@
 
     // Computed on construction.AwServiceWorkerSettingsTest
     private final boolean mHasInternetPermission;
+    private boolean mIncludeCookiesOnIntercept;
 
     public AwServiceWorkerSettings(Context context, AwBrowserContext browserContext) {
         mBrowserContext = browserContext;
@@ -184,4 +185,20 @@
             return mRequestedWithHeaderAllowedOriginRules;
         }
     }
+
+    /**
+     * Set whether the shouldInterceptRequest API should include request cookies and accept response
+     * cookies.
+     */
+    public void setIncludeCookiesOnIntercept(boolean includeCookiesOnIntercept) {
+        synchronized (mAwServiceWorkerSettingsLock) {
+            this.mIncludeCookiesOnIntercept = includeCookiesOnIntercept;
+        }
+    }
+
+    public boolean getIncludeCookiesOnIntercept() {
+        synchronized (mAwServiceWorkerSettingsLock) {
+            return mIncludeCookiesOnIntercept;
+        }
+    }
 }
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSettings.java b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
index 491881d..197004b 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwSettings.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
@@ -220,6 +220,7 @@
     private boolean mPaymentRequestEnabled;
     private boolean mHasEnrolledInstrumentEnabled = true;
     private final AwMediaIntegrityApiStatusConfig mIntegrityApiStatusConfig;
+    private boolean mIncludeCookiesOnIntercept;
 
     private @WebauthnMode int mWebauthnMode = WebauthnMode.NONE;
 
@@ -2264,6 +2265,22 @@
         }
     }
 
+    /**
+     * Set whether the shouldInterceptRequest API should include request cookies and accept response
+     * cookies.
+     */
+    public void setIncludeCookiesOnIntercept(boolean includeCookiesOnIntercept) {
+        synchronized (mAwSettingsLock) {
+            this.mIncludeCookiesOnIntercept = includeCookiesOnIntercept;
+        }
+    }
+
+    public boolean getIncludeCookiesOnIntercept() {
+        synchronized (mAwSettingsLock) {
+            return mIncludeCookiesOnIntercept;
+        }
+    }
+
     @NativeMethods
     interface Natives {
         long init(AwSettings caller, WebContents webContents);
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index babba53a..a6506ca 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -892,25 +892,6 @@
         Flag.baseFeature(ContentFeatures.PWA_NAVIGATION_CAPTURING),
         Flag.baseFeature("TransportSecurityFileWriterSchedule"),
         Flag.baseFeature(
-                AwFeatures.WEBVIEW_INTERCEPTED_COOKIE_HEADER,
-                "When enabled, WebView performs normal processing work for cookie request headers"
-                    + " and response headers for the shouldInterceptRequest API. However, whether"
-                    + " the app is provided the cookie jar contents is controlled by "
-                        + AwFeatures.WEBVIEW_INTERCEPTED_COOKIE_HEADER_READ_WRITE
-                        + ". Whether Set-Cookie headers affect the cookie jar is also controlled by"
-                        + " "
-                        + AwFeatures.WEBVIEW_INTERCEPTED_COOKIE_HEADER_READ_WRITE
-                        + ". When this flag is disabled, set-cookie headers are ignored and the"
-                        + " cookie jar is never read."),
-        Flag.baseFeature(
-                AwFeatures.WEBVIEW_INTERCEPTED_COOKIE_HEADER_READ_WRITE,
-                "When enabled in conjunction with WebViewInterceptedCookieHeader flag, the cookie"
-                    + " header in the request headers will be included for shouldInterceptRequest."
-                    + " Also, the set-cookie header in the response headers will be processed for"
-                    + " shouldInterceptRequest. When disabled while WebViewInterceptedCookieHeader"
-                    + " is enabled, the response headers passed to the app remain unchanged. Also,"
-                    + " the set-cookie header has no effect on the cookie jar."),
-        Flag.baseFeature(
                 AwFeatures.WEBVIEW_HYPERLINK_CONTEXT_MENU,
                 "Enables hyperlink context menu in WebView"),
         Flag.baseFeature("MojoUseBinder"),
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
index 1b5c8d4d..71549366 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java
@@ -10,9 +10,9 @@
 import android.webkit.JavascriptInterface;
 import android.webkit.WebViewClient;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.google.common.util.concurrent.SettableFuture;
 
@@ -31,6 +31,7 @@
 import org.chromium.android_webview.test.TestAwContentsClient.OnReceivedErrorHelper;
 import org.chromium.android_webview.test.util.AwTestTouchUtils;
 import org.chromium.android_webview.test.util.CommonResources;
+import org.chromium.android_webview.test.util.CookieUtils;
 import org.chromium.android_webview.test.util.JSUtils;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CallbackHelper;
@@ -109,8 +110,12 @@
     }
 
     @After
-    public void tearDown() {
-        mWebServer.shutdown();
+    public void tearDown() throws Throwable {
+        CookieUtils.clearCookies(
+                InstrumentationRegistry.getInstrumentation(), new AwCookieManager());
+        if (mWebServer != null) {
+            mWebServer.shutdown();
+        }
     }
 
     @Test
@@ -1402,10 +1407,8 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Network"})
-    @CommandLineFlags.Add({
-        "enable-features=WebViewInterceptedCookieHeader,WebViewInterceptedCookieHeaderReadWrite"
-    })
     public void testInterceptedCookieHeaders_readWriteEnabled() throws Throwable {
+        mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setIncludeCookiesOnIntercept(true);
         HistogramWatcher histogramExpectation =
                 HistogramWatcher.newBuilder()
                         .expectAnyRecord(
@@ -1441,14 +1444,14 @@
     @Test
     @SmallTest
     @Feature({"AndroidWebView", "Network"})
-    @CommandLineFlags.Add({"enable-features=WebViewInterceptedCookieHeader"})
     public void testInterceptedCookieHeaders_readWriteDisabled() throws Throwable {
+        mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setIncludeCookiesOnIntercept(false);
         HistogramWatcher histogramExpectation =
                 HistogramWatcher.newBuilder()
-                        .expectAnyRecord(
+                        .expectNoRecords(
                                 "Android.WebView.ShouldInterceptRequest."
                                         + "SetCookieHeader.TimeToRun")
-                        .expectAnyRecord(
+                        .expectNoRecords(
                                 "Android.WebView.ShouldInterceptRequest."
                                         + "GetCookieHeader.PostMojo.TimeToRun")
                         .build();
@@ -1458,23 +1461,18 @@
         cookieManager.setCookie(destinationUrl, "blah=yo");
 
         // Forcing a cookie to be set in the response
+        Map<String, String> responseHeaders = Map.of("set-cookie", "foo=bar");
         mShouldInterceptRequestHelper.enqueueHtmlResponseForUrl(
-                destinationUrl, "hello", Map.of("set-cookie", "foo=bar"));
+                destinationUrl, "hello", responseHeaders);
 
         mActivityTestRule.loadUrlSync(
                 mAwContents, mContentsClient.getOnPageFinishedHelper(), destinationUrl);
 
-        // These are the cookies that were sent before we set a new one.
+        // Intercept should not contain the Cookie header.
         var resourceRequest = mShouldInterceptRequestHelper.getRequestsForUrl(destinationUrl);
-        Assert.assertFalse(
-                "Cookie jar should be empty",
-                resourceRequest.getRequestHeaders().containsKey("Cookie"));
-        Assert.assertNotEquals(
-                "Cookie jar should be empty",
-                "blah=yo",
-                resourceRequest.getRequestHeaders().get("Cookie"));
+        Assert.assertFalse(resourceRequest.getRequestHeaders().containsKey("Cookie"));
 
-        // And then we should see our new value in the cookie manager.
+        // The set-cookie header in the response should not affect stored cookies.
         Assert.assertEquals("blah=yo", cookieManager.getCookie(destinationUrl));
         histogramExpectation.assertExpected();
     }
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
index 2b8336b..a107f34a 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
@@ -1242,8 +1242,8 @@
     @Test
     @MediumTest
     @Feature({"AndroidWebView", "Privacy"})
-    @CommandLineFlags.Add("enable-features=WebViewInterceptedCookieHeader")
     public void testPartitionedNetCookies() throws Throwable {
+        mActivityTestRule.getAwSettingsOnUiThread(mAwContents).setIncludeCookiesOnIntercept(true);
         TestAwContentsClient.ShouldInterceptRequestHelper shouldInterceptRequestHelper =
                 mContentsClient.getShouldInterceptRequestHelper();
 
@@ -1292,15 +1292,10 @@
                     expectedCookies,
                     webServer.getLastRequest("/path_to_intercept").headerValue("Cookie"));
 
-            // TODO(crbug.com/384986095): Re-add the real expected cookie behavior
-            // post-experimentation
-            String interceptRequestFailureMessage =
-                    "No cookies should be returned for shouldInterceptRequest";
-            expectedCookies = null;
             var interceptedRequest =
                     shouldInterceptRequestHelper.getRequestsForUrl(iframeUrl + "path_to_intercept");
             Assert.assertEquals(
-                    interceptRequestFailureMessage,
+                    failureMessage,
                     expectedCookies,
                     interceptedRequest.getRequestHeaders().get("Cookie"));
 
@@ -1314,13 +1309,10 @@
                     expectedCookies,
                     webServer.getLastRequest("/path_to_intercept").headerValue("Cookie"));
 
-            // TODO(crbug.com/384986095): Re-add the real expected cookie behavior
-            // post-experimentation
-            expectedCookies = null;
             interceptedRequest =
                     shouldInterceptRequestHelper.getRequestsForUrl(iframeUrl + "path_to_intercept");
             Assert.assertEquals(
-                    interceptRequestFailureMessage,
+                    failureMessage,
                     expectedCookies,
                     interceptedRequest.getRequestHeaders().get("Cookie"));
 
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ServiceWorkerWebSettingsBoundaryInterface.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ServiceWorkerWebSettingsBoundaryInterface.java
index ba733ab..0981617 100644
--- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ServiceWorkerWebSettingsBoundaryInterface.java
+++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/ServiceWorkerWebSettingsBoundaryInterface.java
@@ -30,4 +30,8 @@
     void setRequestedWithHeaderOriginAllowList(Set<String> allowedOriginRules);
 
     Set<String> getRequestedWithHeaderOriginAllowList();
+
+    void setIncludeCookiesOnIntercept(boolean includeCookiesOnIntercept);
+
+    boolean getIncludeCookiesOnIntercept();
 }
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebSettingsBoundaryInterface.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebSettingsBoundaryInterface.java
index a908056..0791550 100644
--- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebSettingsBoundaryInterface.java
+++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/WebSettingsBoundaryInterface.java
@@ -133,4 +133,8 @@
     void setHasEnrolledInstrumentEnabled(boolean enabled);
 
     boolean getHasEnrolledInstrumentEnabled();
+
+    void setIncludeCookiesOnIntercept(boolean includeCookiesOnIntercept);
+
+    boolean getIncludeCookiesOnIntercept();
 }
diff --git a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
index 6bf5dbc..e655e1b2 100644
--- a/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
+++ b/android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/Features.java
@@ -353,4 +353,10 @@
 
     // WebViewBuilder
     public static final String WEBVIEW_BUILDER = "WEBVIEW_BUILDER";
+
+    // WebSettingsCompat.setIncludeCookiesOnIntercept
+    // WebSettingsCompat.getIncludeCookiesOnIntercept
+    // ServiceWorkerWebSettingsCompat.setIncludeCookiesOnIntercept
+    // ServiceWorkerWebSettingsCompat.getIncludeCookiesOnIntercept
+    public static final String COOKIE_INTERCEPT = "COOKIE_INTERCEPT";
 }
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerSettingsAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerSettingsAdapter.java
index 58600ef..2f96c5ff4 100644
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerSettingsAdapter.java
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerSettingsAdapter.java
@@ -126,4 +126,24 @@
             return mAwServiceWorkerSettings.getRequestedWithHeaderOriginAllowList();
         }
     }
+
+    @Override
+    public void setIncludeCookiesOnIntercept(boolean includeCookiesOnIntercept) {
+        try (TraceEvent ignored =
+                TraceEvent.scoped(
+                        "WebView.APICall.AndroidX.SERVICE_WORKER_SET_INCLUDE_COOKIES_ON_INTERCEPT")) {
+            recordApiCall(ApiCall.SERVICE_WORKER_SET_INCLUDE_COOKIES_ON_INTERCEPT);
+            mAwServiceWorkerSettings.setIncludeCookiesOnIntercept(includeCookiesOnIntercept);
+        }
+    }
+
+    @Override
+    public boolean getIncludeCookiesOnIntercept() {
+        try (TraceEvent ignored =
+                TraceEvent.scoped(
+                        "WebView.APICall.AndroidX.SERVICE_WORKER_GET_INCLUDE_COOKIES_ON_INTERCEPT")) {
+            recordApiCall(ApiCall.SERVICE_WORKER_GET_INCLUDE_COOKIES_ON_INTERCEPT);
+            return mAwServiceWorkerSettings.getIncludeCookiesOnIntercept();
+        }
+    }
 }
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsAdapter.java
index 0c3f31bb..7fdf0ea 100644
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsAdapter.java
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsAdapter.java
@@ -524,4 +524,22 @@
             return mAwSettings.getHasEnrolledInstrumentEnabled();
         }
     }
+
+    @Override
+    public void setIncludeCookiesOnIntercept(boolean includeCookiesOnIntercept) {
+        try (TraceEvent ignored =
+                TraceEvent.scoped("WebView.APICall.AndroidX.SET_INCLUDE_COOKIES_ON_INTERCEPT")) {
+            recordApiCall(ApiCall.SET_INCLUDE_COOKIES_ON_INTERCEPT);
+            mAwSettings.setIncludeCookiesOnIntercept(includeCookiesOnIntercept);
+        }
+    }
+
+    @Override
+    public boolean getIncludeCookiesOnIntercept() {
+        try (TraceEvent ignored =
+                TraceEvent.scoped("WebView.APICall.AndroidX.GET_INCLUDE_COOKIES_ON_INTERCEPT")) {
+            recordApiCall(ApiCall.GET_INCLUDE_COOKIES_ON_INTERCEPT);
+            return mAwSettings.getIncludeCookiesOnIntercept();
+        }
+    }
 }
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsNoOpAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsNoOpAdapter.java
index 563abf99..6bb6364c8 100644
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsNoOpAdapter.java
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebSettingsNoOpAdapter.java
@@ -221,4 +221,15 @@
         recordApiCall(ApiCall.GET_HAS_ENROLLED_INSTRUMENT_ENABLED);
         return false;
     }
+
+    @Override
+    public void setIncludeCookiesOnIntercept(boolean includeCookiesOnIntercept) {
+        recordApiCall(ApiCall.SET_INCLUDE_COOKIES_ON_INTERCEPT);
+    }
+
+    @Override
+    public boolean getIncludeCookiesOnIntercept() {
+        recordApiCall(ApiCall.GET_INCLUDE_COOKIES_ON_INTERCEPT);
+        return false;
+    }
 }
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
index 25f4ce4..a3d9c6a0 100644
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java
@@ -113,6 +113,7 @@
                 Features.PROVIDER_WEAKLY_REF_WEBVIEW,
                 Features.PAYMENT_REQUEST,
                 Features.WEBVIEW_BUILDER + Features.DEV_SUFFIX,
+                Features.COOKIE_INTERCEPT + Features.DEV_SUFFIX,
                 // Add new features above. New features must include `+ Features.DEV_SUFFIX`
                 // when they're initially added (this can be removed in a future CL). The final
                 // feature should have a trailing comma for cleaner diffs.
@@ -268,6 +269,10 @@
         ApiCall.SET_HAS_ENROLLED_INSTRUMENT_ENABLED,
         ApiCall.GET_HAS_ENROLLED_INSTRUMENT_ENABLED,
         ApiCall.GET_WEBVIEW_BUILDER,
+        ApiCall.SET_INCLUDE_COOKIES_ON_INTERCEPT,
+        ApiCall.GET_INCLUDE_COOKIES_ON_INTERCEPT,
+        ApiCall.SERVICE_WORKER_SET_INCLUDE_COOKIES_ON_INTERCEPT,
+        ApiCall.SERVICE_WORKER_GET_INCLUDE_COOKIES_ON_INTERCEPT,
         // Add new constants above. The final constant should have a trailing comma for cleaner
         // diffs.
         ApiCall.COUNT, // Added to suppress WrongConstant in #recordApiCall
@@ -422,9 +427,13 @@
         int SET_HAS_ENROLLED_INSTRUMENT_ENABLED = 143;
         int GET_HAS_ENROLLED_INSTRUMENT_ENABLED = 144;
         int GET_WEBVIEW_BUILDER = 145;
+        int SET_INCLUDE_COOKIES_ON_INTERCEPT = 146;
+        int GET_INCLUDE_COOKIES_ON_INTERCEPT = 147;
+        int SERVICE_WORKER_SET_INCLUDE_COOKIES_ON_INTERCEPT = 148;
+        int SERVICE_WORKER_GET_INCLUDE_COOKIES_ON_INTERCEPT = 149;
 
         // Remember to update AndroidXWebkitApiCall in enums.xml when adding new values here
-        int COUNT = 146;
+        int COUNT = 150;
     }
 
     // LINT.ThenChange(/tools/metrics/histograms/metadata/android/enums.xml:AndroidXWebkitApiCall)
diff --git a/android_webview/tools/system_webview_shell/lint-baseline.xml b/android_webview/tools/system_webview_shell/lint-baseline.xml
index 5d057d5..2b2eb62 100644
--- a/android_webview/tools/system_webview_shell/lint-baseline.xml
+++ b/android_webview/tools/system_webview_shell/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.11.0-alpha07" type="baseline" client="" dependencies="true" name="" variant="all" version="8.11.0-alpha07">
+<issues format="6" by="lint 8.11.0-alpha09" type="baseline" client="" dependencies="true" name="" variant="all" version="8.11.0-alpha09">
 
     <issue
         id="NewApi"
diff --git a/ash/in_session_auth/auth_dialog_contents_view.cc b/ash/in_session_auth/auth_dialog_contents_view.cc
index 85319a0..506cbc2c 100644
--- a/ash/in_session_auth/auth_dialog_contents_view.cc
+++ b/ash/in_session_auth/auth_dialog_contents_view.cc
@@ -394,7 +394,7 @@
   auto border = std::make_unique<views::BubbleBorder>(
       views::BubbleBorder::FLOAT, views::BubbleBorder::STANDARD_SHADOW);
   border->SetColor(ui::kColorPrimaryBackground);
-  border->SetCornerRadius(kCornerRadius);
+  border->set_rounded_corners(gfx::RoundedCornersF(kCornerRadius));
   SetBackground(std::make_unique<views::BubbleBackground>(border.get()));
   SetBorder(std::move(border));
 
diff --git a/ash/in_session_auth/in_session_auth_dialog_contents_view.cc b/ash/in_session_auth/in_session_auth_dialog_contents_view.cc
index 08c660b8..4a20ff6 100644
--- a/ash/in_session_auth/in_session_auth_dialog_contents_view.cc
+++ b/ash/in_session_auth/in_session_auth_dialog_contents_view.cc
@@ -100,7 +100,7 @@
   auto border = std::make_unique<views::BubbleBorder>(
       views::BubbleBorder::FLOAT, views::BubbleBorder::STANDARD_SHADOW);
   border->SetColor(ui::kColorPrimaryBackground);
-  border->SetCornerRadius(kCornerRadius);
+  border->set_rounded_corners(gfx::RoundedCornersF(kCornerRadius));
   SetBackground(std::make_unique<views::BubbleBackground>(border.get()));
   SetBorder(std::move(border));
 
diff --git a/ash/quick_insert/views/quick_insert_submenu_view.cc b/ash/quick_insert/views/quick_insert_submenu_view.cc
index a95e547..73d00e7 100644
--- a/ash/quick_insert/views/quick_insert_submenu_view.cc
+++ b/ash/quick_insert/views/quick_insert_submenu_view.cc
@@ -35,7 +35,8 @@
       base::i18n::IsRTL() ? views::BubbleBorder::Arrow::RIGHT_TOP
                           : views::BubbleBorder::Arrow::LEFT_TOP,
       views::BubbleBorder::CHROMEOS_SYSTEM_UI_SHADOW);
-  border->SetCornerRadius(kQuickInsertContainerBorderRadius);
+  border->set_rounded_corners(
+      gfx::RoundedCornersF(kQuickInsertContainerBorderRadius));
   border->SetColor(SK_ColorTRANSPARENT);
   return border;
 }
diff --git a/ash/quick_insert/views/quick_insert_view.cc b/ash/quick_insert/views/quick_insert_view.cc
index 9cbf689..0854a0a 100644
--- a/ash/quick_insert/views/quick_insert_view.cc
+++ b/ash/quick_insert/views/quick_insert_view.cc
@@ -94,7 +94,8 @@
 std::unique_ptr<views::BubbleBorder> CreateBorder() {
   auto border = std::make_unique<views::BubbleBorder>(
       views::BubbleBorder::NONE, views::BubbleBorder::NO_SHADOW);
-  border->SetCornerRadius(kQuickInsertContainerBorderRadius);
+  border->set_rounded_corners(
+      gfx::RoundedCornersF(kQuickInsertContainerBorderRadius));
   border->SetColor(SK_ColorTRANSPARENT);
   return border;
 }
diff --git a/ash/shelf/kiosk_app_instruction_bubble.cc b/ash/shelf/kiosk_app_instruction_bubble.cc
index 6afa06d..dd746b23 100644
--- a/ash/shelf/kiosk_app_instruction_bubble.cc
+++ b/ash/shelf/kiosk_app_instruction_bubble.cc
@@ -16,6 +16,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/mojom/dialog_button.mojom.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/fill_layout.h"
@@ -79,9 +80,11 @@
       std::make_unique<views::BubbleBorder>(arrow(), GetShadow());
   bubble_border->set_insets(
       GetBubbleInsets(anchor_widget()->GetNativeWindow()->GetRootWindow()));
-  bubble_border->SetCornerRadius(
-      views::LayoutProvider::Get()->GetCornerRadiusMetric(
-          views::Emphasis::kHigh));
+
+  const int corner_radius = views::LayoutProvider::Get()->GetCornerRadiusMetric(
+      views::Emphasis::kHigh);
+  bubble_border->set_rounded_corners(gfx::RoundedCornersF(corner_radius));
+
   GetBubbleFrameView()->SetBubbleBorder(std::move(bubble_border));
   GetBubbleFrameView()->SetBackgroundColor(background_color());
 
diff --git a/ash/shelf/shelf_bubble.cc b/ash/shelf/shelf_bubble.cc
index 7d8fea7..5ee893a4 100644
--- a/ash/shelf/shelf_bubble.cc
+++ b/ash/shelf/shelf_bubble.cc
@@ -13,6 +13,7 @@
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/mojom/dialog_button.mojom.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/views/bubble/bubble_frame_view.h"
 
 namespace {
@@ -91,7 +92,7 @@
   views::BubbleDialogDelegateView::CreateBubble(this);
 
   // Settings that should only be changed just after bubble creation.
-  GetBubbleFrameView()->SetCornerRadius(border_radius_);
+  GetBubbleFrameView()->SetRoundedCorners(gfx::RoundedCornersF(border_radius_));
   GetBubbleFrameView()->SetBackgroundColor(background_color());
 }
 
diff --git a/ash/shelf/shelf_shutdown_confirmation_bubble.cc b/ash/shelf/shelf_shutdown_confirmation_bubble.cc
index 279a525..bea7d87 100644
--- a/ash/shelf/shelf_shutdown_confirmation_bubble.cc
+++ b/ash/shelf/shelf_shutdown_confirmation_bubble.cc
@@ -22,6 +22,7 @@
 #include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/image_model.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/paint_vector_icon.h"
@@ -141,9 +142,9 @@
   auto bubble_border =
       std::make_unique<views::BubbleBorder>(arrow(), GetShadow());
   bubble_border->set_insets(kShutdownConfirmationBubbleInsets);
-  bubble_border->SetCornerRadius(
-      views::LayoutProvider::Get()->GetCornerRadiusMetric(
-          views::Emphasis::kHigh));
+  const int corner_radius = views::LayoutProvider::Get()->GetCornerRadiusMetric(
+      views::Emphasis::kHigh);
+  bubble_border->set_rounded_corners(gfx::RoundedCornersF(corner_radius));
   GetBubbleFrameView()->SetBubbleBorder(std::move(bubble_border));
   GetBubbleFrameView()->SetBackgroundColor(background_color());
 
diff --git a/ash/system/tray/tray_bubble_view.cc b/ash/system/tray/tray_bubble_view.cc
index eda5c9ce..94dc07a 100644
--- a/ash/system/tray/tray_bubble_view.cc
+++ b/ash/system/tray/tray_bubble_view.cc
@@ -46,6 +46,7 @@
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/gfx/geometry/skia_conversions.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/bubble/bubble_frame_view.h"
@@ -555,7 +556,8 @@
   std::unique_ptr<BubbleBorder> bubble_border =
       std::make_unique<BubbleBorder>(arrow(), BubbleBorder::NO_SHADOW);
   if (params_.corner_radius) {
-    bubble_border->SetCornerRadius(params_.corner_radius);
+    bubble_border->set_rounded_corners(
+        gfx::RoundedCornersF(params_.corner_radius));
   }
   bubble_border->set_avoid_shadow_overlap(true);
   if (params_.insets.has_value()) {
diff --git a/ash/user_education/views/help_bubble_view_ash.cc b/ash/user_education/views/help_bubble_view_ash.cc
index 49f15511..d2b29c96 100644
--- a/ash/user_education/views/help_bubble_view_ash.cc
+++ b/ash/user_education/views/help_bubble_view_ash.cc
@@ -901,7 +901,7 @@
   const float lower_left = dx < 0 && dy > 0 ? kSmall : kDefault;
 
   // Update rounded corners.
-  GetBubbleFrameView()->bubble_border()->set_rounded_corners(
+  GetBubbleFrameView()->SetRoundedCorners(
       gfx::RoundedCornersF(upper_left, upper_right, lower_right, lower_left));
 }
 
diff --git a/base/allocator/early_zone_registration_apple.cc b/base/allocator/early_zone_registration_apple.cc
index 702f86f..32a9a00 100644
--- a/base/allocator/early_zone_registration_apple.cc
+++ b/base/allocator/early_zone_registration_apple.cc
@@ -152,9 +152,11 @@
                                       num_to_be_freed);
   };
 #if PA_TRY_FREE_DEFAULT_IS_AVAILABLE
-  g_delegating_zone.try_free_default = [](malloc_zone_t* zone, void* ptr) {
-    return g_default_zone->try_free_default(g_default_zone, ptr);
-  };
+  if (g_default_zone->version >= 13 && g_default_zone->try_free_default) {
+    g_delegating_zone.try_free_default = [](malloc_zone_t* zone, void* ptr) {
+      return g_default_zone->try_free_default(g_default_zone, ptr);
+    };
+  }
 #endif
 
   // Diagnostics and debugging.
diff --git a/base/allocator/partition_allocator/src/partition_alloc/page_allocator_unittest.cc b/base/allocator/partition_allocator/src/partition_alloc/page_allocator_unittest.cc
index 5c5bc31..c136658 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/page_allocator_unittest.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/page_allocator_unittest.cc
@@ -561,13 +561,7 @@
   FreePages(buffer, size);
 }
 
-// TODO(crbug.com/416151077): Enabled the test on UBSan.
-#if defined(UNDEFINED_SANITIZER)
-#define MAYBE_DecommitAndZero DISABLED_DecommitAndZero
-#else
-#define MAYBE_DecommitAndZero DecommitAndZero
-#endif
-TEST(PartitionAllocPageAllocatorTest, MAYBE_DecommitAndZero) {
+TEST(PartitionAllocPageAllocatorTest, DecommitAndZero) {
   size_t size = PageAllocationGranularity();
   uintptr_t buffer = AllocPages(size, PageAllocationGranularity(),
                                 PageAccessibilityConfiguration(
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/debug/stack_trace.cc b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/debug/stack_trace.cc
index 7b79717..76e3c1a 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/debug/stack_trace.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/debug/stack_trace.cc
@@ -12,6 +12,10 @@
 #include "partition_alloc/partition_alloc_base/process/process_handle.h"
 #include "partition_alloc/partition_alloc_base/threading/platform_thread.h"
 
+#if PA_BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
+#include <algorithm>
+#endif
+
 #if (PA_BUILDFLAG(IS_LINUX) || PA_BUILDFLAG(IS_CHROMEOS)) && defined(__GLIBC__)
 extern "C" void* __libc_stack_end;
 #endif
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/numerics/safe_conversions.h b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/numerics/safe_conversions.h
index 62a5ddf..6ab5e02 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/numerics/safe_conversions.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/numerics/safe_conversions.h
@@ -19,10 +19,6 @@
 #define PA_BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0)
 #endif
 
-#if !PA_BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
-#include <ostream>
-#endif
-
 namespace partition_alloc::internal::base {
 namespace internal {
 
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/time/time.cc b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/time/time.cc
index 5e7e92eb..9464192 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/time/time.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/time/time.cc
@@ -122,6 +122,10 @@
                       : (*this + half).FloorToMultiple(interval);
 }
 
+std::ostream& operator<<(std::ostream& os, TimeDelta time_delta) {
+  return os << time_delta.InSecondsF() << " s";
+}
+
 // Time -----------------------------------------------------------------------
 
 // static
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/time/time.h b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/time/time.h
index e30fecd..3c77bef 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/time/time.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/time/time.h
@@ -322,10 +322,6 @@
     return delta_ >= other.delta_;
   }
 
-  friend std::ostream& operator<<(std::ostream& os, const TimeDelta& td) {
-    return os << td.InSecondsF() << " s";
-  }
-
   // Returns this delta, ceiled/floored/rounded-away-from-zero to the nearest
   // multiple of |interval|.
   TimeDelta CeilToMultiple(TimeDelta interval) const;
@@ -379,6 +375,10 @@
   return td * a;
 }
 
+// For logging use only.
+PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE)
+std::ostream& operator<<(std::ostream& os, TimeDelta time_delta);
+
 // TimeBase--------------------------------------------------------------------
 
 // Do not reference the time_internal::TimeBase template class directly.  Please
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h
index e9f891a..d4c70f2 100644
--- a/base/trace_event/builtin_categories.h
+++ b/base/trace_event/builtin_categories.h
@@ -53,10 +53,10 @@
     perfetto::Category("android_webview.timeline"),
     perfetto::Category("aogh").SetDescription(
       "Actions on Google Hardware, used in Google-internal code."),
-    perfetto::Category("audio"),
-    perfetto::Category("base"),
-    perfetto::Category("benchmark"),
-    perfetto::Category("blink"),
+    perfetto::Category("audio").SetTags("audio"),
+    perfetto::Category("base").SetTags("toplevel"),
+    perfetto::Category("benchmark").SetTags("input"),
+    perfetto::Category("blink").SetTags("javascript", "rendering"),
     perfetto::Category("blink.animations"),
     perfetto::Category("blink.bindings"),
     perfetto::Category("blink.console"),
@@ -66,7 +66,7 @@
     perfetto::Category("blink.worker"),
     perfetto::Category("blink_style"),
     perfetto::Category("Blob"),
-    perfetto::Category("browser"),
+    perfetto::Category("browser").SetTags("navigation"),
     perfetto::Category("browsing_data"),
     perfetto::Category("CacheStorage"),
     perfetto::Category("Calculators"),
@@ -77,8 +77,8 @@
     perfetto::Category("cast.mdns"),
     perfetto::Category("cast.mdns.socket"),
     perfetto::Category("cast.stream"),
-    perfetto::Category("cc"),
-    perfetto::Category("cc.debug"),
+    perfetto::Category("cc").SetTags("rendering"),
+    perfetto::Category("cc.debug").SetTags("debug"),
     perfetto::Category("cdp.perf"),
     perfetto::Category("chromeos"),
     perfetto::Category("cma"),
@@ -102,7 +102,7 @@
     perfetto::Category("drm"),
     perfetto::Category("drmcursor"),
     perfetto::Category("dwrite"),
-    perfetto::Category("evdev"),
+    perfetto::Category("evdev").SetTags("input"),
     perfetto::Category("event"),
     perfetto::Category("exo"),
     perfetto::Category("extensions"),
@@ -112,10 +112,10 @@
     perfetto::Category("fledge"),
     perfetto::Category("fonts"),
     perfetto::Category("GAMEPAD"),
-    perfetto::Category("gpu"),
+    perfetto::Category("gpu").SetTags("rendering"),
     perfetto::Category("gpu.angle"),
     perfetto::Category("gpu.angle.texture_metrics"),
-    perfetto::Category("gpu.capture"),
+    perfetto::Category("gpu.capture").SetTags("video"),
     perfetto::Category("graphics.pipeline"),
     perfetto::Category("headless"),
     perfetto::Category("history").SetDescription(
@@ -124,37 +124,37 @@
     perfetto::Category("identity"),
     perfetto::Category("ime"),
     perfetto::Category("IndexedDB"),
-    perfetto::Category("input"),
-    perfetto::Category("input.scrolling"),
+    perfetto::Category("input").SetTags("input"),
+    perfetto::Category("input.scrolling").SetTags("input"),
     perfetto::Category("io"),
-    perfetto::Category("ipc"),
+    perfetto::Category("ipc").SetTags("ipc"),
     perfetto::Category("Java"),
     perfetto::Category("jni"),
     perfetto::Category("jpeg"),
     perfetto::Category("latency"),
     perfetto::Category("latencyInfo"),
     perfetto::Category("leveldb"),
-    perfetto::Category("loading"),
+    perfetto::Category("loading").SetTags("navigation"),
     perfetto::Category("log"),
     perfetto::Category("login"),
-    perfetto::Category("media"),
-    perfetto::Category("mediastream"),
+    perfetto::Category("media").SetTags("video"),
+    perfetto::Category("mediastream").SetTags("audio"),
     perfetto::Category("media_router"),
     perfetto::Category("memory"),
     perfetto::Category("midi"),
-    perfetto::Category("mojom"),
+    perfetto::Category("mojom").SetTags("ipc"),
     perfetto::Category("mojom.flow").SetDescription(
         "Includes flow events related to mojom. Notably, records flows between "
-        "senders and receivers."),
+        "senders and receivers.").SetTags("ipc"),
     perfetto::Category("mus"),
     perfetto::Category("native"),
-    perfetto::Category("navigation"),
-    perfetto::Category("navigation.debug"),
-    perfetto::Category("net"),
+    perfetto::Category("navigation").SetTags("navigation"),
+    perfetto::Category("navigation.debug").SetTags("debug"),
+    perfetto::Category("net").SetTags("navigation"),
     perfetto::Category("net.stream").SetDescription(
         "Includes events related to creating HTTP streams to serve requests."),
     perfetto::Category("network.scheduler"),
-    perfetto::Category("netlog"),
+    perfetto::Category("netlog").SetTags("navigation"),
     perfetto::Category("offline_pages"),
     perfetto::Category("omnibox"),
     perfetto::Category("oobe"),
@@ -162,7 +162,7 @@
     perfetto::Category("ozone"),
     perfetto::Category("partition_alloc"),
     perfetto::Category("passwords"),
-    perfetto::Category("p2p"),
+    perfetto::Category("p2p").SetTags("audio"),
     perfetto::Category("page-serialization"),
     perfetto::Category("paint_preview"),
     perfetto::Category("pepper"),
@@ -189,7 +189,7 @@
     perfetto::Category("ServiceWorker"),
     perfetto::Category("SiteEngagement"),
     perfetto::Category("safe_browsing"),
-    perfetto::Category("scheduler"),
+    perfetto::Category("scheduler").SetTags("scheduling"),
     perfetto::Category("scheduler.flow").SetDescription(
         "Includes flow events related to scheduling dependency. Notably, "
         "records flows between tasks running in the thread pool on the same "
@@ -197,7 +197,7 @@
     perfetto::Category("scheduler.long_tasks"),
     perfetto::Category("screenlock_monitor"),
     perfetto::Category("segmentation_platform"),
-    perfetto::Category("sequence_manager"),
+    perfetto::Category("sequence_manager").SetTags("scheduling"),
     perfetto::Category("service_manager"),
     perfetto::Category("sharing"),
     perfetto::Category("shell"),
@@ -210,37 +210,42 @@
     perfetto::Category("sync"),
     perfetto::Category("system_apps"),
     perfetto::Category("test_gpu"),
-    perfetto::Category("toplevel"),
-    perfetto::Category("toplevel.flow"),
-    perfetto::Category("ui"),
+    perfetto::Category("toplevel").SetTags("scheduling", "toplevel"),
+    perfetto::Category("toplevel.flow").SetTags("scheduling", "toplevel"),
+    perfetto::Category("ui").SetTags("rendering"),
     perfetto::Category("v8"),
     perfetto::Category("v8.execute"),
     perfetto::Category("v8.wasm"),
     perfetto::Category("ValueStoreFrontend::Backend"),
-    perfetto::Category("views"),
+    perfetto::Category("views").SetTags("rendering"),
     perfetto::Category("views.frame"),
-    perfetto::Category("viz"),
+    perfetto::Category("viz").SetTags("rendering"),
     perfetto::Category("vk"),
-    perfetto::Category("wakeup.flow"),
+    perfetto::Category("wakeup.flow").SetTags("scheduling"),
     perfetto::Category("wayland"),
-    perfetto::Category("webaudio"),
+    perfetto::Category("webaudio").SetTags("audio"),
     perfetto::Category("webengine.fidl"),
     perfetto::Category("weblayer"),
     perfetto::Category("WebCore"),
     perfetto::Category("webnn"),
-    perfetto::Category("webrtc"),
+    perfetto::Category("webrtc").SetTags("audio", "video"),
     perfetto::Category("webrtc_stats"),
     perfetto::Category("xr"),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("android_view_hierarchy")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("animation-worklet")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("audio")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("audio.latency")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("audio-worklet")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("audio-worklet"))
+        .SetTags("audio"),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("base")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("blink.debug")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("blink.debug.display_lock")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("blink.debug.layout")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.trees")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("blink.debug"))
+        .SetTags("debug"),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("blink.debug.display_lock"))
+        .SetTags("debug"),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("blink.debug.layout"))
+        .SetTags("debug"),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("blink.debug.layout.trees"))
+        .SetTags("debug"),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("blink.feature_usage")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("blink.image_decoding")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("blink.invalidation")),
@@ -248,17 +253,25 @@
     perfetto::Category(
         TRACE_DISABLED_BY_DEFAULT("identifiability.high_entropy_api")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug.display_items")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug.lcd_text")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug.picture")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.now")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug")).SetTags("debug"),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"))
+        .SetTags("debug"),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug.display_items"))
+        .SetTags("debug"),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug.lcd_text"))
+        .SetTags("debug"),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug.picture"))
+        .SetTags("debug"),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"))
+        .SetTags("debug"),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"))
+        .SetTags("debug"),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.now"))
+        .SetTags("debug"),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("content.verbose")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cpu_profiler")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"))
+        .SetTags("debug"),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("crypto.dpapi")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("devtools.screenshot")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("devtools.timeline")),
@@ -277,7 +290,7 @@
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("fonts")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("gpu_cmd_queue")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("gpu.dawn")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("gpu.debug")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("gpu.debug")).SetTags("debug"),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("gpu.decoder")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("gpu.device")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("gpu.graphite.dawn")),
@@ -286,10 +299,12 @@
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("histogram_samples")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("java-heap-profiler")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("layer-element")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("layout_shift.debug")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("layout_shift.debug"))
+        .SetTags("debug"),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("lifecycles")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("loading")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("mediastream")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("mediastream"))
+        .SetTags("audio"),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("memory-infra")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("memory-infra.v8.code_stats")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("mojom")),
@@ -300,9 +315,11 @@
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("power")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("system_metrics")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug"))
+        .SetTags("debug"),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("sequence_manager")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("sequence_manager.debug")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("sequence_manager.debug"))
+        .SetTags("debug"),
     perfetto::Category(
         TRACE_DISABLED_BY_DEFAULT("sequence_manager.verbose_snapshots")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("skia")),
@@ -313,17 +330,20 @@
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("SyncFileSystem")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("system_power")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("thread_pool_diagnostics")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("toplevel.ipc")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("toplevel.ipc"))
+        .SetTags("ipc"),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("user_action_samples")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("v8.compile")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("v8.inspector")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("v8.runtime")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("v8.runtime_stats")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("v8.runtime_stats_sampling")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"))
+        .SetTags("video"),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("display.framedisplayed")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("viz.gpu_composite_time")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("viz.debug.overlay_planes")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("viz.debug.overlay_planes"))
+        .SetTags("debug"),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("viz.hit_testing_flow")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("viz.overdraw")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("viz.quads")),
@@ -331,12 +351,14 @@
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("viz.triangles")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("viz.visual_debugger")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("webaudio.audionode")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("webaudio.audionode"))
+        .SetTags("audio"),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("webgpu")),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("webnn")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("webrtc")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("webrtc"))
+        .SetTags("audio", "video"),
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("worker.scheduler")),
-    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("xr.debug")),
+    perfetto::Category(TRACE_DISABLED_BY_DEFAULT("xr.debug")).SetTags("debug"),
     perfetto::Category::Group("android_webview,toplevel"),
     perfetto::Category::Group("android_webview.timeline,android.ui.jank"),
     perfetto::Category::Group("base,toplevel"),
diff --git a/buildtools/deps_revisions.gni b/buildtools/deps_revisions.gni
index 72dff7b..ab4de6f 100644
--- a/buildtools/deps_revisions.gni
+++ b/buildtools/deps_revisions.gni
@@ -5,5 +5,5 @@
 declare_args() {
   # Used to cause full rebuilds on libc++ rolls. This should be kept in sync
   # with the libcxx_revision var in //DEPS.
-  libcxx_revision = "a9cc573e7c591795d11b72d8323ba0e573b55bd6"
+  libcxx_revision = "a01c02c9d4acbdae3b7e8a2f3ee58579a9c29f96"
 }
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc
index 30bad49..da7360a 100644
--- a/cc/metrics/compositor_frame_reporter.cc
+++ b/cc/metrics/compositor_frame_reporter.cc
@@ -602,7 +602,11 @@
       layer_tree_host_id_(layer_tree_host_id),
       global_trackers_(trackers) {
   DCHECK(global_trackers_.dropped_frame_counter);
-  global_trackers_.dropped_frame_counter->OnBeginFrame(args);
+  DCHECK(global_trackers_.frame_sorter);
+  if (global_trackers_.dropped_frame_counter
+          ->first_contentful_paint_received()) {
+    global_trackers_.frame_sorter->AddNewFrame(args);
+  }
   if (scrolling_thread_ == FrameInfo::SmoothEffectDrivingThread::kCompositor) {
     DCHECK(smooth_thread_ == SmoothThread::kSmoothCompositor ||
            smooth_thread_ == SmoothThread::kSmoothBoth);
@@ -995,7 +999,11 @@
     else
       global_trackers_.dropped_frame_counter->AddGoodFrame();
   }
-  global_trackers_.dropped_frame_counter->OnEndFrame(args_, frame_info);
+  if (global_trackers_.dropped_frame_counter
+          ->first_contentful_paint_received()) {
+    // Delegates call to DFC->OnEndFrame.
+    global_trackers_.frame_sorter->AddFrameResult(args_, frame_info);
+  }
 }
 
 void CompositorFrameReporter::EndCurrentStage(base::TimeTicks end_time) {
diff --git a/cc/metrics/compositor_frame_reporter.h b/cc/metrics/compositor_frame_reporter.h
index c4bd318..61ea132 100644
--- a/cc/metrics/compositor_frame_reporter.h
+++ b/cc/metrics/compositor_frame_reporter.h
@@ -25,6 +25,7 @@
 #include "cc/metrics/event_metrics.h"
 #include "cc/metrics/frame_info.h"
 #include "cc/metrics/frame_sequence_metrics.h"
+#include "cc/metrics/frame_sequence_tracker_collection.h"
 #include "cc/metrics/predictor_jank_tracker.h"
 #include "cc/metrics/scroll_jank_dropped_frame_tracker.h"
 #include "cc/scheduler/scheduler.h"
@@ -38,7 +39,7 @@
 namespace cc {
 class DroppedFrameCounter;
 class EventLatencyTracker;
-class FrameSequenceTrackerCollection;
+class FrameSorter;
 class LatencyUkmReporter;
 
 struct GlobalMetricsTrackers {
@@ -53,6 +54,7 @@
   RAW_PTR_EXCLUSION ScrollJankDroppedFrameTracker*
       scroll_jank_dropped_frame_tracker = nullptr;
   RAW_PTR_EXCLUSION ScrollJankUkmReporter* scroll_jank_ukm_reporter = nullptr;
+  RAW_PTR_EXCLUSION FrameSorter* frame_sorter = nullptr;
 };
 
 // This is used for tracing and reporting the duration of pipeline stages within
diff --git a/cc/metrics/compositor_frame_reporter_unittest.cc b/cc/metrics/compositor_frame_reporter_unittest.cc
index 4b324da..a861809 100644
--- a/cc/metrics/compositor_frame_reporter_unittest.cc
+++ b/cc/metrics/compositor_frame_reporter_unittest.cc
@@ -197,8 +197,14 @@
   }
 
   std::unique_ptr<CompositorFrameReporter> CreatePipelineReporter() {
-    GlobalMetricsTrackers trackers{&dropped_frame_counter_, nullptr, nullptr,
-                                   nullptr, nullptr};
+    GlobalMetricsTrackers trackers{&dropped_frame_counter_,
+                                   nullptr,
+                                   nullptr,
+                                   nullptr,
+                                   nullptr,
+                                   nullptr,
+                                   nullptr,
+                                   &frame_sorter_};
     auto reporter = std::make_unique<CompositorFrameReporter>(
         ActiveTrackers(), viz::BeginFrameArgs(),
         /*should_report_metrics=*/true,
@@ -240,6 +246,7 @@
 
   DroppedFrameCounter dropped_frame_counter_;
   TotalFrameCounter total_frame_counter_;
+  FrameSorter frame_sorter_;
   std::unique_ptr<CompositorFrameReporter> pipeline_reporter_;
 
   // Number of breakdown stages of the current PipelineReporter
diff --git a/cc/metrics/compositor_frame_reporting_controller.cc b/cc/metrics/compositor_frame_reporting_controller.cc
index fbed6caa..a39a3f8 100644
--- a/cc/metrics/compositor_frame_reporting_controller.cc
+++ b/cc/metrics/compositor_frame_reporting_controller.cc
@@ -60,10 +60,6 @@
 }
 
 CompositorFrameReportingController::~CompositorFrameReportingController() {
-  if (global_trackers_.dropped_frame_counter) {
-    global_trackers_.dropped_frame_counter->SetSortedFrameCallback(
-        base::NullCallback());
-  }
   base::TimeTicks now = Now();
   for (int i = 0; i < PipelineStage::kNumPipelineStages; ++i) {
     if (reporters_[i]) {
@@ -78,6 +74,16 @@
 
   predictor_jank_tracker_->set_scroll_jank_ukm_reporter(nullptr);
   scroll_jank_dropped_frame_tracker_->set_scroll_jank_ukm_reporter(nullptr);
+  if (global_trackers_.frame_sorter) {
+    if (global_trackers_.dropped_frame_counter) {
+      global_trackers_.frame_sorter->RemoveObserver(
+          global_trackers_.dropped_frame_counter);
+    }
+    if (global_trackers_.frame_sequence_trackers) {
+      global_trackers_.frame_sorter->RemoveObserver(
+          global_trackers_.frame_sequence_trackers);
+    }
+  }
 }
 
 void CompositorFrameReportingController::SetVisible(bool visible) {
@@ -122,11 +128,16 @@
       previous_frame.frame_id.source_id == args.frame_id.source_id) {
     CreateReportersForDroppedFrames(previous_frame, args);
   }
-
+  FrameSequenceTrackerCollection* trackers =
+      global_trackers_.frame_sequence_trackers;
   last_started_compositor_frame_.args = args;
-  last_started_compositor_frame_.scrolling_thread = scrolling_thread_;
-  last_started_compositor_frame_.active_trackers = active_trackers_;
-  last_started_compositor_frame_.smooth_thread = GetSmoothThread();
+  if (trackers) {
+    last_started_compositor_frame_.scrolling_thread =
+        trackers->GetScrollingThread();
+    last_started_compositor_frame_.active_trackers =
+        trackers->GetActiveTrackers();
+    last_started_compositor_frame_.smooth_thread = trackers->GetSmoothThread();
+  }
 }
 
 void CompositorFrameReportingController::WillBeginImplFrame(
@@ -150,9 +161,20 @@
                                Now());
     }
   }
+  FrameSequenceTrackerCollection* trackers =
+      global_trackers_.frame_sequence_trackers;
+  ActiveTrackers active_trackers;
+  FrameInfo::SmoothEffectDrivingThread scrolling_thread =
+      FrameInfo::SmoothEffectDrivingThread::kUnknown;
+  FrameInfo::SmoothThread smooth_thread = FrameInfo::SmoothThread::kSmoothNone;
+  if (trackers) {
+    active_trackers = trackers->GetActiveTrackers();
+    scrolling_thread = trackers->GetScrollingThread();
+    smooth_thread = trackers->GetSmoothThread();
+  }
   auto reporter = std::make_unique<CompositorFrameReporter>(
-      active_trackers_, args, should_report_histograms_, GetSmoothThread(),
-      scrolling_thread_, layer_tree_host_id_, global_trackers_);
+      active_trackers, args, should_report_histograms_, smooth_thread,
+      scrolling_thread, layer_tree_host_id_, global_trackers_);
   reporter->set_tick_clock(tick_clock_);
   reporter->StartStage(StageType::kBeginImplFrameToSendBeginMainFrame,
                        begin_time);
@@ -178,9 +200,13 @@
     // beginMain frame before next BeginImplFrame (Not reached the ImplFrame
     // deadline yet). So will start a new reporter at BeginMainFrame, and use
     // the state(s) from the ImplFrame where necessary.
-    auto scrolling_thread = scrolling_thread_;
-    auto active_trackers = active_trackers_;
-    auto smooth_thread = GetSmoothThread();
+    FrameSequenceTrackerCollection* trackers =
+        global_trackers_.frame_sequence_trackers;
+    ActiveTrackers active_trackers;
+    FrameInfo::SmoothEffectDrivingThread scrolling_thread =
+        FrameInfo::SmoothEffectDrivingThread::kUnknown;
+    FrameInfo::SmoothThread smooth_thread =
+        FrameInfo::SmoothThread::kSmoothNone;
     if (args.frame_id == last_started_compositor_frame_.args.frame_id) {
       // TODO(crbug.com/40207819): Instead of replacing all current information
       // with the older information from when the impl-frame started, merge the
@@ -188,6 +214,10 @@
       scrolling_thread = last_started_compositor_frame_.scrolling_thread;
       active_trackers = last_started_compositor_frame_.active_trackers;
       smooth_thread = last_started_compositor_frame_.smooth_thread;
+    } else if (trackers) {
+      active_trackers = trackers->GetActiveTrackers();
+      scrolling_thread = trackers->GetScrollingThread();
+      smooth_thread = trackers->GetSmoothThread();
     }
     auto reporter = std::make_unique<CompositorFrameReporter>(
         active_trackers, args, should_report_histograms_, smooth_thread,
@@ -692,71 +722,6 @@
                        PipelineStage::kReadyToCommit);
 }
 
-void CompositorFrameReportingController::AddActiveTracker(
-    FrameSequenceTrackerType type) {
-  active_trackers_.set(static_cast<size_t>(type));
-}
-
-void CompositorFrameReportingController::RemoveActiveTracker(
-    FrameSequenceTrackerType type) {
-  active_trackers_.reset(static_cast<size_t>(type));
-  if (global_trackers_.dropped_frame_counter)
-    global_trackers_.dropped_frame_counter->ReportFrames();
-}
-
-void CompositorFrameReportingController::SetScrollingThread(
-    FrameInfo::SmoothEffectDrivingThread thread) {
-  auto current_scrolling_thread = scrolling_thread_;
-  base::TimeTicks set_time = Now();
-
-  // Assign the thread.
-  scrolling_thread_ = thread;
-
-  // keep the history for the last 3 seconds.
-  if (!scroll_thread_history_.empty()) {
-    auto expired_scrolling_thread =
-        scroll_thread_history_.lower_bound(set_time - base::Seconds(3));
-    scroll_thread_history_.erase(scroll_thread_history_.begin(),
-                                 expired_scrolling_thread);
-  }
-
-  // Only traces the history if there is a change in scrolling_thread
-  if (current_scrolling_thread != scrolling_thread_) {
-    scroll_thread_history_.insert(
-        std::make_pair(set_time, current_scrolling_thread));
-  }
-}
-
-void CompositorFrameReportingController::SetThreadAffectsSmoothness(
-    FrameInfo::SmoothEffectDrivingThread thread_type,
-    bool affects_smoothness) {
-  auto current_smooth_thread = GetSmoothThread();
-  base::TimeTicks set_time = Now();
-
-  if (thread_type == FrameInfo::SmoothEffectDrivingThread::kCompositor) {
-    is_compositor_thread_driving_smoothness_ = affects_smoothness;
-  } else if (thread_type == FrameInfo::SmoothEffectDrivingThread::kRaster) {
-    is_raster_thread_driving_smoothness_ = affects_smoothness;
-  } else {
-    DCHECK_EQ(thread_type, FrameInfo::SmoothEffectDrivingThread::kMain);
-    is_main_thread_driving_smoothness_ = affects_smoothness;
-  }
-
-  // keep the history for the last 3 seconds.
-  if (!smooth_thread_history_.empty()) {
-    auto expired_smooth_thread =
-        smooth_thread_history_.lower_bound(set_time - base::Seconds(3));
-    smooth_thread_history_.erase(smooth_thread_history_.begin(),
-                                 expired_smooth_thread);
-  }
-
-  // Only trackes the history if there is a change in smooth_thread_
-  if (current_smooth_thread != GetSmoothThread()) {
-    smooth_thread_history_.insert(
-        std::make_pair(set_time, current_smooth_thread));
-  }
-}
-
 void CompositorFrameReportingController::AdvanceReporterStage(
     PipelineStage start,
     PipelineStage target) {
@@ -836,41 +801,6 @@
   latency_ukm_reporter_->SetSourceId(source_id);
 }
 
-CompositorFrameReportingController::SmoothThread
-CompositorFrameReportingController::GetSmoothThread() const {
-  if (is_main_thread_driving_smoothness_) {
-    return is_compositor_thread_driving_smoothness_ ? SmoothThread::kSmoothBoth
-                                                    : SmoothThread::kSmoothMain;
-  }
-  if (is_raster_thread_driving_smoothness_) {
-    return SmoothThread::kSmoothRaster;
-  }
-
-  return is_compositor_thread_driving_smoothness_
-             ? SmoothThread::kSmoothCompositor
-             : SmoothThread::kSmoothNone;
-}
-
-CompositorFrameReportingController::SmoothThread
-CompositorFrameReportingController::GetSmoothThreadAtTime(
-    base::TimeTicks timestamp) const {
-  auto last_smooth_thread = smooth_thread_history_.lower_bound(timestamp);
-  if (last_smooth_thread == smooth_thread_history_.end()) {
-    return GetSmoothThread();
-  }
-  return last_smooth_thread->second;
-}
-
-CompositorFrameReporter::SmoothEffectDrivingThread
-CompositorFrameReportingController::GetScrollThreadAtTime(
-    base::TimeTicks timestamp) const {
-  auto last_scroll_thread = scroll_thread_history_.lower_bound(timestamp);
-  if (last_scroll_thread == scroll_thread_history_.end()) {
-    return scrolling_thread_;
-  }
-  return last_scroll_thread->second;
-}
-
 CompositorFrameReporter*
 CompositorFrameReportingController::GetOutstandingUpdatesFromMain(
     const viz::BeginFrameId& id) const {
@@ -919,6 +849,12 @@
     return;
 
   auto timestamp = old_args.frame_time + old_args.interval;
+  FrameSequenceTrackerCollection* trackers =
+      global_trackers_.frame_sequence_trackers;
+  ActiveTrackers active_trackers;
+  FrameInfo::SmoothEffectDrivingThread scrolling_thread =
+      FrameInfo::SmoothEffectDrivingThread::kUnknown;
+  FrameInfo::SmoothThread smooth_thread = FrameInfo::SmoothThread::kSmoothNone;
   for (uint32_t i = 1; i < interval; ++i, timestamp += old_args.interval) {
     auto args = viz::BeginFrameArgs::Create(
         BEGINFRAME_FROM_HERE, old_args.frame_id.source_id,
@@ -931,10 +867,14 @@
     // Set the scrolling thread based on the global frame sequence trackers
     // rather than the `scrolling_thread_` member, because the scrolling thread
     // might have changed for a skipped or backfilled frame.
+    if (trackers) {
+      active_trackers = trackers->GetActiveTrackers();
+      scrolling_thread = trackers->GetScrollThreadAtTime(timestamp);
+      smooth_thread = trackers->GetSmoothThreadAtTime(timestamp);
+    }
     auto reporter = std::make_unique<CompositorFrameReporter>(
-        active_trackers_, args, should_report_histograms_,
-        GetSmoothThreadAtTime(timestamp), GetScrollThreadAtTime(timestamp),
-        layer_tree_host_id_, global_trackers_);
+        active_trackers, args, should_report_histograms_, smooth_thread,
+        scrolling_thread, layer_tree_host_id_, global_trackers_);
     reporter->set_tick_clock(tick_clock_);
     reporter->StartStage(StageType::kBeginImplFrameToSendBeginMainFrame,
                          timestamp);
@@ -944,22 +884,16 @@
   }
 }
 
-void CompositorFrameReportingController::AddSortedFrame(
-    const viz::BeginFrameArgs& args,
-    const FrameInfo& frame_info) {
-  if (global_trackers_.frame_sequence_trackers) {
-    global_trackers_.frame_sequence_trackers->AddSortedFrame(args, frame_info);
-  }
-}
-
 void CompositorFrameReportingController::SetDroppedFrameCounter(
     DroppedFrameCounter* counter) {
-  global_trackers_.dropped_frame_counter = counter;
-  if (counter) {
-    counter->SetSortedFrameCallback(
-        base::BindRepeating(&CompositorFrameReportingController::AddSortedFrame,
-                            base::Unretained(this)));
+  if (global_trackers_.dropped_frame_counter && global_trackers_.frame_sorter) {
+    global_trackers_.frame_sorter->RemoveObserver(
+        global_trackers_.dropped_frame_counter);
   }
+  if (global_trackers_.frame_sorter) {
+    global_trackers_.frame_sorter->AddObserver(counter);
+  }
+  global_trackers_.dropped_frame_counter = counter;
 }
 
 }  // namespace cc
diff --git a/cc/metrics/compositor_frame_reporting_controller.h b/cc/metrics/compositor_frame_reporting_controller.h
index 2d998b40..2b3921cc 100644
--- a/cc/metrics/compositor_frame_reporting_controller.h
+++ b/cc/metrics/compositor_frame_reporting_controller.h
@@ -18,6 +18,7 @@
 #include "cc/metrics/compositor_frame_reporter.h"
 #include "cc/metrics/event_metrics.h"
 #include "cc/metrics/frame_sequence_metrics.h"
+#include "cc/metrics/frame_sorter.h"
 #include "cc/metrics/predictor_jank_tracker.h"
 #include "cc/metrics/scroll_jank_dropped_frame_tracker.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
@@ -91,14 +92,6 @@
   void InitializeUkmManager(std::unique_ptr<ukm::UkmRecorder> recorder);
   void SetSourceId(ukm::SourceId source_id);
 
-  void AddActiveTracker(FrameSequenceTrackerType type);
-  void RemoveActiveTracker(FrameSequenceTrackerType type);
-  void SetScrollingThread(FrameInfo::SmoothEffectDrivingThread thread);
-
-  void SetThreadAffectsSmoothness(
-      FrameInfo::SmoothEffectDrivingThread thread_type,
-      bool affects_smoothness);
-
   void set_tick_clock(const base::TickClock* tick_clock) {
     DCHECK(tick_clock);
     tick_clock_ = tick_clock;
@@ -110,13 +103,38 @@
     return reporters_;
   }
 
+  void SetFrameSorter(FrameSorter* frame_sorter) {
+    global_trackers_.frame_sorter = frame_sorter;
+  }
+
   void SetDroppedFrameCounter(DroppedFrameCounter* counter);
 
+  void ClearDroppedFrameCounter() {
+    if (global_trackers_.frame_sorter &&
+        global_trackers_.dropped_frame_counter) {
+      global_trackers_.frame_sorter->RemoveObserver(
+          global_trackers_.dropped_frame_counter);
+    }
+    global_trackers_.dropped_frame_counter = nullptr;
+  }
+
   void SetFrameSequenceTrackerCollection(
       FrameSequenceTrackerCollection* frame_sequence_trackers) {
+    if (global_trackers_.frame_sorter) {
+      global_trackers_.frame_sorter->AddObserver(frame_sequence_trackers);
+    }
     global_trackers_.frame_sequence_trackers = frame_sequence_trackers;
   }
 
+  void ClearFrameSequenceTrackerCollection() {
+    if (global_trackers_.frame_sorter &&
+        global_trackers_.frame_sequence_trackers) {
+      global_trackers_.frame_sorter->RemoveObserver(
+          global_trackers_.frame_sequence_trackers);
+    }
+    global_trackers_.frame_sequence_trackers = nullptr;
+  }
+
   void set_event_latency_tracker(EventLatencyTracker* event_latency_tracker) {
     global_trackers_.event_latency_tracker = event_latency_tracker;
   }
@@ -159,11 +177,6 @@
   bool CanSubmitMainFrame(const viz::BeginFrameId& id) const;
   std::unique_ptr<CompositorFrameReporter> RestoreReporterAtBeginImpl(
       const viz::BeginFrameId& id);
-  SmoothThread GetSmoothThread() const;
-  SmoothEffectDrivingThread GetScrollingThread() const;
-  SmoothThread GetSmoothThreadAtTime(base::TimeTicks timestamp) const;
-  SmoothEffectDrivingThread GetScrollThreadAtTime(
-      base::TimeTicks timestamp) const;
 
   // Checks whether there are reporters containing updates from the main
   // thread, and returns a pointer to that reporter (if any). Otherwise
@@ -192,28 +205,12 @@
   void SetPartialUpdateDeciderWhenWaitingOnMain(
       std::unique_ptr<CompositorFrameReporter>& reporter);
 
-  void AddSortedFrame(const viz::BeginFrameArgs& args,
-                      const FrameInfo& frame_info);
-
   const bool should_report_histograms_;
   const int layer_tree_host_id_;
 
   viz::BeginFrameId last_submitted_frame_id_;
 
   bool next_activate_has_invalidation_ = false;
-  ActiveTrackers active_trackers_;
-  FrameInfo::SmoothEffectDrivingThread scrolling_thread_ =
-      FrameInfo::SmoothEffectDrivingThread::kUnknown;
-
-  bool is_compositor_thread_driving_smoothness_ = false;
-  bool is_main_thread_driving_smoothness_ = false;
-  bool is_raster_thread_driving_smoothness_ = false;
-  // Sorted history of smooththread. Element i indicating the smooththread
-  // from timestamp of element i-1 until timestamp of element i.
-  std::map<base::TimeTicks, SmoothThread> smooth_thread_history_;
-  // Sorted history of scrollthread. Element i indicating the smooththread
-  // from timestamp of element i-1 until timestamp of element i.
-  std::map<base::TimeTicks, SmoothEffectDrivingThread> scroll_thread_history_;
 
   // Must outlive `reporters_` and `submitted_compositor_frames_` (which also
   // have reporters), since destroying the reporters can flush frames to
diff --git a/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
index a38dc0feb..b1b0f30 100644
--- a/cc/metrics/compositor_frame_reporting_controller_unittest.cc
+++ b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
@@ -17,6 +17,8 @@
 #include "base/time/time.h"
 #include "cc/metrics/dropped_frame_counter.h"
 #include "cc/metrics/event_metrics.h"
+#include "cc/metrics/frame_sequence_metrics.h"
+#include "cc/metrics/frame_sequence_tracker_collection.h"
 #include "cc/metrics/total_frame_counter.h"
 #include "cc/scheduler/commit_earlyout_reason.h"
 #include "components/viz/common/frame_timing_details.h"
@@ -116,10 +118,14 @@
 
 class CompositorFrameReportingControllerTest : public testing::Test {
  public:
-  CompositorFrameReportingControllerTest() : current_id_(1, 1) {
+  CompositorFrameReportingControllerTest()
+      : current_id_(1, 1), tracker_collection_(false, &dropped_counter_) {
     test_tick_clock_.SetNowTicks(base::TimeTicks::Now());
     reporting_controller_.set_tick_clock(&test_tick_clock_);
     args_ = SimulateBeginFrameArgs(current_id_);
+    reporting_controller_.SetFrameSorter(&frame_sorter_);
+    reporting_controller_.SetFrameSequenceTrackerCollection(
+        &tracker_collection_);
     reporting_controller_.SetDroppedFrameCounter(&dropped_counter_);
     dropped_counter_.set_total_counter(&total_frame_counter_);
   }
@@ -336,6 +342,8 @@
   base::TimeTicks end_activation_time_;
   base::TimeTicks submit_time_;
   viz::FrameTokenGenerator current_token_;
+  FrameSorter frame_sorter_;
+  FrameSequenceTrackerCollection tracker_collection_;
   DroppedFrameCounter dropped_counter_;
   TotalFrameCounter total_frame_counter_;
   TestCompositorFrameReportingController reporting_controller_;
@@ -1192,17 +1200,17 @@
   base::HistogramTester histogram_tester;
 
   SimulatePresentCompositorFrame();
-  reporting_controller_.AddActiveTracker(
+  tracker_collection_.StartSequence(
       FrameSequenceTrackerType::kCompositorAnimation);
   SimulatePresentCompositorFrame();
-  reporting_controller_.AddActiveTracker(
-      FrameSequenceTrackerType::kWheelScroll);
+  tracker_collection_.StartScrollSequence(
+      FrameSequenceTrackerType::kWheelScroll,
+      SmoothEffectDrivingThread::kCompositor);
   SimulatePresentCompositorFrame();
-  reporting_controller_.RemoveActiveTracker(
+  tracker_collection_.StopSequence(
       FrameSequenceTrackerType::kCompositorAnimation);
   SimulatePresentCompositorFrame();
-  reporting_controller_.RemoveActiveTracker(
-      FrameSequenceTrackerType::kWheelScroll);
+  tracker_collection_.StopSequence(FrameSequenceTrackerType::kWheelScroll);
   SimulatePresentCompositorFrame();
 
   // All frames are presented so only test on-dropped cases.
@@ -1669,7 +1677,9 @@
   EXPECT_EQ(1u, reporting_controller_.GetBlockedReportersCount());
 
   reporting_controller_.ResetReporters();
-  reporting_controller_.SetDroppedFrameCounter(nullptr);
+  reporting_controller_.ClearFrameSequenceTrackerCollection();
+  reporting_controller_.ClearDroppedFrameCounter();
+  reporting_controller_.SetFrameSorter(nullptr);
 }
 
 // Verifies that when a dependent frame is submitted to Viz, but not presented
@@ -1729,7 +1739,9 @@
   EXPECT_EQ(1u, dropped_counter_.total_dropped());
 
   reporting_controller_.ResetReporters();
-  reporting_controller_.SetDroppedFrameCounter(nullptr);
+  reporting_controller_.ClearFrameSequenceTrackerCollection();
+  reporting_controller_.ClearDroppedFrameCounter();
+  reporting_controller_.SetFrameSorter(nullptr);
 }
 
 TEST_F(CompositorFrameReportingControllerTest,
@@ -1766,7 +1778,9 @@
   EXPECT_EQ(0u, dropped_counter_.total_dropped());
 
   reporting_controller_.ResetReporters();
-  reporting_controller_.SetDroppedFrameCounter(nullptr);
+  reporting_controller_.ClearFrameSequenceTrackerCollection();
+  reporting_controller_.ClearDroppedFrameCounter();
+  reporting_controller_.SetFrameSorter(nullptr);
 }
 
 TEST_F(CompositorFrameReportingControllerTest,
@@ -1850,9 +1864,10 @@
 
 TEST_F(CompositorFrameReportingControllerTest,
        SkippedFramesFromDisplayCompositorHaveSmoothThread) {
-  auto thread_type_compositor = SmoothEffectDrivingThread::kCompositor;
-  reporting_controller_.SetThreadAffectsSmoothness(thread_type_compositor,
-                                                   true);
+  auto thread_type_compositor = FrameInfo::SmoothThread::kSmoothCompositor;
+  tracker_collection_.StartSequence(
+      FrameSequenceTrackerType::kCompositorAnimation);
+  EXPECT_EQ(tracker_collection_.GetSmoothThread(), thread_type_compositor);
   dropped_counter_.OnFirstContentfulPaintReceived();
 
   // Submit and present two compositor frames.
@@ -1877,8 +1892,10 @@
   EXPECT_EQ(kSkipFrames_1, dropped_counter_.total_smoothness_dropped());
 
   // Now skip over a few frames which are not affecting smoothness.
-  reporting_controller_.SetThreadAffectsSmoothness(thread_type_compositor,
-                                                   false);
+  tracker_collection_.StopSequence(
+      FrameSequenceTrackerType::kCompositorAnimation);
+  EXPECT_EQ(tracker_collection_.GetSmoothThread(),
+            FrameInfo::SmoothThread::kSmoothNone);
   const uint32_t kSkipFrames_2 = 7;
   for (uint32_t i = 0; i < kSkipFrames_2; ++i)
     IncrementCurrentId();
@@ -1890,8 +1907,9 @@
   EXPECT_EQ(kSkipFrames_1, dropped_counter_.total_smoothness_dropped());
 
   // Now skip over a few frames more frames which are affecting smoothness.
-  reporting_controller_.SetThreadAffectsSmoothness(thread_type_compositor,
-                                                   true);
+  tracker_collection_.StartSequence(
+      FrameSequenceTrackerType::kCompositorAnimation);
+  EXPECT_EQ(tracker_collection_.GetSmoothThread(), thread_type_compositor);
   const uint32_t kSkipFrames_3 = 10;
   for (uint32_t i = 0; i < kSkipFrames_3; ++i)
     IncrementCurrentId();
@@ -1990,9 +2008,11 @@
 
 TEST_F(CompositorFrameReportingControllerTest,
        NewMainThreadUpdateNotReportedAsDropped) {
-  auto thread_type_main = SmoothEffectDrivingThread::kMain;
-  reporting_controller_.SetThreadAffectsSmoothness(thread_type_main,
-                                                   /*affects_smoothness=*/true);
+  auto thread_type_main = FrameInfo::SmoothThread::kSmoothMain;
+  tracker_collection_.StartSequence(
+      FrameSequenceTrackerType::kMainThreadAnimation);
+  EXPECT_EQ(tracker_collection_.GetSmoothThread(), thread_type_main);
+
   dropped_counter_.OnFirstContentfulPaintReceived();
   dropped_counter_.SetTimeFirstContentfulPaintReceivedForTesting(
       args_.frame_time);
@@ -2037,11 +2057,6 @@
 
 TEST_F(CompositorFrameReportingControllerTest,
        NoUpdateCompositorWithJankyMain) {
-  reporting_controller_.SetThreadAffectsSmoothness(
-      SmoothEffectDrivingThread::kCompositor, /*affects_smoothness=*/true);
-  reporting_controller_.SetThreadAffectsSmoothness(
-      SmoothEffectDrivingThread::kMain, /*affects_smoothness=*/false);
-
   dropped_counter_.OnFirstContentfulPaintReceived();
   dropped_counter_.SetTimeFirstContentfulPaintReceivedForTesting(
       args_.frame_time);
diff --git a/cc/metrics/dropped_frame_counter.cc b/cc/metrics/dropped_frame_counter.cc
index 03c5c640..106adb84 100644
--- a/cc/metrics/dropped_frame_counter.cc
+++ b/cc/metrics/dropped_frame_counter.cc
@@ -115,14 +115,8 @@
   return histogram.Dump(stream);
 }
 
-DroppedFrameCounter::DroppedFrameCounter()
-    : frame_sorter_(base::BindRepeating(&DroppedFrameCounter::NotifyFrameResult,
-                                        base::Unretained(this))) {
-}
-DroppedFrameCounter::~DroppedFrameCounter() {
-  sorted_frame_callback_.Reset();
-  frame_sorter_.Reset();
-}
+DroppedFrameCounter::DroppedFrameCounter() = default;
+DroppedFrameCounter::~DroppedFrameCounter() = default;
 
 uint32_t DroppedFrameCounter::GetAverageThroughput() const {
   size_t good_frames = 0;
@@ -151,12 +145,11 @@
   ++total_dropped_;
 }
 
+// Start with flushing the frames in frame_sorter ignoring the currently
+// pending frames, so all callers should call frame_sorter_.Reset();
+// prior to this function.
+// TODO(crbug.com/409093076): Remove all uses of this function.
 void DroppedFrameCounter::ResetPendingFrames(base::TimeTicks timestamp) {
-  // Start with flushing the frames in frame_sorter ignoring the currently
-  // pending frames (In other words calling NotifyFrameResult and update
-  // smoothness metrics tracked for all frames that have received their ack).
-  frame_sorter_.Reset();
-
   // Before resetting the pending frames, update the measurements for the
   // sliding windows.
   if (!latest_sliding_window_start_.is_null()) {
@@ -204,12 +197,6 @@
   report_for_ui_ = true;
 }
 
-void DroppedFrameCounter::OnBeginFrame(const viz::BeginFrameArgs& args) {
-  if (first_contentful_paint_received_) {
-    frame_sorter_.AddNewFrame(args);
-  }
-}
-
 void DroppedFrameCounter::OnEndFrame(const viz::BeginFrameArgs& args,
                                      const FrameInfo& frame_info) {
   const bool is_dropped = frame_info.IsDroppedAffectingSmoothness();
@@ -219,8 +206,7 @@
   // Don't measure smoothness for frames that start before FCP is received, or
   // that have already been reported as dropped.
   if (is_dropped && first_contentful_paint_received_ &&
-      args.frame_time >= time_first_contentful_paint_received_ &&
-      !frame_sorter_.IsAlreadyReportedDropped(args.frame_id)) {
+      args.frame_time >= time_first_contentful_paint_received_) {
     ++total_smoothness_dropped_;
 
     if (!report_for_ui_) {
@@ -228,10 +214,6 @@
     }
   }
 
-  if (first_contentful_paint_received_) {
-    frame_sorter_.AddFrameResult(args, frame_info);
-  }
-
   // Report frames on every frame for UI. And this needs to happen after
   // `frame_sorter_.AddFrameResult` so that the current ending frame is included
   // in the sliding window.
@@ -289,8 +271,11 @@
   ukm_smoothness_data_ = smoothness_data;
 }
 
+// Start with flushing the frames in frame_sorter ignoring the currently
+// pending frames, so all callers should call frame_sorter_.Reset();
+// prior to invoking this function.
+// TODO(crbug.com/409093076): Remove all uses of this function.
 void DroppedFrameCounter::Reset() {
-  frame_sorter_.Reset();
   total_frames_ = 0;
   total_partial_ = 0;
   total_dropped_ = 0;
@@ -316,8 +301,8 @@
          sliding_window_.front().first.frame_time;
 }
 
-void DroppedFrameCounter::NotifyFrameResult(const viz::BeginFrameArgs& args,
-                                            const FrameInfo& frame_info) {
+void DroppedFrameCounter::AddSortedFrame(const viz::BeginFrameArgs& args,
+                                         const FrameInfo& frame_info) {
   // Entirely disregard the frames with interval larger than the window --
   // these are violating the assumptions in the below code and should
   // only occur with external frame control, where dropped frame stats
@@ -326,10 +311,7 @@
     return;
   }
 
-  if (sorted_frame_callback_)
-    sorted_frame_callback_.Run(args, frame_info);
-
-  sliding_window_.push({args, frame_info});
+  sliding_window_.emplace(args, frame_info);
   UpdateDroppedFrameCountInWindow(frame_info, 1);
 
   const bool is_dropped = frame_info.IsDroppedAffectingSmoothness();
@@ -347,6 +329,8 @@
     in_dropping_ = false;
   }
 
+  OnEndFrame(args, frame_info);
+
   if (ComputeCurrentWindowSize() < kDefaultSlidingWindowInterval) {
     return;
   }
@@ -447,8 +431,4 @@
   time_first_contentful_paint_received_ = base::TimeTicks::Now();
 }
 
-void DroppedFrameCounter::SetSortedFrameCallback(SortedFrameCallback callback) {
-  sorted_frame_callback_ = callback;
-}
-
 }  // namespace cc
diff --git a/cc/metrics/dropped_frame_counter.h b/cc/metrics/dropped_frame_counter.h
index 89c5f60b..1b5ddab8 100644
--- a/cc/metrics/dropped_frame_counter.h
+++ b/cc/metrics/dropped_frame_counter.h
@@ -29,7 +29,7 @@
 
 // This class maintains a counter for produced/dropped frames, and can be used
 // to estimate the recent throughput.
-class CC_EXPORT DroppedFrameCounter {
+class CC_EXPORT DroppedFrameCounter : public FrameSorterObserver {
  public:
   enum FrameState {
     kFrameStateDropped,
@@ -64,7 +64,7 @@
   };
 
   DroppedFrameCounter();
-  virtual ~DroppedFrameCounter();
+  ~DroppedFrameCounter() override;
 
   DroppedFrameCounter(const DroppedFrameCounter&) = delete;
   DroppedFrameCounter& operator=(const DroppedFrameCounter&) = delete;
@@ -77,11 +77,6 @@
 
   uint32_t GetAverageThroughput() const;
 
-  using SortedFrameCallback =
-      base::RepeatingCallback<void(const viz::BeginFrameArgs& args,
-                                   const FrameInfo&)>;
-  void SetSortedFrameCallback(SortedFrameCallback callback);
-
   typedef base::RingBuffer<FrameState, 180> RingBufferType;
   RingBufferType::Iterator Begin() const { return ring_buffer_.Begin(); }
   // `End()` points to the last `FrameState`, not past it.
@@ -93,9 +88,6 @@
   void ReportFrames();
   void ReportFramesOnEveryFrameForUI();
 
-  void OnBeginFrame(const viz::BeginFrameArgs& args);
-  virtual void OnEndFrame(const viz::BeginFrameArgs& args,
-                          const FrameInfo& frame_info);
   void SetUkmSmoothnessDestination(UkmSmoothnessDataShared* smoothness_data);
   void OnFirstContentfulPaintReceived();
 
@@ -161,9 +153,15 @@
     return sliding_window_current_percent_dropped_.value_or(0);
   }
 
+  bool first_contentful_paint_received() {
+    return first_contentful_paint_received_;
+  }
+
  private:
-  void NotifyFrameResult(const viz::BeginFrameArgs& args,
-                         const FrameInfo& frame_info);
+  void AddSortedFrame(const viz::BeginFrameArgs& args,
+                      const FrameInfo& frame_info) override;
+  virtual void OnEndFrame(const viz::BeginFrameArgs& args,
+                          const FrameInfo& frame_info);
   base::TimeDelta ComputeCurrentWindowSize() const;
 
   void PopSlidingWindow();
@@ -193,7 +191,6 @@
   std::optional<double> sliding_window_max_percent_dropped_After_5_sec_;
   base::TimeTicks time_first_contentful_paint_received_;
   raw_ptr<UkmSmoothnessDataShared> ukm_smoothness_data_ = nullptr;
-  FrameSorter frame_sorter_;
   raw_ptr<TotalFrameCounter> total_counter_ = nullptr;
 
   struct {
@@ -201,8 +198,6 @@
     double p95_window = 0;
   } last_reported_metrics_;
 
-  SortedFrameCallback sorted_frame_callback_;
-
   bool report_for_ui_ = false;
   std::optional<double> sliding_window_current_percent_dropped_;
 
diff --git a/cc/metrics/dropped_frame_counter_unittest.cc b/cc/metrics/dropped_frame_counter_unittest.cc
index aab43f3..0f711cc 100644
--- a/cc/metrics/dropped_frame_counter_unittest.cc
+++ b/cc/metrics/dropped_frame_counter_unittest.cc
@@ -17,7 +17,9 @@
 #include "build/chromeos_buildflags.h"
 #include "cc/animation/animation_host.h"
 #include "cc/base/features.h"
+#include "cc/metrics/compositor_frame_reporting_controller.h"
 #include "cc/metrics/custom_metrics_recorder.h"
+#include "cc/metrics/frame_sorter.h"
 #include "cc/test/fake_content_layer_client.h"
 #include "cc/test/fake_frame_info.h"
 #include "cc/test/fake_picture_layer.h"
@@ -307,6 +309,7 @@
     dropped_frame_counter_ = std::make_unique<DroppedFrameCounter>();
     dropped_frame_counter_->set_total_counter(&total_frame_counter_);
     dropped_frame_counter_->OnFirstContentfulPaintReceived();
+    frame_sorter_.AddObserver(dropped_frame_counter_.get());
   }
   ~DroppedFrameCounterTest() override = default;
 
@@ -315,9 +318,10 @@
     for (int i = 0; i < repeat; i++) {
       for (auto is_dropped : frame_states) {
         viz::BeginFrameArgs args_ = SimulateBeginFrameArgs();
-        dropped_frame_counter_->OnBeginFrame(args_);
-        dropped_frame_counter_->OnEndFrame(args_,
-                                           CreateStubFrameInfo(is_dropped));
+        if (dropped_frame_counter_->first_contentful_paint_received()) {
+          frame_sorter_.AddNewFrame(args_);
+          frame_sorter_.AddFrameResult(args_, CreateStubFrameInfo(is_dropped));
+        }
         sequence_number_++;
         frame_time_ += interval_;
       }
@@ -338,7 +342,9 @@
     std::vector<viz::BeginFrameArgs> args(repeat);
     for (int i = 0; i < repeat; i++) {
       args[i] = SimulateBeginFrameArgs();
-      dropped_frame_counter_->OnBeginFrame(args[i]);
+      if (dropped_frame_counter_->first_contentful_paint_received()) {
+        frame_sorter_.AddNewFrame(args[i]);
+      }
       sequence_number_++;
       frame_time_ += interval_;
     }
@@ -348,18 +354,19 @@
   // Simulate a main and impl thread update on the same frame.
   void SimulateForkedFrame(bool main_dropped, bool impl_dropped) {
     viz::BeginFrameArgs args_ = SimulateBeginFrameArgs();
-    dropped_frame_counter_->OnBeginFrame(args_);
-    dropped_frame_counter_->OnBeginFrame(args_);
-
+    if (dropped_frame_counter_->first_contentful_paint_received()) {
+      frame_sorter_.AddNewFrame(args_);
+      frame_sorter_.AddNewFrame(args_);
+    }
     // End the 'main thread' arm of the fork.
     auto main_info = CreateStubFrameInfo(main_dropped);
     main_info.main_thread_response = FrameInfo::MainThreadResponse::kIncluded;
-    dropped_frame_counter_->OnEndFrame(args_, main_info);
+    frame_sorter_.AddFrameResult(args_, main_info);
 
     // End the 'compositor thread' arm of the fork.
     auto impl_info = CreateStubFrameInfo(impl_dropped);
     impl_info.main_thread_response = FrameInfo::MainThreadResponse::kMissing;
-    dropped_frame_counter_->OnEndFrame(args_, impl_info);
+    frame_sorter_.AddFrameResult(args_, impl_info);
 
     sequence_number_++;
     frame_time_ += interval_;
@@ -393,6 +400,7 @@
 
  public:
   std::unique_ptr<DroppedFrameCounter> dropped_frame_counter_;
+  FrameSorter frame_sorter_;
 
  private:
   uint64_t sequence_number_ = 1;
@@ -600,7 +608,7 @@
   // End each of the frames as dropped. The first three should not count for
   // smoothness, only the last two.
   for (const auto& frame : pending_frames) {
-    dropped_frame_counter_->OnEndFrame(frame, CreateStubFrameInfo(true));
+    frame_sorter_.AddFrameResult(frame, CreateStubFrameInfo(true));
   }
   EXPECT_EQ(dropped_frame_counter_->total_smoothness_dropped(), 2u);
 }
@@ -637,7 +645,12 @@
 
   // Recorded (kFps * 3) samples of 20% dropped frame percentage. Only 3 seconds
   // of frames reported because there is no reports for the very 1st second.
-  EXPECT_EQ(recorder.report_count(), kFps * 3);
+  // Off-by-one introduced by FrameSorter refactor.
+  // We have inverted the order in which we call DFC::AddSortedFrame and
+  // DFC::OnEndFrame, meaning that DFC's sliding_window_current_percent_dropped_
+  // is set after OnEndFrame has been called at the 1s second threshold.
+  // Therefore, we expect one less call to the frame recorder.
+  EXPECT_EQ(recorder.report_count(), (kFps * 3) - 1);
   EXPECT_FLOAT_EQ(recorder.last_percent_dropped_frames(), 20.0f);
 
   recorder.Reset();
@@ -660,8 +673,11 @@
 };
 
 DroppedFrameCounterLegacyMetricsTest::DroppedFrameCounterLegacyMetricsTest() {
+  frame_sorter_.RemoveObserver(dropped_frame_counter_.get());
   dropped_frame_counter_ = std::make_unique<DroppedFrameCounter>();
+  frame_sorter_.Reset();
   dropped_frame_counter_->OnFirstContentfulPaintReceived();
+  frame_sorter_.AddObserver(dropped_frame_counter_.get());
 }
 
 TEST_F(DroppedFrameCounterLegacyMetricsTest, DoesNotReportLegacyMetrics) {
@@ -677,7 +693,11 @@
 
   // 5 seconds with 20% dropped frames.
   SimulateFrameSequence({false, false, false, false, true}, (kFps / 5) * 6);
-  EXPECT_EQ(recorder.report_count(), 5 * kFps);
+  // We have inverted the order in which we call DFC::AddSortedFrame and
+  // DFC::OnEndFrame, meaning that DFC's sliding_window_current_percent_dropped_
+  // is set after OnEndFrame has been called at the 1s second threshold.
+  // Therefore, we expect one less call to the frame recorder.
+  EXPECT_EQ(recorder.report_count(), (5 * kFps) - 1);
 
   // The following metrics should report data.
   // Average calculation
diff --git a/cc/metrics/frame_sequence_tracker_collection.cc b/cc/metrics/frame_sequence_tracker_collection.cc
index 65fc260..ddd96ca 100644
--- a/cc/metrics/frame_sequence_tracker_collection.cc
+++ b/cc/metrics/frame_sequence_tracker_collection.cc
@@ -9,7 +9,11 @@
 
 #include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
+#include "base/time/time.h"
 #include "cc/metrics/compositor_frame_reporting_controller.h"
+#include "cc/metrics/dropped_frame_counter.h"
+#include "cc/metrics/frame_info.h"
+#include "cc/metrics/frame_sequence_metrics.h"
 #include "cc/metrics/frame_sequence_tracker.h"
 #include "cc/metrics/ukm_dropped_frames_data.h"
 
@@ -29,10 +33,9 @@
 
 FrameSequenceTrackerCollection::FrameSequenceTrackerCollection(
     bool is_single_threaded,
-    CompositorFrameReportingController* compositor_frame_reporting_controller)
+    DroppedFrameCounter* dropped_frame_counter)
     : is_single_threaded_(is_single_threaded),
-      compositor_frame_reporting_controller_(
-          compositor_frame_reporting_controller) {}
+      dropped_frame_counter_(dropped_frame_counter) {}
 
 FrameSequenceTrackerCollection::~FrameSequenceTrackerCollection() {
   CleanUp();
@@ -42,6 +45,99 @@
   accumulated_metrics_.clear();
 }
 
+void FrameSequenceTrackerCollection::SetScrollingThread(
+    FrameInfo::SmoothEffectDrivingThread thread) {
+  auto current_scrolling_thread = scrolling_thread_;
+  base::TimeTicks set_time = base::TimeTicks::Now();
+
+  // Assign the thread.
+  scrolling_thread_ = thread;
+
+  // keep the history for the last 3 seconds.
+  if (!scroll_thread_history_.empty()) {
+    auto expired_scrolling_thread =
+        scroll_thread_history_.lower_bound(set_time - base::Seconds(3));
+    scroll_thread_history_.erase(scroll_thread_history_.begin(),
+                                 expired_scrolling_thread);
+  }
+
+  // Only traces the history if there is a change in scrolling_thread
+  if (current_scrolling_thread != scrolling_thread_) {
+    scroll_thread_history_.insert(
+        std::make_pair(set_time, current_scrolling_thread));
+  }
+}
+
+FrameInfo::SmoothThread FrameSequenceTrackerCollection::GetSmoothThread()
+    const {
+  if (main_thread_driving_smoothness_) {
+    return compositor_thread_driving_smoothness_
+               ? FrameInfo::SmoothThread::kSmoothBoth
+               : FrameInfo::SmoothThread::kSmoothMain;
+  }
+  if (raster_thread_driving_smoothness_) {
+    return FrameInfo::SmoothThread::kSmoothRaster;
+  }
+  return compositor_thread_driving_smoothness_
+             ? FrameInfo::SmoothThread::kSmoothCompositor
+             : FrameInfo::SmoothThread::kSmoothNone;
+}
+
+void FrameSequenceTrackerCollection::UpdateSmoothThreadHistory(
+    FrameInfo::SmoothEffectDrivingThread thread_type,
+    int modifier) {
+  auto current_smooth_thread = GetSmoothThread();
+  base::TimeTicks set_time = base::TimeTicks::Now();
+
+  // Update smooth effect thread tracking count based on the type of
+  // tracker being created or destroyed.
+  if (thread_type == FrameInfo::SmoothEffectDrivingThread::kCompositor) {
+    compositor_thread_driving_smoothness_ += modifier;
+  } else if (thread_type == FrameInfo::SmoothEffectDrivingThread::kRaster) {
+    raster_thread_driving_smoothness_ += modifier;
+  } else {
+    DCHECK_EQ(thread_type, FrameInfo::SmoothEffectDrivingThread::kMain);
+    main_thread_driving_smoothness_ += modifier;
+  }
+
+  // Only called if there is a change in smooth_thread_
+  if (current_smooth_thread != GetSmoothThread()) {
+    // Keep the history for the last 3 seconds.
+    if (!smooth_thread_history_.empty()) {
+      auto expired_smooth_thread =
+          smooth_thread_history_.lower_bound(set_time - base::Seconds(3));
+      smooth_thread_history_.erase(smooth_thread_history_.begin(),
+                                   expired_smooth_thread);
+    }
+    smooth_thread_history_.insert(
+        std::make_pair(set_time, current_smooth_thread));
+  }
+}
+
+FrameInfo::SmoothThread FrameSequenceTrackerCollection::GetSmoothThreadAtTime(
+    base::TimeTicks timestamp) const {
+  auto last_smooth_thread = smooth_thread_history_.lower_bound(timestamp);
+  if (last_smooth_thread == smooth_thread_history_.end()) {
+    return GetSmoothThread();
+  }
+  return last_smooth_thread->second;
+}
+
+FrameInfo::SmoothEffectDrivingThread
+FrameSequenceTrackerCollection::GetScrollThreadAtTime(
+    base::TimeTicks timestamp) const {
+  auto last_scroll_thread = scroll_thread_history_.lower_bound(timestamp);
+  if (last_scroll_thread == scroll_thread_history_.end()) {
+    return scrolling_thread_;
+  }
+  return last_scroll_thread->second;
+}
+
+FrameInfo::SmoothEffectDrivingThread
+FrameSequenceTrackerCollection::GetScrollingThread() const {
+  return scrolling_thread_;
+}
+
 FrameSequenceTracker* FrameSequenceTrackerCollection::StartSequenceInternal(
     FrameSequenceTrackerType type,
     FrameInfo::SmoothEffectDrivingThread scrolling_thread) {
@@ -55,8 +151,7 @@
   auto tracker = base::WrapUnique(new FrameSequenceTracker(type));
   frame_trackers_[key] = std::move(tracker);
 
-  if (compositor_frame_reporting_controller_)
-    compositor_frame_reporting_controller_->AddActiveTracker(type);
+  active_trackers_.set(static_cast<size_t>(type));
 
   auto* metrics = frame_trackers_[key]->metrics();
   if (accumulated_metrics_.contains(key)) {
@@ -65,36 +160,24 @@
   if (IsScrollType(type)) {
     DCHECK_NE(scrolling_thread, ThreadType::kUnknown);
     metrics->SetScrollingThread(scrolling_thread);
-    compositor_frame_reporting_controller_->SetScrollingThread(
-        scrolling_thread);
+    SetScrollingThread(scrolling_thread);
   }
 
   if (metrics->GetEffectiveThread() == ThreadType::kCompositor) {
-    if (compositor_frame_reporting_controller_ &&
-        compositor_thread_driving_smoothness_ == 0) {
-      compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
-          ThreadType::kCompositor, true);
-    }
-    ++compositor_thread_driving_smoothness_;
+    UpdateSmoothThreadHistory(ThreadType::kCompositor, /*modifier=*/1);
   } else if (metrics->GetEffectiveThread() == ThreadType::kRaster) {
-    if (compositor_frame_reporting_controller_ &&
-        raster_thread_driving_smoothness_ == 0) {
-      compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
-          ThreadType::kRaster, true);
-    }
-    ++raster_thread_driving_smoothness_;
+    UpdateSmoothThreadHistory(ThreadType::kRaster, /*modifier=*/1);
   } else {
     DCHECK_EQ(metrics->GetEffectiveThread(), ThreadType::kMain);
-    if (compositor_frame_reporting_controller_ &&
-        main_thread_driving_smoothness_ == 0) {
-      compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
-          ThreadType::kMain, true);
-    }
-    ++main_thread_driving_smoothness_;
+    UpdateSmoothThreadHistory(ThreadType::kMain, /*modifier=*/1);
   }
   return frame_trackers_[key].get();
 }
 
+ActiveTrackers FrameSequenceTrackerCollection::GetActiveTrackers() const {
+  return active_trackers_;
+}
+
 FrameSequenceTracker* FrameSequenceTrackerCollection::StartSequence(
     FrameSequenceTrackerType type) {
   DCHECK(!IsScrollType(type));
@@ -125,8 +208,7 @@
 
   auto key = std::make_pair(type, ThreadType::kUnknown);
   if (IsScrollType(type)) {
-    compositor_frame_reporting_controller_->SetScrollingThread(
-        ThreadType::kUnknown);
+    SetScrollingThread(ThreadType::kUnknown);
     key = std::make_pair(type, ThreadType::kCompositor);
     if (!frame_trackers_.contains(key))
       key = std::make_pair(type, ThreadType::kMain);
@@ -139,35 +221,20 @@
     return;
 
   auto tracker = std::move(frame_trackers_[key]);
-  if (compositor_frame_reporting_controller_) {
-    compositor_frame_reporting_controller_->RemoveActiveTracker(
-        tracker->type());
+  active_trackers_.reset(static_cast<size_t>(tracker->type()));
+  if (dropped_frame_counter_) {
+    dropped_frame_counter_->ReportFrames();
   }
 
   if (tracker->metrics()->GetEffectiveThread() == ThreadType::kCompositor) {
     DCHECK_GT(compositor_thread_driving_smoothness_, 0u);
-    --compositor_thread_driving_smoothness_;
-    if (compositor_frame_reporting_controller_ &&
-        compositor_thread_driving_smoothness_ == 0) {
-      compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
-          ThreadType::kCompositor, false);
-    }
+    UpdateSmoothThreadHistory(ThreadType::kCompositor, /*modifier=*/-1);
   } else if (tracker->metrics()->GetEffectiveThread() == ThreadType::kRaster) {
     DCHECK_GT(raster_thread_driving_smoothness_, 0u);
-    --raster_thread_driving_smoothness_;
-    if (compositor_frame_reporting_controller_ &&
-        raster_thread_driving_smoothness_ == 0) {
-      compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
-          ThreadType::kRaster, false);
-    }
+    UpdateSmoothThreadHistory(ThreadType::kRaster, /*modifier=*/-1);
   } else {
     DCHECK_GT(main_thread_driving_smoothness_, 0u);
-    --main_thread_driving_smoothness_;
-    if (compositor_frame_reporting_controller_ &&
-        main_thread_driving_smoothness_ == 0) {
-      compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
-          ThreadType::kMain, false);
-    }
+    UpdateSmoothThreadHistory(ThreadType::kMain, /*modifier=*/-1);
   }
 
   frame_trackers_.erase(key);
diff --git a/cc/metrics/frame_sequence_tracker_collection.h b/cc/metrics/frame_sequence_tracker_collection.h
index ca86b4b..29bf960 100644
--- a/cc/metrics/frame_sequence_tracker_collection.h
+++ b/cc/metrics/frame_sequence_tracker_collection.h
@@ -5,6 +5,7 @@
 #ifndef CC_METRICS_FRAME_SEQUENCE_TRACKER_COLLECTION_H_
 #define CC_METRICS_FRAME_SEQUENCE_TRACKER_COLLECTION_H_
 
+#include <map>
 #include <memory>
 #include <utility>
 #include <vector>
@@ -13,7 +14,10 @@
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "cc/cc_export.h"
+#include "cc/metrics/dropped_frame_counter.h"
+#include "cc/metrics/frame_info.h"
 #include "cc/metrics/frame_sequence_metrics.h"
+#include "cc/metrics/frame_sorter.h"
 #include "cc/metrics/ukm_dropped_frames_data.h"
 
 namespace viz {
@@ -22,7 +26,6 @@
 
 namespace cc {
 class FrameSequenceTracker;
-class CompositorFrameReportingController;
 
 // Map of kCustom tracker results keyed by a sequence id.
 using CustomTrackerResults =
@@ -32,12 +35,11 @@
 
 // Used for notifying attached FrameSequenceTracker's of begin-frames and
 // submitted frames.
-class CC_EXPORT FrameSequenceTrackerCollection {
+class CC_EXPORT FrameSequenceTrackerCollection : public FrameSorterObserver {
  public:
-  FrameSequenceTrackerCollection(
-      bool is_single_threaded,
-      CompositorFrameReportingController* frame_reporting_controller);
-  ~FrameSequenceTrackerCollection();
+  FrameSequenceTrackerCollection(bool is_single_threaded,
+                                 DroppedFrameCounter* dropped_frame_counter);
+  ~FrameSequenceTrackerCollection() override;
 
   FrameSequenceTrackerCollection(const FrameSequenceTrackerCollection&) =
       delete;
@@ -96,19 +98,34 @@
   }
 
   void AddSortedFrame(const viz::BeginFrameArgs& args,
-                      const FrameInfo& frame_info);
+                      const FrameInfo& frame_info) override;
 
   // Registers the shared memory location for PDF4 UKMs.
   void SetUkmDroppedFramesDestination(
       UkmDroppedFramesDataShared* dropped_frames_data);
 
+  ActiveTrackers GetActiveTrackers() const;
+  FrameInfo::SmoothThread GetSmoothThreadAtTime(
+      base::TimeTicks timestamp) const;
+  FrameInfo::SmoothEffectDrivingThread GetScrollThreadAtTime(
+      base::TimeTicks timestamp) const;
+  FrameInfo::SmoothEffectDrivingThread GetScrollingThread() const;
+  FrameInfo::SmoothThread GetSmoothThread() const;
+
+  void UpdateSmoothThreadHistory(
+      FrameInfo::SmoothEffectDrivingThread thread_type,
+      int modifier);
+
  private:
+  friend class CompositorFrameReportingControllerTest;
   friend class FrameSequenceTrackerTest;
 
   FrameSequenceTracker* StartSequenceInternal(
       FrameSequenceTrackerType type,
       FrameInfo::SmoothEffectDrivingThread scrolling_thread);
 
+  void SetScrollingThread(FrameInfo::SmoothEffectDrivingThread thread);
+
   void RecreateTrackers(const viz::BeginFrameArgs& args);
   // Destroy the trackers that are ready to be terminated.
   void DestroyTrackers();
@@ -140,8 +157,10 @@
   NotifyCustomerTrackerResutlsCallback custom_tracker_results_added_callback_;
 
   std::vector<std::unique_ptr<FrameSequenceTracker>> removal_trackers_;
-  const raw_ptr<CompositorFrameReportingController>
-      compositor_frame_reporting_controller_;
+  const raw_ptr<DroppedFrameCounter> dropped_frame_counter_ = nullptr;
+  ActiveTrackers active_trackers_;
+  FrameInfo::SmoothEffectDrivingThread scrolling_thread_ =
+      FrameInfo::SmoothEffectDrivingThread::kUnknown;
 
   base::flat_map<
       std::pair<FrameSequenceTrackerType, FrameInfo::SmoothEffectDrivingThread>,
@@ -153,6 +172,14 @@
   size_t compositor_thread_driving_smoothness_ = 0;
   size_t raster_thread_driving_smoothness_ = 0;
 
+  // Sorted history of smooththread. Element i indicating the smooththread
+  // from timestamp of element i-1 until timestamp of element i.
+  std::map<base::TimeTicks, FrameInfo::SmoothThread> smooth_thread_history_;
+  // Sorted history of scrollthread. Element i indicating the smooththread
+  // from timestamp of element i-1 until timestamp of element i.
+  std::map<base::TimeTicks, FrameInfo::SmoothEffectDrivingThread>
+      scroll_thread_history_;
+
   // Pointer to shared memory map for PDF4 UKMs
   raw_ptr<UkmDroppedFramesDataShared> ukm_dropped_frames_data_ = nullptr;
 };
diff --git a/cc/metrics/frame_sequence_tracker_unittest.cc b/cc/metrics/frame_sequence_tracker_unittest.cc
index ea999a7..ba558f3 100644
--- a/cc/metrics/frame_sequence_tracker_unittest.cc
+++ b/cc/metrics/frame_sequence_tracker_unittest.cc
@@ -51,22 +51,23 @@
   MOCK_METHOD2(OnEndFrame, void(const viz::BeginFrameArgs&, const FrameInfo&));
 };
 
-class FrameSequenceTrackerTest : public testing::Test {
+class FrameSequenceTrackerTest : public testing::Test, FrameSorterObserver {
  public:
   const uint32_t kImplDamage = 0x1;
   const uint32_t kMainDamage = 0x2;
 
   FrameSequenceTrackerTest()
       : dfc_mock_(DroppedFrameCounterMock()),
+        collection_(/*is_single_threaded=*/false, &dfc_mock_),
         compositor_frame_reporting_controller_(
             std::make_unique<CompositorFrameReportingController>(
                 /*should_report_histograms=*/true,
                 /*should_report_ukm=*/false,
-                /*layer_tree_host_id=*/1)),
-        collection_(/*is_single_threaded=*/false,
-                    compositor_frame_reporting_controller_.get()),
-        sorter_(base::BindRepeating(&FrameSequenceTrackerTest::OnFrameResult,
-                                    base::Unretained(this))) {
+                /*layer_tree_host_id=*/1)) {
+    dfc_mock_.set_total_counter(&total_frame_counter_);
+    compositor_frame_reporting_controller_->SetFrameSorter(&sorter_);
+    compositor_frame_reporting_controller_->SetDroppedFrameCounter(&dfc_mock_);
+    sorter_.AddObserver(this);
     tracker_ = collection_.StartScrollSequence(
         FrameSequenceTrackerType::kTouchScroll,
         FrameInfo::SmoothEffectDrivingThread::kCompositor);
@@ -335,19 +336,24 @@
     return tracker_->termination_status_;
   }
 
-  // FrameSorter callback.
-  void OnFrameResult(const viz::BeginFrameArgs& args,
-                     const FrameInfo& frame_info) {
+  // FrameSorter observer function.
+  void AddSortedFrame(const viz::BeginFrameArgs& args,
+                      const FrameInfo& frame_info) override {
     collection_.AddSortedFrame(args, frame_info);
   }
 
  protected:
+  TotalFrameCounter total_frame_counter_;
   DroppedFrameCounterMock dfc_mock_;
+  FrameSequenceTrackerCollection collection_;
+  FrameSorter sorter_;
+  // Since CFRC destructor cleans up the FrameSorter's
+  // registered observers (in this case, DFC and FSTC)
+  // it needs to be declared last so that it will be
+  // cleaned up first.
   std::unique_ptr<CompositorFrameReportingController>
       compositor_frame_reporting_controller_;
-  FrameSequenceTrackerCollection collection_;
   raw_ptr<FrameSequenceTracker, DanglingUntriaged> tracker_;
-  FrameSorter sorter_;
 };
 
 // Tests that the tracker works correctly when the source-id for the
@@ -895,6 +901,7 @@
   uint64_t sequence = 0;
   const uint64_t kNumFramesSkipped = 5;
 
+  dfc_mock_.OnFirstContentfulPaintReceived();
   // Expect that kNumFramesSkipped are backfilled with the appropriate smooth
   // thread set.
   EXPECT_CALL(dfc_mock_, OnEndFrame(testing::_, testing::_))
@@ -906,7 +913,6 @@
                   FrameInfo::SmoothEffectDrivingThread::kCompositor);
       });
 
-  compositor_frame_reporting_controller_->SetDroppedFrameCounter(&dfc_mock_);
   compositor_frame_reporting_controller_->SetFrameSequenceTrackerCollection(
       &collection_);
   auto frame0_args = CreateBeginFrameArgs(source, ++sequence);
diff --git a/cc/metrics/frame_sorter.cc b/cc/metrics/frame_sorter.cc
index faae5ed..beb4a5a 100644
--- a/cc/metrics/frame_sorter.cc
+++ b/cc/metrics/frame_sorter.cc
@@ -4,6 +4,7 @@
 
 #include "cc/metrics/frame_sorter.h"
 
+#include <cstddef>
 #include <utility>
 
 #include "cc/metrics/frame_info.h"
@@ -29,11 +30,18 @@
   return (on_begin_counter == ack_counter);
 }
 
-FrameSorter::FrameSorter(InOrderBeginFramesCallback callback)
-    : flush_callback_(std::move(callback)) {
-  DCHECK(!flush_callback_.is_null());
+FrameSorter::FrameSorter() = default;
+FrameSorter::~FrameSorter() {
+  observers_.Clear();
 }
-FrameSorter::~FrameSorter() = default;
+
+void FrameSorter::AddObserver(FrameSorterObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void FrameSorter::RemoveObserver(FrameSorterObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
 
 void FrameSorter::AddNewFrame(const viz::BeginFrameArgs& args) {
   if (current_source_id_.has_value() &&
@@ -132,7 +140,9 @@
     const auto& frame_id = pending_frame.frame_id;
     auto& frame_state = frame_states_[frame_id];
     if (frame_state.IsComplete() && !frame_state.should_ignore()) {
-      flush_callback_.Run(pending_frame, frame_infos_[frame_id]);
+      for (auto& observer : observers_) {
+        observer.AddSortedFrame(pending_frame, frame_infos_[frame_id]);
+      }
       frame_states_.erase(frame_id);
       frame_infos_.erase(frame_id);
       continue;
@@ -152,7 +162,9 @@
     if (!frame_state.IsComplete())
       break;
     ++flushed_count;
-    flush_callback_.Run(first, frame_infos_[frame_id]);
+    for (auto& observer : observers_) {
+      observer.AddSortedFrame(first, frame_infos_[frame_id]);
+    }
     frame_states_.erase(frame_id);
     frame_infos_.erase(frame_id);
     pending_frames_.pop_front();
diff --git a/cc/metrics/frame_sorter.h b/cc/metrics/frame_sorter.h
index ced0946..bff6415 100644
--- a/cc/metrics/frame_sorter.h
+++ b/cc/metrics/frame_sorter.h
@@ -9,9 +9,13 @@
 
 #include <map>
 #include <optional>
+#include <vector>
 
 #include "base/containers/circular_deque.h"
+#include "base/containers/flat_set.h"
 #include "base/functional/callback.h"
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
 #include "cc/cc_export.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 
@@ -19,9 +23,16 @@
 
 struct FrameInfo;
 
+// FrameSorterObserver class notifies registered
+// observers when frames are flushed by the FrameSorter.
+class FrameSorterObserver : public base::CheckedObserver {
+ public:
+  virtual void AddSortedFrame(const viz::BeginFrameArgs&, const FrameInfo&) = 0;
+};
+
 // This class is used to process the frames in order of initiation.
-// So regardless of which order frames are terminated, the  callback function
-// will frames sorter will br called on the frames in the order of initiation
+// So regardless of which order frames are terminated, the frames
+// sorter will be called on the frames in the order of initiation
 // (e.g. frame_time of that frame).
 class CC_EXPORT FrameSorter {
  public:
@@ -41,10 +52,9 @@
     bool should_ignore_ = false;    // Flags if there was a reset prior to acks.
   };
 
-  using InOrderBeginFramesCallback =
-      base::RepeatingCallback<void(const viz::BeginFrameArgs&,
-                                   const FrameInfo&)>;
-  explicit FrameSorter(InOrderBeginFramesCallback callback);
+  FrameSorter();
+  void AddObserver(FrameSorterObserver* frame_sorter_observer);
+  void RemoveObserver(FrameSorterObserver* frame_sorter_observer);
   ~FrameSorter();
 
   FrameSorter(const FrameSorter&) = delete;
@@ -69,7 +79,7 @@
   const uint64_t kPendingFramesMaxSize = 300u;
 
   // The callback to run for each flushed frame.
-  const InOrderBeginFramesCallback flush_callback_;
+  base::ObserverList<FrameSorterObserver> observers_;
 
   // Frames which are started.
   base::circular_deque<viz::BeginFrameArgs> pending_frames_;
diff --git a/cc/metrics/frame_sorter_unittest.cc b/cc/metrics/frame_sorter_unittest.cc
index aaeaddde..6fba78f 100644
--- a/cc/metrics/frame_sorter_unittest.cc
+++ b/cc/metrics/frame_sorter_unittest.cc
@@ -17,11 +17,10 @@
 namespace cc {
 
 // Test class for FrameSorter
-class FrameSorterTest : public testing::Test {
+class FrameSorterTest : public testing::Test, FrameSorterObserver {
  public:
-  FrameSorterTest()
-      : frame_sorter_(base::BindRepeating(&FrameSorterTest::FlushFrame,
-                                          base::Unretained(this))) {
+  FrameSorterTest() {
+    frame_sorter_.AddObserver(this);
     IncreaseSourceId();
   }
   ~FrameSorterTest() override = default;
@@ -121,7 +120,8 @@
   }
 
  private:
-  void FlushFrame(const viz::BeginFrameArgs& args, const FrameInfo& frame) {
+  void AddSortedFrame(const viz::BeginFrameArgs& args,
+                      const FrameInfo& frame) override {
     sorted_frames_.emplace_back(args, frame.IsDroppedAffectingSmoothness());
   }
 
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index 44035e4..8b5cb9a 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -31,7 +31,9 @@
 #include "base/trace_event/trace_event.h"
 #include "cc/base/features.h"
 #include "cc/metrics/begin_main_frame_metrics.h"
+#include "cc/metrics/dropped_frame_counter.h"
 #include "cc/metrics/event_metrics.h"
+#include "cc/metrics/frame_sequence_tracker_collection.h"
 #include "cc/test/fake_compositor_frame_reporting_controller.h"
 #include "cc/test/scheduler_test_common.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
@@ -374,7 +376,8 @@
  public:
   SchedulerTest()
       : task_runner_(base::MakeRefCounted<SchedulerTestTaskRunner>()),
-        fake_external_begin_frame_source_(nullptr) {}
+        fake_external_begin_frame_source_(nullptr),
+        tracker_collection_(false, &dropped_counter) {}
 
   ~SchedulerTest() override { client_->set_scheduler(nullptr); }
 
@@ -411,6 +414,9 @@
     fake_compositor_timing_history_ = fake_compositor_timing_history.get();
     reporting_controller =
         std::make_unique<FakeCompositorFrameReportingController>();
+    reporting_controller->SetFrameSorter(&frame_sorter);
+    reporting_controller->SetFrameSequenceTrackerCollection(
+        &tracker_collection_);
     reporting_controller->SetDroppedFrameCounter(&dropped_counter);
 
     scheduler_ = std::make_unique<TestScheduler>(
@@ -614,6 +620,12 @@
   std::unique_ptr<TestScheduler> scheduler_;
   raw_ptr<FakeCompositorTimingHistory> fake_compositor_timing_history_;
   DroppedFrameCounter dropped_counter;
+  FrameSequenceTrackerCollection tracker_collection_;
+  FrameSorter frame_sorter;
+  // Since CFRC destructor cleans up the FrameSorter's
+  // registered observers (in this case, DFC and FSTC)
+  // it needs to be declared last so that it will be
+  // cleaned up first.
   std::unique_ptr<CompositorFrameReportingController> reporting_controller;
 };
 
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 10d17649..435c8b1 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -480,7 +480,7 @@
               /*should_report_ukm=*/!settings.single_thread_proxy_scheduler,
               id)),
       frame_trackers_(settings.single_thread_proxy_scheduler,
-                      compositor_frame_reporting_controller_.get()),
+                      &dropped_frame_counter_),
       lcd_text_metrics_reporter_(LCDTextMetricsReporter::CreateIfNeeded(this)),
       frame_rate_estimator_(GetTaskRunner()),
       contains_srgb_cache_(kContainsSrgbCacheSize),
@@ -524,6 +524,7 @@
   }
 
   SetDebugState(settings.initial_debug_state);
+  compositor_frame_reporting_controller_->SetFrameSorter(&frame_sorter_);
   compositor_frame_reporting_controller_->SetDroppedFrameCounter(
       &dropped_frame_counter_);
   compositor_frame_reporting_controller_->SetFrameSequenceTrackerCollection(
@@ -535,8 +536,8 @@
 
 #if BUILDFLAG(IS_CHROMEOS)
     dropped_frame_counter_.EnableReportForUI();
-    compositor_frame_reporting_controller_->SetThreadAffectsSmoothness(
-        FrameInfo::SmoothEffectDrivingThread::kMain, true);
+    frame_trackers_.StartSequence(
+        FrameSequenceTrackerType::kCompositorAnimation);
 #endif  // BUILDFLAG(IS_CHROMEOS)
   }
 
@@ -583,19 +584,17 @@
   mutator_host_->ClearMutators();
   mutator_host_->SetMutatorHostClient(nullptr);
 
-  // `frame_trackers_` holds a pointer to
-  // `compositor_frame_reporting_controller_`. Setting
-  // `compositor_frame_reporting_controller_` to nullptr here leads to
-  // `frame_trackers_` holding a dangling ptr. Don't set to null here and let
-  // members be destroyed in reverse order of declaration.
-  // Since `frame_trackers_` is destroyed first, we need to clear the ptr that
-  // `compositor_frame_reporting_controller_` holds.
-  compositor_frame_reporting_controller_->SetFrameSequenceTrackerCollection(
-      nullptr);
-  // Similar to the logic above. The `compositor_frame_reporting_controller_`
+  // The `compositor_frame_reporting_controller_`
   // was given a `this` pointer for the event_latency_tracker and thus needs
-  // to be nulled to prevent it dangling.
+  // to be nulled to prevent it dangling. Members will be destroyed in reverse
+  // order of their declaration, so since event latency tracker is destroyed
+  // first, we need to clar the pointer that
+  // `compositor_frame_reporting_controller_` holds.
   compositor_frame_reporting_controller_->set_event_latency_tracker(nullptr);
+  // CFRC needs to unregister the frame trackers from the frame_sorter observer
+  // set before being cleaned up.
+  compositor_frame_reporting_controller_->ClearFrameSequenceTrackerCollection();
+  compositor_frame_reporting_controller_->ClearDroppedFrameCounter();
 }
 
 InputHandler& LayerTreeHostImpl::GetInputHandler() {
@@ -3771,7 +3770,7 @@
   has_valid_layer_tree_frame_sink_ = false;
   client_->DidLoseLayerTreeFrameSinkOnImplThread();
   lag_tracking_manager_.Clear();
-
+  frame_sorter_.Reset();
   dropped_frame_counter_.ResetPendingFrames(base::TimeTicks::Now());
 }
 
@@ -4079,6 +4078,7 @@
   } else {
     auto now = base::TimeTicks::Now();
     total_frame_counter_.OnHide(now);
+    frame_sorter_.Reset();
     dropped_frame_counter_.ResetPendingFrames(now);
 
     // When page is invisible, throw away corresponding EventsMetrics since
@@ -5928,6 +5928,7 @@
   // The source id has already been associated to the URL.
   compositor_frame_reporting_controller_->SetSourceId(source_id);
   total_frame_counter_.Reset();
+  frame_sorter_.Reset();
   dropped_frame_counter_.Reset();
   is_measuring_smoothness_ = false;
 }
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index acf640c..4a95d137 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -868,6 +868,7 @@
   DroppedFrameCounter* dropped_frame_counter_for_testing() {
     return &dropped_frame_counter_;
   }
+  FrameSorter* frame_sorter_for_testing() { return &frame_sorter_; }
 
   // Returns true if the client is currently compositing synchronously.
   bool IsInSynchronousComposite() const;
@@ -1258,7 +1259,8 @@
   PresentationTimeCallbackBuffer presentation_time_callbacks_;
 
   // `compositor_frame_reporting_controller_` has a dependency on
-  // `dropped_frame_counter_` so it must be declared last and deleted first;
+  // `dropped_frame_counter_` so it must be declared last and deleted first.
+  FrameSorter frame_sorter_;
   std::unique_ptr<CompositorFrameReportingController>
       compositor_frame_reporting_controller_;
   FrameSequenceTrackerCollection frame_trackers_;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index de6c0adb..32eac727 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -14464,6 +14464,7 @@
       host_impl_->total_frame_counter_for_testing();
   DroppedFrameCounter* dropped_frame_counter =
       host_impl_->dropped_frame_counter_for_testing();
+  FrameSorter* frame_sorter = host_impl_->frame_sorter_for_testing();
   EXPECT_EQ(total_frame_counter->total_frames(), 0u);
   EXPECT_EQ(dropped_frame_counter->total_frames(), 0u);
   total_frame_counter->set_total_frames_for_testing(1u);
@@ -14478,7 +14479,9 @@
       BEGINFRAME_FROM_HERE, 1u /*source_id*/, 2u /*sequence_number*/, now,
       deadline, interval, viz::BeginFrameArgs::NORMAL);
 
-  dropped_frame_counter->OnEndFrame(
+  frame_sorter->AddNewFrame(args);
+  // Delegates to DFC::AddSortedFrame, which calls DFC::OnEndFrame.
+  frame_sorter->AddFrameResult(
       args, CreateFakeFrameInfo(FrameInfo::FrameFinalState::kDropped));
   // FCP not received, so the total_smoothness_dropped_ won't increase.
   EXPECT_EQ(dropped_frame_counter->total_smoothness_dropped(), 0u);
@@ -14489,7 +14492,9 @@
                             &begin_frame_metrics, /*commit_timeout=*/false);
   dropped_frame_counter->SetTimeFirstContentfulPaintReceivedForTesting(
       args.frame_time);
-  dropped_frame_counter->OnEndFrame(
+  frame_sorter->AddNewFrame(args);
+  // Delegates to DFC::AddSortedFrame, which calls DFC::OnEndFrame.
+  frame_sorter->AddFrameResult(
       args, CreateFakeFrameInfo(FrameInfo::FrameFinalState::kDropped));
   EXPECT_EQ(dropped_frame_counter->total_smoothness_dropped(), 1u);
 
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 2b14a8c..5a0528ab 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -9732,6 +9732,7 @@
       host_impl->dropped_frame_counter()->OnFirstContentfulPaintReceived();
       fcp_sent_ = true;
     }
+    host_impl->frame_sorter_for_testing()->AddNewFrame(last_args_);
   }
 
   void DidFinishImplFrameOnThread(LayerTreeHostImpl* host_impl) override {
@@ -9744,7 +9745,8 @@
     }
 
     // Mark every frame as a dropped frame affecting smoothness.
-    host_impl->dropped_frame_counter()->OnEndFrame(
+    // Delegates to DFC::AddSortedFrame, which calls DFC::OnEndFrame.
+    host_impl->frame_sorter_for_testing()->AddFrameResult(
         last_args_, CreateFakeImplDroppedFrameInfo());
     host_impl->SetNeedsRedraw();
     --frames_counter_;
@@ -9801,7 +9803,8 @@
     // Mark every frame as a dropped frame affecting smoothness. This happens
     // entirely on the compositor thread, so mark it as not including
     // main-thread update.
-    host_impl->dropped_frame_counter()->OnEndFrame(
+    // Delegates to DFC::AddSortedFrame, which calls DFC::OnEndFrame.
+    host_impl->frame_sorter_for_testing()->AddFrameResult(
         last_args_, CreateFakeImplDroppedFrameInfo());
     host_impl->SetNeedsRedraw();
     --frames_counter_;
diff --git a/chrome/VERSION b/chrome/VERSION
index 7e2c030..a063e48 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=138
 MINOR=0
-BUILD=7167
+BUILD=7168
 PATCH=0
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 5660f0e2..5536e34 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1007,9 +1007,12 @@
   "java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesViewBinder.java",
   "java/src/org/chromium/chrome/browser/suggestions/tile/SuggestionsTileView.java",
   "java/src/org/chromium/chrome/browser/suggestions/tile/Tile.java",
+  "java/src/org/chromium/chrome/browser/suggestions/tile/TileDragDelegateImpl.java",
+  "java/src/org/chromium/chrome/browser/suggestions/tile/TileDragSession.java",
   "java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java",
   "java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java",
   "java/src/org/chromium/chrome/browser/suggestions/tile/TileInteractionDelegateImpl.java",
+  "java/src/org/chromium/chrome/browser/suggestions/tile/TileMovement.java",
   "java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java",
   "java/src/org/chromium/chrome/browser/suggestions/tile/TileUtils.java",
   "java/src/org/chromium/chrome/browser/suggestions/tile/TilesLinearLayout.java",
diff --git a/chrome/android/expectations/lint-baseline.xml b/chrome/android/expectations/lint-baseline.xml
index 72a6151..6c913179 100644
--- a/chrome/android/expectations/lint-baseline.xml
+++ b/chrome/android/expectations/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.11.0-alpha07" type="baseline" client="" dependencies="true" name="" variant="all" version="8.11.0-alpha07">
+<issues format="6" by="lint 8.11.0-alpha09" type="baseline" client="" dependencies="true" name="" variant="all" version="8.11.0-alpha09">
 
     <issue
         id="WrongCommentType"
@@ -63,7 +63,7 @@
         errorLine2="       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManager.java"
-            line="278"
+            line="334"
             column="8"/>
     </issue>
 
@@ -74,7 +74,7 @@
         errorLine2="       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java"
-            line="387"
+            line="393"
             column="8"/>
     </issue>
 
@@ -118,7 +118,7 @@
         errorLine2="       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java"
-            line="348"
+            line="338"
             column="8"/>
     </issue>
 
@@ -228,7 +228,7 @@
         errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java"
-            line="1327"
+            line="1344"
             column="43"/>
     </issue>
 
@@ -1130,7 +1130,7 @@
         errorLine2="                                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java"
-            line="291"
+            line="300"
             column="53"/>
     </issue>
 
@@ -1147,17 +1147,6 @@
 
     <issue
         id="WrongConstant"
-        message="Must be one of: DialogDismissalCause.UNKNOWN, DialogDismissalCause.POSITIVE_BUTTON_CLICKED, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED, DialogDismissalCause.ACTION_ON_CONTENT, DialogDismissalCause.DISMISSED_BY_NATIVE, DialogDismissalCause.NAVIGATE_BACK, DialogDismissalCause.TOUCH_OUTSIDE, DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE, DialogDismissalCause.TAB_SWITCHED, DialogDismissalCause.TAB_DESTROYED, DialogDismissalCause.ACTIVITY_DESTROYED, DialogDismissalCause.NOT_ATTACHED_TO_WINDOW, DialogDismissalCause.NAVIGATE, DialogDismissalCause.WEB_CONTENTS_DESTROYED, DialogDismissalCause.DIALOG_INTERACTION_DEFERRED, DialogDismissalCause.ACTION_ON_DIALOG_COMPLETED, DialogDismissalCause.ACTION_ON_DIALOG_NOT_POSSIBLE, DialogDismissalCause.CLIENT_TIMEOUT, but could be ButtonType.POSITIVE, ButtonType.NEGATIVE, ButtonType.TITLE_ICON, ButtonType.POSITIVE_EPHEMERAL"
-        errorLine1="                        mManager.dismissDialog(model, buttonType);"
-        errorLine2="                                                      ~~~~~~~~~~">
-        <location
-            file="../../components/browser_ui/contacts_picker/android/java/src/org/chromium/components/browser_ui/contacts_picker/ContactView.java"
-            line="121"
-            column="55"/>
-    </issue>
-
-    <issue
-        id="WrongConstant"
         message="Must be one of: SigninAccessPoint.RECENT_TABS, SigninAccessPoint.BOOKMARK_MANAGER, SigninAccessPoint.HISTORY_PAGE, SigninAccessPoint.NTP_FEED_TOP_PROMO, SigninAccessPoint.NTP_FEED_BOTTOM_PROMO, SigninAccessPoint.SAFETY_CHECK, SigninAccessPoint.SETTINGS, SigninAccessPoint.WEB_SIGNIN, SigninAccessPoint.NTP_SIGNED_OUT_ICON, SigninAccessPoint.NTP_FEED_CARD_MENU_PROMO, SigninAccessPoint.SEND_TAB_TO_SELF_PROMO, SigninAccessPoint.CCT_ACCOUNT_MISMATCH_NOTIFICATION, SigninAccessPoint.COLLABORATION_JOIN_TAB_GROUP, SigninAccessPoint.COLLABORATION_SHARE_TAB_GROUP, SigninAccessPoint.COLLABORATION_LEAVE_OR_DELETE_TAB_GROUP, SigninAccessPoint.HISTORY_SYNC_EDUCATIONAL_TIP"
         errorLine1="                                mActivity, mProfile, config, signinAccessPoint);"
         errorLine2="                                                             ~~~~~~~~~~~~~~~~~">
@@ -1449,7 +1438,7 @@
         errorLine2="                        ^">
         <location
             file="../../chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java"
-            line="410"
+            line="417"
             column="25"/>
     </issue>
 
@@ -1542,6 +1531,314 @@
     </issue>
 
     <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0, 1), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;কাইলৈ ম্যাদ উকলিব&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-as/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0, 1), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;আগামীকাল মেয়াদ শেষ হবে&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-bn/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0, 1), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;فردا منقضی می‌شود&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-fa/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0, 1), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Expire demain&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-fr-rCA/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0, 1), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Expire demain&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-fr/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0, 1), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;આવતીકાલે સમયસીમા સમાપ્ત થાય છે&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-gu/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0, 1), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Գործողության ժամկետը սպառվում է վաղը&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-hy/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0, 1), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;ਮਿਆਦ ਕੱਲ੍ਹ ਨੂੰ ਸਮਾਪਤ ਹੋ ਜਾਵੇਗੀ&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-pa/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0, 1), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;හෙට කල් ඉකුත් වෙයි&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-si/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0, 1), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Kuphelelwa yisikhathi kusasa&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-zu/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0~3, 5, 7, 8, 10~13, 15, 17, 18, 20, 21, 100, 1000, 10000, 100000, 1000000, …), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Mag-e-expire bukas&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-tl/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (1, 101, 201, 301, 401, 501, 601, 701, 1001, …), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Poteče jutri&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-sl/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Тэрмін дзеяння заканчваецца заўтра&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-be/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Ističe sutra&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-bs/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Rennur út á morgun&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-is/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Истекува утре&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-mk/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Срок действия истекает завтра&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-ru/components_strings.xml"
+            line="357"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0, 1), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;ነገ ጊዜው ያበቃል&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-am/components_strings.xml"
+            line="358"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0, 1), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;समयसीमा कल खत्म हो जाएगी&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-hi/components_strings.xml"
+            line="358"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0, 1), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;ನಾಳೆ ಅವಧಿ ಮುಕ್ತಾಯವಾಗುತ್ತದೆ&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-kn/components_strings.xml"
+            line="358"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0, 1), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Expira amanhã&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-pt-rBR/components_strings.xml"
+            line="358"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (0, 1), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Expira amanhã&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-pt-rPT/components_strings.xml"
+            line="358"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Ističe sutra&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-b+sr+Latn/components_strings.xml"
+            line="358"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Istječe sutra&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-hr/components_strings.xml"
+            line="358"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Galiojimo laikas baigsis rytoj&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-lt/components_strings.xml"
+            line="358"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Derīguma termiņš beigsies rīt&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-lv/components_strings.xml"
+            line="358"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Истиче сутра&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-sr/components_strings.xml"
+            line="358"
+            column="3"/>
+    </issue>
+
+    <issue
+        id="ImpliedQuantity"
+        message="The quantity `&apos;one&apos;` matches more than one specific number in this locale (1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …), but the message did not include a formatting argument (such as `%d`). This is usually an internationalization error. See full issue explanation for more."
+        errorLine1="  &lt;item quantity=&quot;one&quot;>&quot;Строк дії закінчується завтра&quot;&lt;/item>"
+        errorLine2="  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="gen/chrome/android/monochrome_public_bundle__lint/RESZIPS/obj/components/strings/components_strings.resources.zip/values-uk/components_strings.xml"
+            line="358"
+            column="3"/>
+    </issue>
+
+    <issue
         id="DataExtractionRules"
         message="The attribute `android:allowBackup` is deprecated from Android 12 and higher and may be removed in future versions. Consider adding the attribute `android:dataExtractionRules` specifying an `@xml` resource which configures cloud backups and device transfers on Android 12 and higher."
         errorLine1="  &lt;application android:name=&quot;org.chromium.chrome.browser.base.SplitMonochromeApplication&quot; android:icon=&quot;@drawable/ic_launcher&quot; android:roundIcon=&quot;@drawable/ic_launcher_round&quot; android:label=&quot;@string/app_name&quot; android:memtagMode=&quot;async&quot; android:largeHeap=&quot;false&quot; android:manageSpaceActivity=&quot;@string/manage_space_activity&quot; android:supportsRtl=&quot;true&quot; android:zygotePreloadName=&quot;org.chromium.content_public.app.ZygotePreload&quot; android:allowBackup=&quot;false&quot; android:networkSecurityConfig=&quot;@xml/network_security_config&quot; android:allowAudioPlaybackCapture=&quot;false&quot; android:appComponentFactory=&quot;org.chromium.chrome.browser.base.SplitCompatAppComponentFactory&quot; android:enableOnBackInvokedCallback=&quot;true&quot; android:multiArch=&quot;true&quot; android:extractNativeLibs=&quot;false&quot;>"
@@ -1889,7 +2186,7 @@
         errorLine2="                   ~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java"
-            line="688"
+            line="689"
             column="20"/>
     </issue>
 
@@ -1988,7 +2285,7 @@
         errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButton.java"
-            line="79"
+            line="85"
             column="9"/>
     </issue>
 
@@ -2065,7 +2362,7 @@
         errorLine2="                   ~~~~~~~~~~~~">
         <location
             file="../../chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java"
-            line="764"
+            line="787"
             column="20"/>
     </issue>
 
@@ -2076,7 +2373,7 @@
         errorLine2="                   ~~~~~~~~~~~~">
         <location
             file="../../chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java"
-            line="532"
+            line="542"
             column="20"/>
     </issue>
 
@@ -2120,7 +2417,7 @@
         errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java"
-            line="245"
+            line="261"
             column="13"/>
     </issue>
 
diff --git a/chrome/android/features/keyboard_accessory/internal/BUILD.gn b/chrome/android/features/keyboard_accessory/internal/BUILD.gn
index 4ad7f25..72a8762 100644
--- a/chrome/android/features/keyboard_accessory/internal/BUILD.gn
+++ b/chrome/android/features/keyboard_accessory/internal/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/config/android/rules.gni")
-import("//build/config/locales.gni")
 import("//chrome/common/features.gni")
 import("//third_party/jni_zero/jni_zero.gni")
 import("//tools/grit/grit_rule.gni")
@@ -168,7 +167,4 @@
 java_strings_grd("java_strings_grd") {
   defines = chrome_grit_defines
   grd_file = "java/strings/android_keyboard_accessory_strings.grd"
-  outputs = [ "values/android_keyboard_accessory_strings.xml" ] + process_file_template(
-                android_bundle_locales_as_resources,
-                [ "values-{{source_name_part}}/android_keyboard_accessory_strings.xml" ])
 }
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index faf9c9b5..f83a008 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/config/android/rules.gni")
-import("//build/config/locales.gni")
 import("//chrome/android/features/android_library_factory_tmpl.gni")
 import("//chrome/common/features.gni")
 import("//tools/grit/grit_rule.gni")
@@ -11,11 +10,6 @@
 java_strings_grd("java_strings_grd") {
   defines = chrome_grit_defines
   grd_file = "java/strings/android_chrome_tab_ui_strings.grd"
-  outputs =
-      [ "values/android_chrome_tab_ui_strings.xml" ] +
-      process_file_template(
-          android_bundle_locales_as_resources,
-          [ "values-{{source_name_part}}/android_chrome_tab_ui_strings.xml" ])
 }
 
 android_resources("java_resources") {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 9ee2359..d544d7d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -3538,6 +3538,7 @@
                 || type == TabLaunchType.FROM_EXTERNAL_APP
                 || type == TabLaunchType.FROM_READING_LIST
                 || type == TabLaunchType.FROM_LONGPRESS_FOREGROUND
+                || type == TabLaunchType.FROM_LONGPRESS_FOREGROUND_IN_GROUP
                 || type == TabLaunchType.FROM_LONGPRESS_INCOGNITO
                 || type == TabLaunchType.FROM_LONGPRESS_BACKGROUND
                 || type == TabLaunchType.FROM_LONGPRESS_BACKGROUND_IN_GROUP
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationChannelPreserver.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationChannelPreserver.java
index 54a4a3d..2cdf03e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationChannelPreserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationChannelPreserver.java
@@ -40,25 +40,29 @@
         }
 
         SiteChannelsManager siteChannelsManager = SiteChannelsManager.getInstance();
-        @NotificationChannelStatus int status = siteChannelsManager.getChannelStatus(channelId);
-        if (status == NotificationChannelStatus.UNAVAILABLE) {
-            // This shouldn't happen if we passed the above conditional return - but it just means
-            // that the channel doesn't exist, so again, we don't need to do anything.
-            return;
-        }
+        siteChannelsManager.getChannelStatusAsync(
+                channelId,
+                (status) -> {
+                    if (status == NotificationChannelStatus.UNAVAILABLE) {
+                        // This shouldn't happen if we passed the above conditional return - but it
+                        // just means
+                        // that the channel doesn't exist, so again, we don't need to do anything.
+                        return;
+                    }
 
-        assert status == NotificationChannelStatus.ENABLED
-                || status == NotificationChannelStatus.BLOCKED;
+                    assert status == NotificationChannelStatus.ENABLED
+                            || status == NotificationChannelStatus.BLOCKED;
 
-        @ContentSettingValues
-        int settingValue =
-                status == NotificationChannelStatus.ENABLED
-                        ? ContentSettingValues.ALLOW
-                        : ContentSettingValues.BLOCK;
-        WebappRegistry.getInstance()
-                .getPermissionStore()
-                .setPreInstallNotificationPermission(origin, settingValue);
-        siteChannelsManager.deleteSiteChannel(channelId);
+                    @ContentSettingValues
+                    int settingValue =
+                            status == NotificationChannelStatus.ENABLED
+                                    ? ContentSettingValues.ALLOW
+                                    : ContentSettingValues.BLOCK;
+                    WebappRegistry.getInstance()
+                            .getPermissionStore()
+                            .setPreInstallNotificationPermission(origin, settingValue);
+                    siteChannelsManager.deleteSiteChannel(channelId);
+                });
     }
 
     /** Restores the SiteChannel if called on a version of Android that requires it. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java
index 3e97eb2..72911306 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java
@@ -344,15 +344,20 @@
     }
 
     /**
-     * Returns true if animations are running (excluding {@link #mFadeAnimator}).
+     * Returns true if the foreground animation is running (excluding {@link #mFadeAnimator}).
      *
      * <p>Including {@link #mFadeAnimator} would prevent {@link #doneHiding} from being called
      * during the animation cycle in {@link
      * org.chromium.chrome.browser.compositor.layouts.LayoutManagerImpl#onUpdate(long, long)}.
+     *
+     * <p>There is also a race condition in {@link #tabCreatedInBackground} where {@link
+     * org.chromium.chrome.browser.compositor.layouts.LayoutManagerImpl#onUpdate(long, long)} gets
+     * called when the animation already started, causing the layout to freeze. Hence, we skip this
+     * check for the background animation.
      */
     @Override
     public boolean isRunningAnimations() {
-        return mTabCreatedForegroundAnimation != null || mTabCreatedBackgroundAnimation != null;
+        return mTabCreatedForegroundAnimation != null;
     }
 
     private void reset() {
@@ -632,14 +637,8 @@
             @Px int y,
             ObservableSupplier<Boolean> visibilitySupplier) {
         boolean isIncognito = animationTab.isIncognitoBranded();
-
-        // TODO(crbug.com/40282469): Investigate why NTP presents lower quality during the
-        // animation and how to stop forcing browser controls in the NTP.
         assert mLayoutTabs.length == 1;
         forceNewTabAnimationToFinish();
-
-        // TODO(crbug.com/40282469): Fix bug where the animation has a weird state and does not call
-        // startHiding when opening multiple tabs from NTP MVT context menu.
         mSkipForceAnimationToFinish = true;
         startHiding();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaLauncherActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaLauncherActivity.java
index 4d7f7c3..f867c220 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaLauncherActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaLauncherActivity.java
@@ -66,8 +66,16 @@
     }
 
     private String getMIMEType(Uri uri) {
+        if (uri == null) {
+            return "";
+        }
+        String uriScheme = uri.getScheme();
+        if (uriScheme == null) {
+            return "";
+        }
+
         // With a content URI, we can just query the ContentResolver.
-        if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
+        if (uriScheme.equals(ContentResolver.SCHEME_CONTENT)) {
             return getContentResolver().getType(uri);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/ContextMenuManager.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/ContextMenuManager.java
index f99040e..ca7e47da 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/ContextMenuManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/ContextMenuManager.java
@@ -322,6 +322,13 @@
         return true;
     }
 
+    /** Dismisses the context menu shown by {@link showListContextMenu()}, if any. */
+    public void hideListContextMenu() {
+        if (mListContextMenu != null) {
+            mListContextMenu.dismiss();
+        }
+    }
+
     @Override
     public void onContextMenuClosed() {
         if (mAnchorView == null) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/CustomLinkOperations.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/CustomLinkOperations.java
index e6febee..c9c5e6dc8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/CustomLinkOperations.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/CustomLinkOperations.java
@@ -48,4 +48,14 @@
      * @return Whether a custom link identified by {@param keyUrl} exists.
      */
     boolean hasCustomLink(GURL keyUrl);
+
+    /**
+     * Moves a custom link identified by {@param keyUrl} to a new position, and shift all other
+     * custom links between the old position and the new towards the former.
+     *
+     * @param keyUrl The URL of the custom link to move.
+     * @param newPos The new position for the custom link to move to.
+     * @return Whether the operation successfully ran.
+     */
+    boolean reorderCustomLink(GURL keyUrl, int newPos);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java
index 81464f2..e0c94f7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/mostvisited/MostVisitedSitesBridge.java
@@ -67,6 +67,13 @@
         return MostVisitedSitesBridgeJni.get().hasCustomLink(mNativeMostVisitedSitesBridge, keyUrl);
     }
 
+    @Override
+    public boolean reorderCustomLink(GURL keyUrl, int newPos) {
+        if (mNativeMostVisitedSitesBridge == 0) return false;
+        return MostVisitedSitesBridgeJni.get()
+                .reorderCustomLink(mNativeMostVisitedSitesBridge, keyUrl, newPos);
+    }
+
     // MostVisitedSites implementation.
     /**
      * Cleans up the C++ side of this class. This instance must not be used after calling destroy().
@@ -201,6 +208,9 @@
 
         boolean hasCustomLink(long nativeMostVisitedSitesBridge, @JniType("GURL") GURL keyUrl);
 
+        boolean reorderCustomLink(
+                long nativeMostVisitedSitesBridge, @JniType("GURL") GURL keyUrl, int newPos);
+
         void destroy(long nativeMostVisitedSitesBridge, MostVisitedSitesBridge caller);
 
         void onHomepageStateChanged(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesLayout.java
index 392e282..37072ae 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesLayout.java
@@ -57,6 +57,10 @@
         return null;
     }
 
+    public SiteSuggestion getTileViewData(TileView tileView) {
+        return ((SuggestionsTileView) tileView).getData();
+    }
+
     /**
      * Adjusts the edge margin of the tile elements when they are displayed in the center of the NTP
      * on the tablet.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesMediator.java
index 22531ce..52bbb5c8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesMediator.java
@@ -107,6 +107,7 @@
                         suggestionsUiDelegate,
                         contextMenuManager,
                         tileGroupDelegate,
+                        new TileDragDelegateImpl(mMvTilesLayout),
                         /* observer= */ this,
                         offlinePageBridge);
         mTileGroup.startObserving(MAX_RESULTS);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SuggestionsTileView.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SuggestionsTileView.java
index 375f7fcb..8ca55fc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SuggestionsTileView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/SuggestionsTileView.java
@@ -27,6 +27,12 @@
         super(context, attrs);
     }
 
+    // TileView override.
+    @Override
+    public boolean isDraggable() {
+        return mData.source == TileSource.CUSTOM_LINKS;
+    }
+
     /**
      * Initializes the view using the data held by {@code tile}. This should be called immediately
      * after inflation.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileDragDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileDragDelegateImpl.java
new file mode 100644
index 0000000..926ee68
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileDragDelegateImpl.java
@@ -0,0 +1,209 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import android.content.res.Resources;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.IntDef;
+
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+import org.chromium.components.browser_ui.widget.tile.TileView;
+import org.chromium.ui.util.RunnableTimer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * UI logic for dragging a "from" tile to a "to" tile's location: Handles touch events, updates the
+ * the state machine. Reusable across drag sessions for a {@link MostVisitedTilesLayout} instance.
+ * Uses {@link TileDragSession} to store per-session states.
+ */
+@NullMarked
+class TileDragDelegateImpl implements TileGroup.TileDragDelegate, TileDragSession.Delegate {
+
+    // Tile drag dynamics are represented by a state machine. Here are its states.
+    @IntDef({
+        DragPhase.NONE,
+        DragPhase.PREPARE,
+        DragPhase.START,
+        DragPhase.DOMINATE,
+    })
+    public @interface DragPhase {
+        // NONE: No drag. ACTION_DOWN => :=PREPARE (i.e., enter the PREPARE phase).
+        int NONE = 0;
+
+        // PREPARE: No drag. Default touch handling triggers ACTION_UP => tile click; ACTION_MOVE
+        // (after small drag) => scroll. These default interactions trigger ACTION_CANCEL => :=NONE.
+        // If PREPARE persists longer than "start duration" then :=START. TileDragHandlerDelegate
+        // is passed here.
+        int PREPARE = 1;
+
+        // START: Tile drag is live: ACTION_MOVE => move "from" tile; ACTION_UP => cancel drag
+        // :=NONE. If drag displacement exceeds "dominate threshold" then :=DOMINATE, and call
+        // TileDragHandlerDelegate.onDragDominate().
+        int START = 2;
+
+        // DOMINATE: Tile drag is live: ACTION_MOVE => move "from" and background tiles; ACTION_UP
+        // => finalize, which *may* call TileDragHandlerDelegate.onDragAccept(), and then :=NONE.
+        int DOMINATE = 3;
+
+        int NUM_ENTRIES = 4;
+    }
+
+    // "Start duration": Delay (in ms) for triggering PREPARE -> START change.
+    private static final long START_DURATION_MS = 300;
+
+    // Relative "dominate threshold": Multiplied by tile width to get "dominate threshold".
+    private static final float DOMINATE_TRESHOLD_RATIO = 0.4f;
+
+    // Parent container for dragged tiles.
+    private final MostVisitedTilesLayout mMvTilesLayout;
+
+    private final float mTileWidthPx;
+
+    // Squared "dominate threshold": During drag, if the ACTION_MOVE position's (Euclidean) distance
+    // to the ACTION_DOWN distance exceeds "dominate threshold" then START -> DOMINATE change
+    // triggers.
+    private final float mDominateThresholdPxSquared;
+
+    // Timer for PREPARE -> START change.
+    private final RunnableTimer mTimer = new RunnableTimer();
+
+    // Current UI phase.
+    private @DragPhase int mPhase;
+
+    // Ephemeral drag states: Null in EMPTY; assigned in PREPARE; active in {START, DOMINATE}.
+    private @Nullable TileDragSession mTileDragSession;
+
+    // Runnable to cancel tile movement that might not have completed yet, due to animation.
+    private @Nullable Runnable mPendingChangeCanceller;
+
+    public TileDragDelegateImpl(MostVisitedTilesLayout mvTilesLayout) {
+        mMvTilesLayout = mvTilesLayout;
+        Resources res = mMvTilesLayout.getResources();
+        mTileWidthPx = res.getDimensionPixelSize(R.dimen.tile_view_width);
+        float mDominateThresholdPx = DOMINATE_TRESHOLD_RATIO * mTileWidthPx;
+        mDominateThresholdPxSquared = mDominateThresholdPx * mDominateThresholdPx;
+        mPhase = DragPhase.NONE;
+    }
+
+    // TileGroup.TileDragDelegate implementation.
+    @Override
+    public void onTileTouchDown(
+            View view, MotionEvent event, TileGroup.TileDragHandlerDelegate dragHandlerDelegate) {
+        assert event.getAction() == MotionEvent.ACTION_DOWN;
+        if (!((TileView) view).isDraggable()) {
+            return;
+        }
+
+        resetInternal(false);
+        mPhase = DragPhase.PREPARE;
+        mTileDragSession =
+                new TileDragSession(
+                        this, dragHandlerDelegate, (TileView) view, event.getX(), event.getY());
+
+        mTimer.startTimer(
+                START_DURATION_MS,
+                () -> {
+                    if (mTileDragSession == null) {
+                        assert mPhase == DragPhase.NONE;
+                        resetInternal(false);
+                    } else {
+                        cancelPendingChange();
+                        mPhase = DragPhase.START;
+                        // Needed to do this caller to consistently receive ACTION_MOVE.
+                        mMvTilesLayout.requestDisallowInterceptTouchEvent(true);
+                        mTileDragSession.start();
+                    }
+                });
+    }
+
+    @Override
+    public void onSessionTileTouch(View view, MotionEvent event) {
+        assert ((TileView) view).isDraggable() && mTileDragSession != null;
+
+        if (event.getAction() == MotionEvent.ACTION_MOVE) {
+            if (mPhase == DragPhase.START) {
+                float dragDisplacementSquared =
+                        mTileDragSession.getDragDisplacementSquared(event.getX(), event.getY());
+                if (dragDisplacementSquared >= mDominateThresholdPxSquared) {
+                    mTileDragSession.getTileDragHandlerDelegate().onDragDominate();
+                    mPhase = DragPhase.DOMINATE;
+                } else {
+                    mTileDragSession.updateFromView(event.getX());
+                }
+            }
+            if (mPhase == DragPhase.DOMINATE) {
+                mTileDragSession.updateFromView(event.getX());
+                mTileDragSession.updateToIndexAndAnimate();
+            }
+
+        } else if (event.getAction() == MotionEvent.ACTION_UP) {
+            resetInternal(mPhase == DragPhase.DOMINATE);
+            mPhase = DragPhase.NONE;
+
+        } else if (event.getAction() == MotionEvent.ACTION_CANCEL) {
+            resetInternal(false);
+            mPhase = DragPhase.NONE;
+        }
+    }
+
+    @Override
+    public boolean hasSession() {
+        return mPhase != DragPhase.NONE;
+    }
+
+    @Override
+    public void reset() {
+        resetInternal(false);
+        mPhase = DragPhase.NONE;
+    }
+
+    // TileDragSession.Delegate implementation.
+    @Override
+    public float getTileWidthPx() {
+        return mTileWidthPx;
+    }
+
+    @Override
+    public List<TileView> getDraggableTileViews() {
+        List<TileView> draggableTileViews = new ArrayList<TileView>();
+        int tileCount = mMvTilesLayout.getTileCount();
+        for (int i = 0; i < tileCount; ++i) {
+            TileView tileView = mMvTilesLayout.getTileAt(i);
+            if (tileView.isDraggable()) {
+                draggableTileViews.add(tileView);
+            }
+        }
+        return draggableTileViews;
+    }
+
+    @Override
+    public SiteSuggestion getTileViewData(TileView view) {
+        return mMvTilesLayout.getTileViewData(view);
+    }
+
+    private void cancelPendingChange() {
+        if (mPendingChangeCanceller != null) {
+            mPendingChangeCanceller.run();
+            mPendingChangeCanceller = null;
+        }
+    }
+
+    private void resetInternal(boolean accept) {
+        mMvTilesLayout.requestDisallowInterceptTouchEvent(false);
+        cancelPendingChange();
+        mTimer.cancelTimer();
+        if (mTileDragSession != null) {
+            mPendingChangeCanceller = mTileDragSession.finish(accept);
+            mTileDragSession = null;
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileDragSession.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileDragSession.java
new file mode 100644
index 0000000..3761597
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileDragSession.java
@@ -0,0 +1,202 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import org.chromium.build.annotations.Initializer;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+import org.chromium.components.browser_ui.widget.tile.TileView;
+
+import java.util.List;
+
+/**
+ * Helper for {@link TileDragDelegateImpl} to manage session states and interface with {@link
+ * TileMovement}. W.r.t. {@link TileDragDelegateImpl.DragPhase}, the class is used in {PREPARE,
+ * START, DOMINATE}.
+ */
+@NullMarked
+class TileDragSession {
+
+    // Delegate to retrieve data from {@link TileDragDelegateImpl}.
+    interface Delegate {
+        /**
+         * @return Width of a Most Visit Tile, in PX.
+         */
+        float getTileWidthPx();
+
+        /**
+         * @return The current list of {@link TileView} instances in the MVT container.
+         */
+        List<TileView> getDraggableTileViews();
+
+        /**
+         * @return The {@link SiteSuggestion} corresponding to a {@link TileView}.
+         */
+        SiteSuggestion getTileViewData(TileView view);
+    }
+
+    // Scaling factor to shrink the "from" tile in {START, DOMINATE}.
+    private static final float DRAG_ACTIVE_SCALE = 0.8f;
+
+    // Relative X-margin: Multiplied by tile width to get the X-margin.
+    private static final float DRAG_X_MARGIN_RATIO = 0.2f;
+
+    private final Delegate mDelegate;
+    private final TileGroup.TileDragHandlerDelegate mDragResultDelegate;
+    private final TileView mFromView;
+    private final float mStartX;
+    private final float mStartY;
+
+    // Variables unneeded during PREPARE are initialized in start().
+    private @Nullable TileMovement mTileMovement;
+    private float mSavedSrcX;
+    private float mSavedSrcZ;
+    private int mFromIndex;
+    private int mToIndex;
+    private float mDxLo;
+    private float mDxHi;
+
+    /**
+     * @param delegate Data provider.
+     * @param dragHandlerDelegate Delegate to respond to events and results.
+     * @param fromView The view of the "from" tile that's being dragged.
+     * @param eventX The X coordinate of the initial ACTION_DOWN event on the "from" tile.
+     * @param eventY The Y coordinate of the same.
+     */
+    public TileDragSession(
+            Delegate delegate,
+            TileGroup.TileDragHandlerDelegate dragHandlerDelegate,
+            TileView fromView,
+            float eventX,
+            float eventY) {
+        mDelegate = delegate;
+        mDragResultDelegate = dragHandlerDelegate;
+        mFromView = fromView;
+        mStartX = fixEventX(eventX);
+        mStartY = eventY;
+    }
+
+    @Initializer
+    public void start() {
+        mTileMovement = new TileMovement(mDelegate.getDraggableTileViews());
+        mSavedSrcX = mFromView.getX();
+        mSavedSrcZ = mFromView.getZ();
+        mFromIndex = mTileMovement.getIndexOfView(mFromView);
+        mToIndex = mFromIndex;
+
+        // X-margin: A dragged tile is constrained to stay in the box containing all draggable
+        // tiles. To soften the constraint, the X-margin specifies extra room for the dragged tile
+        // to travel horizontally beyond the box.
+        float dragXMarginPx = DRAG_X_MARGIN_RATIO * mDelegate.getTileWidthPx();
+        mDxLo = mTileMovement.getXLo() - mSavedSrcX - dragXMarginPx;
+        mDxHi = mTileMovement.getXHi() - mSavedSrcX + dragXMarginPx;
+
+        mFromView.animate().scaleX(DRAG_ACTIVE_SCALE).scaleY(DRAG_ACTIVE_SCALE).start();
+        // Temporarily increment Z so the "from" tile is drawn on top of other tiles.
+        mFromView.setZ(mSavedSrcZ + 1.0f);
+    }
+
+    public TileGroup.TileDragHandlerDelegate getTileDragHandlerDelegate() {
+        return mDragResultDelegate;
+    }
+
+    /**
+     * Updates {@link mFromView} movement on ACTION_MOVE.
+     *
+     * @param eventX The X coordinate of the The ACTION_MOVE event on the "from" tile.
+     */
+    public void updateFromView(float eventX) {
+        // {@param eventX} is relative to translation X, so we need to add it back to compensate.
+        float rawDx = fixEventX(eventX) - mStartX;
+        mFromView.setTranslationX(Math.max(mDxLo, Math.min(rawDx, mDxHi)));
+    }
+
+    /**
+     * @param eventX The X coordinate of the The ACTION_MOVE event on the "from" tile.
+     * @param eventY The Y coordinate of same.
+     * @return The Euclidean distance squared from ({@link mStartX}, {@link mStartY}) to ({@param
+     *     eventX}, {@param eventY}).
+     */
+    public float getDragDisplacementSquared(float eventX, float eventY) {
+        float rawDx = fixEventX(eventX) - mStartX;
+        float rawDy = eventY - mStartY;
+        // Not using Math.hypot() since it may be slow.
+        return rawDx * rawDx + rawDy * rawDy;
+    }
+
+    /** Updates {@link mToIndex} and possibly shift background tiles on drag. */
+    public void updateToIndexAndAnimate() {
+        assert mTileMovement != null;
+
+        // Find the draggable tile that's closest to the "from" tile's current location.
+        // This is robust for LTR and RTL. Use linear search, which is fast enough.
+        int newToIndex = mTileMovement.getIndexOfViewNearestTo(mFromView.getX());
+
+        // If {@link #mToIndex} changes: Shift non-"from" tiles towards the "from" tile.
+        if (mToIndex != newToIndex) {
+            mToIndex = newToIndex;
+            mTileMovement.shiftBackgroundTile(mFromIndex, mToIndex);
+        }
+    }
+
+    /**
+     * Finishes the drag-and-drop session. If animation is in flight, also returns the {@link
+     * mTileMovement} instance so animation can be cancelled; otherwise returns null.
+     *
+     * @param accept Whether to accept the drag-and-drop if the "from" tile is dragged to a
+     *     different "to" tile.
+     * @return A {@link Runnable} that can be called to cancel the accept / reject animation and
+     *     action while it's in -flight. Once complete then the calling would have no effect.
+     */
+    public @Nullable Runnable finish(boolean accept) {
+        mFromView.setZ(mSavedSrcZ);
+        mFromView.animate().scaleX(1.0f).scaleY(1.0f).start();
+
+        // Handle the case where function is called before start() is called.
+        if (mTileMovement == null) {
+            return null;
+        }
+
+        if (accept && mFromIndex != mToIndex) {
+            mTileMovement.animatedAccept(
+                    mFromIndex,
+                    mToIndex,
+                    /* onAccept= */ () -> {
+                        if (mTileMovement != null) {
+                            TileView toView = mTileMovement.getTileViewAt(mToIndex);
+                            mDragResultDelegate.onDragAccept(
+                                    mDelegate.getTileViewData(mFromView),
+                                    mDelegate.getTileViewData(toView));
+                        }
+                    });
+
+        } else {
+            mTileMovement.animatedReject();
+        }
+
+        return () -> {
+            if (mTileMovement != null) {
+                mTileMovement.cancelIfActive();
+            }
+            mFromView.setScaleX(1.0f);
+            mFromView.setScaleY(1.0f);
+        };
+    }
+
+    /** Helper to instantiate {@link TileMovement}, extract to method to allow testing override. */
+    protected TileMovement createTileMovement(List<TileView> tileViews) {
+        return new TileMovement(tileViews);
+    }
+
+    /**
+     * Performs correction to {@param eventX} from a fresh {@plink MotionEvent} for {@link
+     * #mFromView}. This is needed because the X value read is relative to
+     * `mFromView.getTranslationX()`, but we'd like the X relative to the container.
+     */
+    private float fixEventX(float eventX) {
+        return eventX + mFromView.getTranslationX();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
index dd23af5..cf116b0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
@@ -5,9 +5,8 @@
 package org.chromium.chrome.browser.suggestions.tile;
 
 import android.util.SparseArray;
+import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnCreateContextMenuListener;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
@@ -167,7 +166,10 @@
 
     /** Delegate for handling interactions with tiles. */
     public interface TileInteractionDelegate
-            extends OnClickListener, OnCreateContextMenuListener, View.OnLongClickListener {
+            extends View.OnClickListener,
+                    View.OnCreateContextMenuListener,
+                    View.OnLongClickListener,
+                    View.OnTouchListener {
         /**
          * Set a runnable for click events on the tile. This is primarily used to track interaction
          * with the tile used by feature engagement purposes.
@@ -184,11 +186,57 @@
         void setOnRemoveRunnable(Runnable removeRunnable);
     }
 
+    /** Delegate for receive intermediate events and final results of tile drag. */
+    public interface TileDragHandlerDelegate {
+        /**
+         * Called when the tile drag session becomes the dominant UI mode. The implementation should
+         * suppress competing UI, e.g., context menu.
+         */
+        void onDragDominate();
+
+        /**
+         * Called when drag UI successfully produces result. The implementation should perform
+         * reorder and refresh UI if successful.
+         *
+         * @param fromSuggestion Data to identify the tile being dragged.
+         * @param toSuggestion Data to identify the tile being dropped on.
+         * @return Whether the operation successfully ran.
+         */
+        boolean onDragAccept(SiteSuggestion fromSuggestion, SiteSuggestion toSuggestion);
+    }
+
+    /** Delegate for tile drag UI. */
+    public interface TileDragDelegate {
+        /**
+         * Handler for ACTION_DOWN touch event on tile. This may start a tile drag session.
+         *
+         * @param view The View of the tile receiving ACTION_DOWN.
+         * @param event The ACTION_DOWN event.
+         * @param dragHandlerDelegate Handler for drag results.
+         */
+        void onTileTouchDown(
+                View view, MotionEvent event, TileDragHandlerDelegate dragHandlerDelegate);
+
+        /**
+         * Handler for non-ACTION_DOWN events to continue / end a tile drag session. Should be
+         * called if a tile drag session is live.
+         */
+        void onSessionTileTouch(View view, MotionEvent event);
+
+        /**
+         * @return Whether a tile drag session is live, requiring onSessionTileTouch() to be called.
+         */
+        boolean hasSession();
+
+        /** Forces tile drag session to end. */
+        void reset();
+    }
+
     /** Delegate for handling interactions with custom tiles. Not tied to a particular Tile. */
     public interface CustomTileModificationDelegate {
         /**
          * Opens the Custom Tile Edit Dialog (as "Add shortcut") to add a new Custom Tile. If add
-         * proceeds and is successful,refreshes the MVT.
+         * proceeds and is successful, refreshes the MVT.
          */
         void add();
 
@@ -210,6 +258,15 @@
          * refreshes the MVT.
          */
         void edit(SiteSuggestion suggestion);
+
+        /**
+         * Searches for existing "from" and "to" Custom Tiles matching {@param fromSuggestion} and
+         * {@param toSuggestion}. If both are found, attempt to move "from" tile to position of the
+         * "to" tile, and shift everything between. If successful, refreshes the MVT.
+         *
+         * @return Whether the operation successfully ran.
+         */
+        boolean reorder(SiteSuggestion fromSuggestion, SiteSuggestion toSuggestion);
     }
 
     /**
@@ -244,6 +301,7 @@
     private final SuggestionsUiDelegate mUiDelegate;
     private final ContextMenuManager mContextMenuManager;
     private final Delegate mTileGroupDelegate;
+    private final TileDragDelegate mTileDragDelegate;
     private final Observer mObserver;
     private final TileRenderer mTileRenderer;
     private final CustomTileModificationDelegate mCustomTileModificationDelegate;
@@ -283,6 +341,7 @@
                     return new TileInteractionDelegateImpl(
                             mContextMenuManager,
                             mTileGroupDelegate,
+                            mTileDragDelegate,
                             mCustomTileModificationDelegate,
                             mPrerenderDelay,
                             tile,
@@ -322,11 +381,13 @@
             SuggestionsUiDelegate uiDelegate,
             ContextMenuManager contextMenuManager,
             Delegate tileGroupDelegate,
+            TileDragDelegate tileDragDelegate,
             Observer observer,
             OfflinePageBridge offlinePageBridge) {
         mUiDelegate = uiDelegate;
         mContextMenuManager = contextMenuManager;
         mTileGroupDelegate = tileGroupDelegate;
+        mTileDragDelegate = tileDragDelegate;
         mObserver = observer;
         mTileRenderer = tileRenderer;
         mOfflineModelObserver = new OfflineModelObserver(offlinePageBridge);
@@ -348,6 +409,7 @@
 
         boolean removalCompleted = mPendingChanges.removalUrl != null;
         boolean insertionCompleted = mPendingChanges.insertionUrl == null;
+        boolean forceUpdate = false;
 
         mPendingChanges.tiles = new ArrayList<>();
         for (SiteSuggestion suggestion : siteSuggestions) {
@@ -371,10 +433,11 @@
         if (mPendingChanges.customTilesIndicator) {
             mPendingChanges.customTilesIndicator = false;
             expectedChangeCompleted = true;
+            forceUpdate = true;
         }
 
         if (!mHasReceivedData || !mUiDelegate.isVisible() || expectedChangeCompleted) {
-            loadTiles();
+            loadTiles(forceUpdate);
         }
     }
 
@@ -430,7 +493,7 @@
      */
     public void onSwitchToForeground(boolean trackLoadTask) {
         if (trackLoadTask) addTask(TileTask.FETCH_DATA);
-        if (mPendingChanges.tiles != null) loadTiles();
+        if (mPendingChanges.tiles != null) loadTiles(/* forceUpdate= */ false);
         if (trackLoadTask) removeTask(TileTask.FETCH_DATA);
     }
 
@@ -438,14 +501,20 @@
         return mTileSetupDelegate;
     }
 
-    /** Loads tile data from {@link #mPendingChanges.tiles} and clears it afterwards. */
-    private void loadTiles() {
+    /**
+     * Loads tile data from {@link #mPendingChanges.tiles} and clears it afterwards.
+     *
+     * @param forceUpdate Flag to force an update even if tile composition remains the same. A
+     *     particular use case is Custom Tile reordering, which keeps the set of suggestions the
+     *     same but still requires update.
+     */
+    private void loadTiles(boolean forceUpdate) {
         assert mPendingChanges.tiles != null;
 
         boolean isInitialLoad = !mHasReceivedData;
         mHasReceivedData = true;
 
-        boolean dataChanged = isInitialLoad;
+        boolean dataChanged = forceUpdate || isInitialLoad;
         List<Tile> personalisedTiles = mTileSections.get(TileSectionType.PERSONALIZED);
         int oldPersonalisedTilesCount = personalisedTiles == null ? 0 : personalisedTiles.size();
 
@@ -629,6 +698,15 @@
                     mTileGroupDelegate::hasCustomLink);
         }
 
+        @Override
+        public boolean reorder(SiteSuggestion fromSuggestion, SiteSuggestion toSuggestion) {
+            @Nullable Tile fromTile = findTile(fromSuggestion);
+            @Nullable Tile toTile = findTile(toSuggestion);
+            return fromTile != null
+                    && toTile != null
+                    && reorderCustomLinkAndUpdateOnSuccess(fromTile.getUrl(), toTile.getIndex());
+        }
+
         private boolean addCustomLinkAndUpdateOnSuccess(String name, GURL url) {
             // On success, onSiteSuggestionsAvailable() triggers.
             mPendingChanges.customTilesIndicator = true;
@@ -657,6 +735,15 @@
                 mPendingChanges.customTilesIndicator = false;
             }
         }
+
+        private boolean reorderCustomLinkAndUpdateOnSuccess(GURL url, int newPos) {
+            mPendingChanges.customTilesIndicator = true;
+            boolean success = mTileGroupDelegate.reorderCustomLink(url, newPos);
+            if (!success) {
+                mPendingChanges.customTilesIndicator = false;
+            }
+            return success;
+        }
     }
 
     private class OfflineModelObserver extends SuggestionsOfflineModelObserver<Tile> {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java
index 393afb5..cb195f7e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroupDelegateImpl.java
@@ -96,6 +96,12 @@
         return mMostVisitedSites.hasCustomLink(keyUrl);
     }
 
+    @Override
+    public boolean reorderCustomLink(GURL keyUrl, int newPos) {
+        assert !mIsDestroyed;
+        return mMostVisitedSites.reorderCustomLink(keyUrl, newPos);
+    }
+
     // TileGroup.Delegate implementation.
     @Override
     @Initializer
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileInteractionDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileInteractionDelegateImpl.java
index 04d6eff..c50d9791 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileInteractionDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileInteractionDelegateImpl.java
@@ -21,6 +21,7 @@
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.native_page.ContextMenuManager.ContextMenuItemId;
 import org.chromium.chrome.browser.preloading.AndroidPrerenderManager;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
 import org.chromium.chrome.browser.suggestions.SuggestionsMetrics;
 import org.chromium.ui.mojom.WindowOpenDisposition;
 import org.chromium.url.GURL;
@@ -35,9 +36,10 @@
 class TileInteractionDelegateImpl
         implements TileGroup.TileInteractionDelegate,
                 ContextMenuManager.Delegate,
-                View.OnTouchListener {
+                TileGroup.TileDragHandlerDelegate {
     private final ContextMenuManager mContextMenuManager;
     private final TileGroup.Delegate mTileGroupDelegate;
+    private final TileGroup.TileDragDelegate mTileDragDelegate;
     private final TileGroup.CustomTileModificationDelegate mCustomTileModificationDelegate;
     private final int mPrerenderDelay;
     private final Tile mTile;
@@ -45,16 +47,16 @@
 
     private @Nullable Runnable mOnClickRunnable;
     private @Nullable Runnable mOnRemoveRunnable;
-    private @Nullable Long mTouchTimer;
+    private @Nullable Long mTouchTime;
     private @Nullable CancelableRunnable mPrerenderRunnable;
     private @Nullable GURL mPrerenderedUrl;
     private @Nullable GURL mScheduldedPrerenderingUrl;
 
     private void maybeRecordTouchDuration(boolean taken) {
-        if (mTouchTimer == null) return;
+        if (mTouchTime == null) return;
 
-        long duration = TimeUtils.elapsedRealtimeMillis() - mTouchTimer;
-        mTouchTimer = null;
+        long duration = TimeUtils.elapsedRealtimeMillis() - mTouchTime;
+        mTouchTime = null;
         RecordHistogram.recordLongTimesHistogram(
                 taken
                         ? "Prerender.Experimental.NewTabPage.TouchDuration.Taken"
@@ -65,12 +67,14 @@
     public TileInteractionDelegateImpl(
             ContextMenuManager contextMenuManager,
             TileGroup.Delegate tileGroupDelegate,
+            TileGroup.TileDragDelegate tileDragDelegate,
             TileGroup.CustomTileModificationDelegate customTileModificationDelegate,
             int prerenderDelay,
             Tile tile,
             View view) {
         mContextMenuManager = contextMenuManager;
         mTileGroupDelegate = tileGroupDelegate;
+        mTileDragDelegate = tileDragDelegate;
         mCustomTileModificationDelegate = customTileModificationDelegate;
         mPrerenderDelay = prerenderDelay;
         mTile = tile;
@@ -80,11 +84,13 @@
         mTileGroupDelegate.initAndroidPrerenderManager(mAndroidPrerenderManager);
     }
 
+    // TileGroup.TileInteractionDelegate => OnClickListener implementation.
     @Override
     public void onClick(View view) {
         maybeRecordTouchDuration(true);
 
         SuggestionsMetrics.recordTileTapped();
+        mTileDragDelegate.reset();
         if (mOnClickRunnable != null) mOnClickRunnable.run();
         mTileGroupDelegate.openMostVisitedItem(WindowOpenDisposition.CURRENT_TAB, mTile);
     }
@@ -137,21 +143,59 @@
         mScheduldedPrerenderingUrl = null;
     }
 
+    // TileGroup.TileInteractionDelegate => View.OnCreateContextMenuListener implementation.
+    @Override
+    public void onCreateContextMenu(
+            ContextMenu contextMenu, View view, ContextMenuInfo contextMenuInfo) {
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.TILE_CONTEXT_MENU_REFACTOR)) return;
+
+        mContextMenuManager.createContextMenu(contextMenu, view, this);
+    }
+
+    // TileGroup.TileInteractionDelegate => View.OnLongClickListener implementation.
+    @Override
+    public boolean onLongClick(View view) {
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.TILE_CONTEXT_MENU_REFACTOR)) {
+            return false;
+        }
+
+        return mContextMenuManager.showListContextMenu(view, this);
+    }
+
+    // TileGroup.TileInteractionDelegate => View.OnTouchListener implementation.
     @Override
     @SuppressLint("ClickableViewAccessibility")
     public boolean onTouch(View view, MotionEvent event) {
         if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            mTouchTimer = TimeUtils.elapsedRealtimeMillis();
+            mTouchTime = TimeUtils.elapsedRealtimeMillis();
             maybePrerender(mTile.getUrl());
-        }
-        if (event.getAction() == MotionEvent.ACTION_CANCEL) {
+        } else if (event.getAction() == MotionEvent.ACTION_CANCEL) {
             maybeRecordTouchDuration(false);
             cancelPrerender();
         }
 
+        // Handle tile drag-and-drop separately.
+        if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            mTileDragDelegate.onTileTouchDown(view, event, this);
+        } else if (mTileDragDelegate.hasSession()) {
+            mTileDragDelegate.onSessionTileTouch(view, event);
+        }
+
         return false;
     }
 
+    // TileGroup.TileInteractionDelegate implementation.
+    @Override
+    public void setOnClickRunnable(Runnable clickRunnable) {
+        mOnClickRunnable = clickRunnable;
+    }
+
+    @Override
+    public void setOnRemoveRunnable(Runnable removeRunnable) {
+        mOnRemoveRunnable = removeRunnable;
+    }
+
+    // ContextMenuManager.Delegate implementation.
     @Override
     public void openItem(int windowDisposition) {
         mTileGroupDelegate.openMostVisitedItem(windowDisposition, mTile);
@@ -212,31 +256,15 @@
     @Override
     public void onContextMenuCreated() {}
 
+    // TileGroup.TileDragHandlerDelegate implementation.
     @Override
-    public void onCreateContextMenu(
-            ContextMenu contextMenu, View view, ContextMenuInfo contextMenuInfo) {
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.TILE_CONTEXT_MENU_REFACTOR)) return;
-
-        mContextMenuManager.createContextMenu(contextMenu, view, this);
+    public void onDragDominate() {
+        mContextMenuManager.hideListContextMenu();
     }
 
     @Override
-    public boolean onLongClick(View view) {
-        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.TILE_CONTEXT_MENU_REFACTOR)) {
-            return false;
-        }
-
-        return mContextMenuManager.showListContextMenu(view, this);
-    }
-
-    @Override
-    public void setOnClickRunnable(Runnable clickRunnable) {
-        mOnClickRunnable = clickRunnable;
-    }
-
-    @Override
-    public void setOnRemoveRunnable(Runnable removeRunnable) {
-        mOnRemoveRunnable = removeRunnable;
+    public boolean onDragAccept(SiteSuggestion fromSuggestion, SiteSuggestion toSuggestion) {
+        return mCustomTileModificationDelegate.reorder(fromSuggestion, toSuggestion);
     }
 
     boolean isCustomizationItemSupported(boolean matchIsCustomLink) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileMovement.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileMovement.java
new file mode 100644
index 0000000..e19bd8a
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileMovement.java
@@ -0,0 +1,195 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import android.view.ViewPropertyAnimator;
+
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.components.browser_ui.widget.tile.TileView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages transient movement and animation of a row of {@link TileView} instances that are being
+ * reordered under drag-and-drop UI. When tiles move, only their (visual) locations are affected,
+ * and not their order within the parent container. Once tile movement has been reject or accepted,
+ * all positions are restored; a refresh is needed to actually move tiles.
+ */
+@NullMarked
+public class TileMovement {
+
+    private final List<TileView> mTileViews;
+    private final List<Float> mOriginalX;
+    private final List<@Nullable ViewPropertyAnimator> mAnimators;
+    private boolean mIsActive;
+
+    /**
+     * @param tileViews A non-empty row of tiles to manage. The X-coordinates are assumed to ascend
+     *     (LTR) or descend (RTL).
+     */
+    TileMovement(List<TileView> tileViews) {
+        assert !tileViews.isEmpty();
+        mTileViews = tileViews;
+        mOriginalX = new ArrayList<Float>();
+        mAnimators = new ArrayList<@Nullable ViewPropertyAnimator>();
+        for (TileView tileView : mTileViews) {
+            float x = tileView.getX();
+            mOriginalX.add(x);
+            mAnimators.add(null);
+        }
+        mIsActive = true;
+    }
+
+    /**
+     * @return The {@link TileView} at {@param index}.
+     */
+    public TileView getTileViewAt(int index) {
+        return mTileViews.get(index);
+    }
+
+    /**
+     * @return Index of {@param view} existing in {@link #mTileViews}.
+     */
+    public int getIndexOfView(TileView view) {
+        int index = mTileViews.indexOf(view);
+        assert index >= 0;
+        return index;
+    }
+
+    /**
+     * @return Index of the {@link #mTileViews} element whose X-coordinate is nearest to {@param x},
+     *     preferring lower index on a tie.
+     */
+    public int getIndexOfViewNearestTo(float x) {
+        int n = mTileViews.size();
+        int bestIndex = 0;
+        float bestDist = Math.abs(mOriginalX.get(0) - x);
+        for (int i = 1; i < n; ++i) {
+            float dist = Math.abs(mOriginalX.get(i) - x);
+            if (bestDist > dist) {
+                bestDist = dist;
+                bestIndex = i;
+            }
+        }
+        return bestIndex;
+    }
+
+    /**
+     * @return The minimum X-coordinate among managed tiles.
+     */
+    public float getXLo() {
+        return Math.min(mOriginalX.get(0), mOriginalX.get(mOriginalX.size() - 1));
+    }
+
+    /**
+     * @return The maximum X-coordinate among managed tiles.
+     */
+    public float getXHi() {
+        return Math.max(mOriginalX.get(0), mOriginalX.get(mOriginalX.size() - 1));
+    }
+
+    /**
+     * Changes the (visual) location of the tile at {@param index} to the original location of tile
+     * at {@param newIndex}. Once move completes (possibly after animation, as determined by {@param
+     * isAnimated}), runs {@param onEnd} if non-null. Replaces the previous tile animation, and
+     * prevents the previously specified {@param onEnd} from running.
+     */
+    public void moveTile(int index, int newIndex, boolean isAnimated, @Nullable Runnable onEnd) {
+        TileView tileView = mTileViews.get(index);
+        float x = mOriginalX.get(newIndex);
+        ViewPropertyAnimator oldAnimator = mAnimators.get(index);
+        if (oldAnimator != null) {
+            oldAnimator.cancel();
+            mAnimators.set(index, null);
+        }
+        if (isAnimated) {
+            ViewPropertyAnimator animator = tileView.animate();
+            mAnimators.set(index, animator);
+            if (onEnd != null) {
+                animator.withEndAction(onEnd);
+            }
+            animator.x(x).start();
+        } else {
+            tileView.setX(x);
+            if (onEnd != null) {
+                onEnd.run();
+            }
+        }
+    }
+
+    /**
+     * Given that tile at {@param fromIndex} is moved to {@param toIndex}, shifts the latter and all
+     * tiles in between towards {@param fromIndex}, and resets all other tiles, with animation that
+     * overrides existing animations.
+     */
+    public void shiftBackgroundTile(int fromIndex, int toIndex) {
+        int n = mTileViews.size();
+        // Shift affected tiles if {@link #toIndex} < {@link #fromIndex}.
+        for (int i = toIndex; i < fromIndex; ++i) {
+            moveTile(i, i + 1, /* isAnimated= */ true, /* onEnd= */ null);
+        }
+        // Shift affected tiles if {@link #toIndex} > {@link #fromIndex}.
+        for (int i = toIndex; i > fromIndex; --i) {
+            moveTile(i, i - 1, /* isAnimated= */ true, /* onEnd= */ null);
+        }
+        // Reset tiles that are unaffected / no longer affected.
+        for (int i = Math.min(fromIndex, toIndex) - 1; i >= 0; --i) {
+            moveTile(i, i, /* isAnimated= */ true, /* onEnd= */ null);
+        }
+        for (int i = Math.max(fromIndex, toIndex) + 1; i < n; ++i) {
+            moveTile(i, i, /* isAnimated= */ true, /* onEnd= */ null);
+        }
+    }
+
+    /** Stops all pending animations and restores original tile locations without animation. */
+    public void cancelIfActive() {
+        if (mIsActive) {
+            restoreTiles(/* isAnimated= */ false);
+            mIsActive = false;
+        }
+    }
+
+    /**
+     * Accepts tile movement from {@param fromIndex} to {@param toIndex}, starting with animation
+     * for aesthetics. The animation can still be cancelled by calling cancel(). When animation
+     * finishes, runs {@param onAccept}.
+     */
+    public void animatedAccept(int fromIndex, int toIndex, Runnable onAccept) {
+        // Animate the "from" tile moving to "to" tile, which can be cancelled via cancelIfActive().
+        moveTile(
+                fromIndex,
+                toIndex,
+                /* isAnimated= */ true,
+                () -> {
+                    // Animation completes: Disable cancelIfActive(), restore all tile positions,
+                    // then run {@param onAccept}. Note that restore is done regardless of whether
+                    // the run fails or succeeds. That's because {@link TileVIew} visual changes
+                    // are transient, and need to be undone, especially that they may be reused.
+                    // Next, would restore cause "glitch", i.e., tiles temporarily jump back, before
+                    // being re-rendered into the desired state? We assume this won't happen
+                    // (perhaps since @{param onAccept} is eager), or that any effect is negligible
+                    // and not worth fixing for now.
+                    mIsActive = false;
+                    restoreTiles(/* isAnimated= */ false);
+                    onAccept.run();
+                });
+    }
+
+    /** Restores original tile locations, with animation. */
+    public void animatedReject() {
+        // Restore tiles with animation. This can get cancelled via cancelIfActive(). And if the
+        // animation completes, cancelIfActive() can still be called -- but this is no-op anyway.
+        restoreTiles(/* isAnimated= */ true);
+    }
+
+    /** Restores original tile locations, with {@param isAnimated} specified. */
+    private void restoreTiles(boolean isAnimated) {
+        for (int i = 0; i < mTileViews.size(); ++i) {
+            moveTile(i, i, isAnimated, null);
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
index 6e73760..0366ef9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
@@ -152,6 +152,8 @@
                 return "HistoryNavigationBackground";
             case TabLaunchType.FROM_HISTORY_NAVIGATION_FOREGROUND:
                 return "HistoryNavigationBackground";
+            case TabLaunchType.FROM_LONGPRESS_FOREGROUND_IN_GROUP:
+                return "LongpressForegroundInGroup";
             default:
                 assert false : "Unexpected serialization of tabLaunchType: " + tabLaunchType;
                 return "TypeUnknown";
@@ -701,6 +703,7 @@
                 transition = PageTransition.AUTO_TOPLEVEL;
                 break;
             case TabLaunchType.FROM_LONGPRESS_FOREGROUND:
+            case TabLaunchType.FROM_LONGPRESS_FOREGROUND_IN_GROUP:
             case TabLaunchType.FROM_LONGPRESS_INCOGNITO:
             case TabLaunchType.FROM_HISTORY_NAVIGATION_FOREGROUND:
                 transition = PageTransition.LINK;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
index 19ef9a8..e3ce040 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelImpl.java
@@ -953,10 +953,17 @@
         switch (disposition) {
             case WindowOpenDisposition.NEW_WINDOW: // fall through
             case WindowOpenDisposition.NEW_FOREGROUND_TAB:
+                tabLaunchType =
+                        parent.getTabGroupId() == null
+                                ? TabLaunchType.FROM_LONGPRESS_FOREGROUND
+                                : TabLaunchType.FROM_LONGPRESS_FOREGROUND_IN_GROUP;
                 break;
             case WindowOpenDisposition.NEW_POPUP: // fall through
             case WindowOpenDisposition.NEW_BACKGROUND_TAB:
-                tabLaunchType = TabLaunchType.FROM_LONGPRESS_BACKGROUND;
+                tabLaunchType =
+                        parent.getTabGroupId() == null
+                                ? TabLaunchType.FROM_LONGPRESS_BACKGROUND
+                                : TabLaunchType.FROM_LONGPRESS_BACKGROUND_IN_GROUP;
                 break;
             case WindowOpenDisposition.OFF_THE_RECORD:
                 incognito = true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index eaf15a00..a532550 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -229,6 +229,8 @@
     private final FullscreenManager.Observer mFullscreenObserver;
     private final ObservableSupplierImpl<Boolean> mHomepageEnabledSupplier =
             new ObservableSupplierImpl<>();
+    private final ObservableSupplierImpl<Boolean> mHomepageNonNtpSupplier =
+            new ObservableSupplierImpl<>();
     private final ObservableSupplier<Boolean> mOmniboxFocusStateSupplier;
     private final ObservableSupplierImpl<Boolean> mIsNtpShowingSupplier =
             new ObservableSupplierImpl<>();
@@ -1737,6 +1739,7 @@
                         mTabSwitcherButtonCoordinator,
                         mCustomTabCount,
                         mHomepageEnabledSupplier,
+                        mHomepageNonNtpSupplier,
                         mCompositorViewHolder::getResourceManager,
                         historyDelegate,
                         initializeWithIncognitoColors,
@@ -1756,6 +1759,7 @@
         mHomepageStateListener =
                 () -> {
                     mHomepageEnabledSupplier.set(HomepageManager.getInstance().isHomepageEnabled());
+                    mHomepageNonNtpSupplier.set(HomepageManager.getInstance().isHomepageNonNtp());
                 };
 
         HomepageManager.getInstance().addListener(mHomepageStateListener);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index ac3ad6f..32aae124 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -1817,6 +1817,9 @@
                 "EdgeToEdgeChinEligibility", eligible ? "Eligible" : "Not Eligible");
 
         if (supportsEdgeToEdge() && EdgeToEdgeUtils.isEdgeToEdgeBottomChinEnabled()) {
+            assert eligible
+                    : "The edge-to-edge controller is being initialized, though it should not be"
+                            + " eligible!";
             mEdgeToEdgeController =
                     EdgeToEdgeControllerFactory.create(
                             mActivity,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/channels/SiteChannelsManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/channels/SiteChannelsManagerTest.java
index 1450fa8..70b791c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/channels/SiteChannelsManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/channels/SiteChannelsManagerTest.java
@@ -28,6 +28,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.PayloadCallbackHelper;
 import org.chromium.chrome.browser.notifications.NotificationChannelStatus;
 import org.chromium.chrome.browser.notifications.NotificationSettingsBridge;
 import org.chromium.chrome.browser.notifications.NotificationSettingsBridge.SiteChannel;
@@ -171,7 +172,7 @@
         NotificationSettingsBridge.SiteChannel channel =
                 mSiteChannelsManager.createSiteChannel("https://example-enabled.org", 0L, true);
         assertThat(
-                mSiteChannelsManager.getChannelStatus(channel.getId()),
+                getChannelStatus(channel.getId()),
                 matchesChannelStatus(NotificationChannelStatus.ENABLED));
     }
 
@@ -179,12 +180,12 @@
     @SmallTest
     public void testGetChannelStatus_channelCreatedAsBlocked() {
         assertThat(
-                mSiteChannelsManager.getChannelStatus("https://example-blocked.com"),
+                getChannelStatus("https://example-blocked.com"),
                 matchesChannelStatus(NotificationChannelStatus.UNAVAILABLE));
         NotificationSettingsBridge.SiteChannel channel =
                 mSiteChannelsManager.createSiteChannel("https://example-blocked.com", 0L, false);
         assertThat(
-                mSiteChannelsManager.getChannelStatus(channel.getId()),
+                getChannelStatus(channel.getId()),
                 matchesChannelStatus(NotificationChannelStatus.BLOCKED));
     }
 
@@ -192,7 +193,7 @@
     @SmallTest
     public void testGetChannelStatus_channelNotCreated() {
         assertThat(
-                mSiteChannelsManager.getChannelStatus("invalid-channel-id"),
+                getChannelStatus("invalid-channel-id"),
                 matchesChannelStatus(NotificationChannelStatus.UNAVAILABLE));
     }
 
@@ -203,7 +204,7 @@
                 mSiteChannelsManager.createSiteChannel("https://chromium.org", 0L, true);
         mSiteChannelsManager.deleteSiteChannel(channel.getId());
         assertThat(
-                mSiteChannelsManager.getChannelStatus(channel.getId()),
+                getChannelStatus(channel.getId()),
                 matchesChannelStatus(NotificationChannelStatus.UNAVAILABLE));
     }
 
@@ -308,4 +309,10 @@
                         "Notifications.Android.SitesChannel"),
                 is(1));
     }
+
+    private static @NotificationChannelStatus int getChannelStatus(String channelId) {
+        PayloadCallbackHelper<Integer> helper = new PayloadCallbackHelper();
+        SiteChannelsManager.getInstance().getChannelStatusAsync(channelId, helper::notifyCalled);
+        return helper.getOnlyPayloadBlocking();
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java
index 5d7a9b44..e2024a54 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/suggestions/tile/TileGroupUnitTest.java
@@ -74,6 +74,7 @@
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Mock private TileGroup.Observer mTileGroupObserver;
     @Mock private TileGroup.Delegate mTileGroupDelegate;
+    @Mock private TileGroup.TileDragDelegate mTileDragDelegate;
     @Mock private SuggestionsUiDelegate mSuggestionsUiDelegate;
     @Mock private ContextMenuManager mContextMenuManager;
     @Mock private OfflinePageBridge mOfflinePageBridge;
@@ -125,6 +126,7 @@
                         mSuggestionsUiDelegate,
                         mContextMenuManager,
                         mTileGroupDelegate,
+                        mTileDragDelegate,
                         mTileGroupObserver,
                         mOfflinePageBridge);
         tileGroup.startObserving(MAX_TILES_TO_FETCH);
@@ -151,6 +153,7 @@
                         mSuggestionsUiDelegate,
                         mContextMenuManager,
                         mTileGroupDelegate,
+                        mTileDragDelegate,
                         mTileGroupObserver,
                         mOfflinePageBridge);
         tileGroup.startObserving(MAX_TILES_TO_FETCH);
@@ -259,6 +262,7 @@
                         uiDelegate,
                         mContextMenuManager,
                         mTileGroupDelegate,
+                        mTileDragDelegate,
                         mTileGroupObserver,
                         mOfflinePageBridge);
         tileGroup.startObserving(MAX_TILES_TO_FETCH);
@@ -281,6 +285,7 @@
                         uiDelegate,
                         mContextMenuManager,
                         mTileGroupDelegate,
+                        mTileDragDelegate,
                         mTileGroupObserver,
                         mOfflinePageBridge);
         tileGroup.startObserving(MAX_TILES_TO_FETCH);
@@ -331,6 +336,7 @@
                         uiDelegate,
                         mContextMenuManager,
                         mTileGroupDelegate,
+                        mTileDragDelegate,
                         mTileGroupObserver,
                         mOfflinePageBridge);
         tileGroup.startObserving(MAX_TILES_TO_FETCH);
@@ -362,6 +368,7 @@
                         uiDelegate,
                         mContextMenuManager,
                         mTileGroupDelegate,
+                        mTileDragDelegate,
                         mTileGroupObserver,
                         mOfflinePageBridge);
         tileGroup.startObserving(MAX_TILES_TO_FETCH);
@@ -389,6 +396,7 @@
                         uiDelegate,
                         mContextMenuManager,
                         mTileGroupDelegate,
+                        mTileDragDelegate,
                         mTileGroupObserver,
                         mOfflinePageBridge);
         tileGroup.startObserving(MAX_TILES_TO_FETCH);
@@ -423,6 +431,7 @@
                         mSuggestionsUiDelegate,
                         mContextMenuManager,
                         mTileGroupDelegate,
+                        mTileDragDelegate,
                         mTileGroupObserver,
                         mOfflinePageBridge);
         tileGroup.startObserving(MAX_TILES_TO_FETCH);
@@ -585,6 +594,7 @@
                         mSuggestionsUiDelegate,
                         mContextMenuManager,
                         mTileGroupDelegate,
+                        mTileDragDelegate,
                         mTileGroupObserver,
                         mOfflinePageBridge);
         tileGroup.startObserving(MAX_TILES_TO_FETCH);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreatorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreatorTest.java
index 8496653..3d6d04cb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreatorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreatorTest.java
@@ -8,6 +8,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_LOW_END_DEVICE;
@@ -409,6 +410,126 @@
                         .getUrl());
     }
 
+    @Test
+    @MediumTest
+    @Feature({"Browser"})
+    public void testCreateNewTabSameGroupAsParent_FromLongpressForegroundInGroup() {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    Tab currentTab = sActivityTestRule.getActivity().getActivityTab();
+                    Tab tabForGroup =
+                            sActivityTestRule
+                                    .getActivity()
+                                    .getCurrentTabCreator()
+                                    .createNewTab(
+                                            new LoadUrlParams(mTestServer.getURL(TEST_PATH)),
+                                            TabLaunchType.FROM_LINK,
+                                            currentTab);
+                    ChromeTabUtils.mergeTabsToGroup(currentTab, tabForGroup);
+                    Tab newTab =
+                            sActivityTestRule
+                                    .getActivity()
+                                    .getCurrentTabCreator()
+                                    .createNewTab(
+                                            new LoadUrlParams(mTestServer.getURL(TEST_PATH)),
+                                            TabLaunchType.FROM_LONGPRESS_FOREGROUND_IN_GROUP,
+                                            currentTab);
+                    assertNotNull("Expected tab to have a tab group ID", newTab.getTabGroupId());
+                    assertEquals(
+                            "Expected tab to have the same tab group ID as its parent",
+                            currentTab.getTabGroupId(),
+                            newTab.getTabGroupId());
+                });
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Browser"})
+    public void testCreateNewTabSameGroupAsParent_FromLongpressBackgroundInGroup() {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    Tab currentTab = sActivityTestRule.getActivity().getActivityTab();
+                    Tab tabForGroup =
+                            sActivityTestRule
+                                    .getActivity()
+                                    .getCurrentTabCreator()
+                                    .createNewTab(
+                                            new LoadUrlParams(mTestServer.getURL(TEST_PATH)),
+                                            TabLaunchType.FROM_LINK,
+                                            currentTab);
+                    ChromeTabUtils.mergeTabsToGroup(currentTab, tabForGroup);
+                    Tab newTab =
+                            sActivityTestRule
+                                    .getActivity()
+                                    .getCurrentTabCreator()
+                                    .createNewTab(
+                                            new LoadUrlParams(mTestServer.getURL(TEST_PATH)),
+                                            TabLaunchType.FROM_LONGPRESS_BACKGROUND_IN_GROUP,
+                                            currentTab);
+                    assertNotNull("Expected tab to have a tab group ID", newTab.getTabGroupId());
+                    assertEquals(
+                            "Expected tab to have the same tab group ID as its parent",
+                            currentTab.getTabGroupId(),
+                            newTab.getTabGroupId());
+                });
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Browser"})
+    public void testCreateNewTab_ParentInGroup_FromLongpressBackground_OutsideGroup() {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    Tab currentTab = sActivityTestRule.getActivity().getActivityTab();
+                    Tab tabForGroup =
+                            sActivityTestRule
+                                    .getActivity()
+                                    .getCurrentTabCreator()
+                                    .createNewTab(
+                                            new LoadUrlParams(mTestServer.getURL(TEST_PATH)),
+                                            TabLaunchType.FROM_LINK,
+                                            currentTab);
+                    ChromeTabUtils.mergeTabsToGroup(currentTab, tabForGroup);
+                    Tab newTab =
+                            sActivityTestRule
+                                    .getActivity()
+                                    .getCurrentTabCreator()
+                                    .createNewTab(
+                                            new LoadUrlParams(mTestServer.getURL(TEST_PATH)),
+                                            TabLaunchType.FROM_LONGPRESS_BACKGROUND,
+                                            currentTab);
+                    assertNull("Expected tab to not be in a group", newTab.getTabGroupId());
+                });
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Browser"})
+    public void testCreateNewTab_FromLongpressForeground_OutsideGroup() {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    Tab currentTab = sActivityTestRule.getActivity().getActivityTab();
+                    Tab tabForGroup =
+                            sActivityTestRule
+                                    .getActivity()
+                                    .getCurrentTabCreator()
+                                    .createNewTab(
+                                            new LoadUrlParams(mTestServer.getURL(TEST_PATH)),
+                                            TabLaunchType.FROM_LINK,
+                                            currentTab);
+                    ChromeTabUtils.mergeTabsToGroup(currentTab, tabForGroup);
+                    Tab newTab =
+                            sActivityTestRule
+                                    .getActivity()
+                                    .getCurrentTabCreator()
+                                    .createNewTab(
+                                            new LoadUrlParams(mTestServer.getURL(TEST_PATH)),
+                                            TabLaunchType.FROM_LONGPRESS_FOREGROUND,
+                                            currentTab);
+                    assertNull("Expected tab to not be in a group", newTab.getTabGroupId());
+                });
+    }
+
     private Intent createIntent(int tabIndex) {
         Intent intent = new Intent();
         intent.putExtra(IntentHandler.EXTRA_TAB_INDEX, tabIndex);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationChannelPreserverTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationChannelPreserverTest.java
index faa6fdbb4..96ffe36384 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationChannelPreserverTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationChannelPreserverTest.java
@@ -22,8 +22,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
+import org.mockito.stubbing.Answer;
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.Callback;
@@ -130,11 +132,25 @@
     }
 
     private void setChannelStatus(boolean enabled) {
-        when(mSiteChannelsManager.getChannelStatus(eq(CHANNEL_ID)))
-                .thenReturn(
-                        enabled
-                                ? NotificationChannelStatus.ENABLED
-                                : NotificationChannelStatus.BLOCKED);
+        doAnswer(
+                        new Answer<Void>() {
+                            @Override
+                            public Void answer(InvocationOnMock invocation) throws Throwable {
+                                String channelIdArg = invocation.getArgument(0);
+                                Callback<Integer> callbackArg = invocation.getArgument(1);
+
+                                if (channelIdArg.equals(CHANNEL_ID)) {
+                                    if (enabled) {
+                                        callbackArg.onResult(NotificationChannelStatus.ENABLED);
+                                    } else {
+                                        callbackArg.onResult(NotificationChannelStatus.BLOCKED);
+                                    }
+                                }
+                                return null; // Method is void
+                            }
+                        })
+                .when(mSiteChannelsManager)
+                .getChannelStatusAsync(eq(CHANNEL_ID), any(Callback.class));
     }
 
     private void setPreInstallNotificationPermission(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedMediatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedMediatorUnitTest.java
index 88642b5f..e2768139 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedMediatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedMediatorUnitTest.java
@@ -352,6 +352,7 @@
     private void createMediator(boolean isTablet) {
         mMvTilesLayout = Mockito.mock(MostVisitedTilesLayout.class);
 
+        when(mMvTilesLayout.getResources()).thenReturn(mResources);
         when(mMvTilesLayout.getChildCount()).thenReturn(1);
         when(mMvTilesLayout.getChildAt(0)).thenReturn(mTileView);
         when(mMvTilesLayout.getTileCount()).thenReturn(1);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileInteractionDelegateTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileInteractionDelegateTest.java
index e7dbb66..86d70be3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileInteractionDelegateTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileInteractionDelegateTest.java
@@ -53,6 +53,7 @@
                 SuggestionsUiDelegate uiDelegate,
                 ContextMenuManager contextMenuManager,
                 Delegate tileGroupDelegate,
+                TileDragDelegate tileDragDelegate,
                 Observer observer,
                 OfflinePageBridge offlinePageBridge) {
             super(
@@ -60,6 +61,7 @@
                     uiDelegate,
                     contextMenuManager,
                     tileGroupDelegate,
+                    tileDragDelegate,
                     observer,
                     offlinePageBridge);
         }
@@ -81,6 +83,7 @@
     @Mock SuggestionsUiDelegate mSuggestionsUiDelegate;
     @Mock ContextMenuManager mContextMenuManager;
     @Mock TileGroup.Delegate mTileGroupDelegate;
+    @Mock TileGroup.TileDragDelegate mTileDragDelegate;
     @Mock OfflinePageBridge mOfflinePageBridge;
     @Mock private Runnable mSnapshotTileGridChangedRunnable;
     @Mock private Runnable mTileCountChangedRunnable;
@@ -118,6 +121,7 @@
                         mSuggestionsUiDelegate,
                         mContextMenuManager,
                         mTileGroupDelegate,
+                        mTileDragDelegate,
                         mTileGroupObserver,
                         mOfflinePageBridge);
         tileGroup.onIconMadeAvailable(new GURL("https://example.com"));
@@ -150,6 +154,7 @@
                         mSuggestionsUiDelegate,
                         mContextMenuManager,
                         mTileGroupDelegate,
+                        mTileDragDelegate,
                         mTileGroupObserver,
                         mOfflinePageBridge);
         tileGroup.onIconMadeAvailable(new GURL("https://example.com"));
@@ -173,6 +178,7 @@
                         mSuggestionsUiDelegate,
                         mContextMenuManager,
                         mTileGroupDelegate,
+                        mTileDragDelegate,
                         mTileGroupObserver,
                         mOfflinePageBridge);
         tileGroup.setTileForTesting(mTile);
diff --git a/chrome/android/webapk/shell_apk/BUILD.gn b/chrome/android/webapk/shell_apk/BUILD.gn
index d3e6024a1..431938c 100644
--- a/chrome/android/webapk/shell_apk/BUILD.gn
+++ b/chrome/android/webapk/shell_apk/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/config/android/rules.gni")
-import("//build/config/locales.gni")
 import("//tools/grit/grit_rule.gni")
 import("current_version/current_version.gni")
 import("generate_manifest_for_upload_outputs.gni")
@@ -284,11 +283,6 @@
 
 java_strings_grd("webapk_strings_grd") {
   grd_file = "//chrome/android/webapk/strings/android_webapk_strings.grd"
-
-  outputs = [ "values/android_webapk_strings.xml" ] +
-            process_file_template(
-                android_bundle_locales_as_resources,
-                [ "values-{{source_name_part}}/android_webapk_strings.xml" ])
 }
 
 # Template for WebAPK. When a WebAPK is generated:
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index bcd29bff..476d661 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -82,20 +82,6 @@
   }
 }
 
-if (is_android) {
-  android_generated_java_resources =
-      [ "java/res/values/generated_resources.xml" ] +
-      process_file_template(
-          android_bundle_locales_as_resources,
-          [ "java/res/values-{{source_name_part}}/generated_resources.xml" ])
-
-  strings_java_resources =
-      [ "java/res/values/branded_strings.xml" ] +
-      process_file_template(
-          android_bundle_locales_as_resources,
-          [ "java/res/values-{{source_name_part}}/branded_strings.xml" ])
-}
-
 grit("generated_resources") {
   source = "generated_resources.grd"
   defines = chrome_grit_defines + [ "is_cfm=${is_cfm}" ]
@@ -105,19 +91,21 @@
       process_file_template(all_chrome_locales,
                             [ "generated_resources_{{source_name_part}}.pak" ])
   if (is_android) {
-    outputs += android_generated_java_resources
+    create_android_resources = true
   }
 }
 
 if (is_android) {
-  java_strings_grd_prebuilt("java_strings_grd") {
-    grit_output_dir = "$root_gen_dir/chrome/java/res"
-    generated_files =
-        rebase_path(android_generated_java_resources, "java/res", ".") +
-        rebase_path(strings_java_resources, "java/res", ".")
-    deps = [
-      ":branded_strings",
-      ":generated_resources",
+  java_strings_grd_prebuilt("branded_resources_grd") {
+    generating_target = ":branded_strings"
+  }
+  java_strings_grd_prebuilt("generated_resources_grd") {
+    generating_target = ":generated_resources"
+  }
+  java_group("java_strings_grd") {
+    public_deps = [
+      ":branded_resources_grd",
+      ":generated_resources_grd",
     ]
   }
 }
@@ -132,7 +120,7 @@
                             [ "branded_strings_{{source_name_part}}.pak" ])
 
   if (is_android) {
-    outputs += strings_java_resources
+    create_android_resources = true
   }
 }
 
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 28c343d..7868e98 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -1013,17 +1013,6 @@
         </message>
       </if>
 
-      <!-- Installer Downloader infobar -->
-      <message name="IDS_INSTALLER_DOWNLOADER_DISCLAIMER" desc=" Installer downloader infobar message for downloading the Chromium installer." translateable="false">
-        Download the Chromium Installer.
-      </message>
-      <message name="IDS_INSTALLER_DOWNLOADER_LINK_TEXT" desc="Link text for the installer downloader infobar, 'Learn More'."  translateable="false">
-        Learn More
-      </message>
-      <message name="IDS_INSTALLER_DOWNLOADER_BUTTON_LABEL" desc="Button label for the installer downloader infobar to download the Chromium installer."  translateable="false">
-        Download Chromium Installer
-      </message>
-
       <!-- Enterprise sign-in dialog -->
       <message name="IDS_ENTERPRISE_VALUE_PROPOSITION_PROFILE_REQUIRED_BY_ORG_TITLE" desc="Title of the screen in the profile creation flow where the user is asked whether they want to confirm signing in to Chromium with their enterprise account when it is mandatory to sign in to Chromium with the enterprise account.">
         Your organization requires you to sign into Chromium
diff --git a/chrome/app/profiles_strings.grdp b/chrome/app/profiles_strings.grdp
index 34a472f..436531fc 100644
--- a/chrome/app/profiles_strings.grdp
+++ b/chrome/app/profiles_strings.grdp
@@ -1025,21 +1025,28 @@
       other {{BOOKMARK_COUNT} bookmarks and other items are saved only to this device. To use them on your other devices, save them in your Google Account.}
       }
     </message>
-    <message name="IDS_BATCH_UPLOAD_SECTION_TITLE_PASSWORDS" desc="Title of the Passwords section in the Batch Upload dialog. This dialog appears when the user clicks on an informational message about items saved only to their device. The title of the dialog is ‘Save items in account’. The list of items has three sections: Passwords, Bookmarks, and Addresses. This section shows the number of selected passwords in the section of the dialog if any.">
+    <message name="IDS_BATCH_UPLOAD_SECTION_TITLE_PASSWORDS" desc="Title of the Passwords section in the Batch Upload dialog. This dialog appears when the user clicks on an informational message about items saved only to their device. The title of the dialog is ‘Save items in account’. The list of items has sections for each applicable data type: Passwords, Bookmarks, and more. This section shows the number of selected items in the section of the dialog, if any.">
       {SELECTED_ITEMS, plural,
       =0 {Passwords}
       =1 {Passwords ({SELECTED_ITEMS})}
       other {Passwords ({SELECTED_ITEMS})}
       }
     </message>
-    <message name="IDS_BATCH_UPLOAD_SECTION_TITLE_BOOKMARKS" desc="Title of the Bookmarks section in the Batch Upload dialog. This dialog appears when the user clicks on an informational message about items saved only to their device. The title of the dialog is ‘Save items in account’. The list of items has three sections: Passwords, Bookmarks, and Addresses. This section shows the number of selected bookmarks in the section of the dialog if any.">
+    <message name="IDS_BATCH_UPLOAD_SECTION_TITLE_BOOKMARKS" desc="Title of the Bookmarks section in the Batch Upload dialog. This dialog appears when the user clicks on an informational message about items saved only to their device. The title of the dialog is ‘Save items in account’. The list of items has sections for each applicable data type: Passwords, Bookmarks, and more. This section shows the number of selected items in the section of the dialog, if any.">
       {SELECTED_ITEMS, plural,
       =0 {Bookmarks}
       =1 {Bookmarks ({SELECTED_ITEMS})}
       other {Bookmarks ({SELECTED_ITEMS})}
       }
     </message>
-    <message name="IDS_BATCH_UPLOAD_SECTION_TITLE_ADDRESSES" desc="Title of the Addresses section in the Batch Upload dialog. This dialog appears when the user clicks on an informational message about items saved only to their device. The title of the dialog is ‘Save items in account’. The list of items has three sections: Passwords, Bookmarks, and Addresses. This section shows the number of selected addresses in the section of the dialog if any.">
+    <message name="IDS_BATCH_UPLOAD_SECTION_TITLE_READING_LIST" desc="Title of the Reading list section in the Batch Upload dialog. This dialog appears when the user clicks on an informational message about items saved only to their device. The title of the dialog is ‘Save items in account’. The list of items has sections for each applicable data type: Passwords, Bookmarks, and more. This section shows the number of selected items in the section of the dialog, if any.">
+      {SELECTED_ITEMS, plural,
+      =0 {Reading list}
+      =1 {Reading list ({SELECTED_ITEMS})}
+      other {Reading list ({SELECTED_ITEMS})}
+      }
+    </message>
+    <message name="IDS_BATCH_UPLOAD_SECTION_TITLE_ADDRESSES" desc="Title of the Addresses section in the Batch Upload dialog. This dialog appears when the user clicks on an informational message about items saved only to their device. The title of the dialog is ‘Save items in account’. The list of items has sections for each applicable data type: Passwords, Bookmarks, and more. This section shows the number of selected items in the section of the dialog, if any.">
       {SELECTED_ITEMS, plural,
       =0 {Addresses}
       =1 {Addresses ({SELECTED_ITEMS})}
diff --git a/chrome/app/profiles_strings_grdp/IDS_BATCH_UPLOAD_SECTION_TITLE_READING_LIST.png.sha1 b/chrome/app/profiles_strings_grdp/IDS_BATCH_UPLOAD_SECTION_TITLE_READING_LIST.png.sha1
new file mode 100644
index 0000000..fac6113
--- /dev/null
+++ b/chrome/app/profiles_strings_grdp/IDS_BATCH_UPLOAD_SECTION_TITLE_READING_LIST.png.sha1
@@ -0,0 +1 @@
+fa546078bc537e6d11cb4d7b18c1e2367c6a6715
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 7be63e23..68adbda6 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -750,6 +750,18 @@
     "metrics/thread_watcher_report_hang.h",
     "metrics/ukm_background_recorder_service.cc",
     "metrics/ukm_background_recorder_service.h",
+    "metrics/usage_scenario/system_event_provider.cc",
+    "metrics/usage_scenario/system_event_provider.h",
+    "metrics/usage_scenario/tab_usage_scenario_tracker.cc",
+    "metrics/usage_scenario/tab_usage_scenario_tracker.h",
+    "metrics/usage_scenario/usage_scenario.cc",
+    "metrics/usage_scenario/usage_scenario.h",
+    "metrics/usage_scenario/usage_scenario_data_store.cc",
+    "metrics/usage_scenario/usage_scenario_data_store.h",
+    "metrics/usage_scenario/usage_scenario_tracker.cc",
+    "metrics/usage_scenario/usage_scenario_tracker.h",
+    "metrics/usage_scenario/video_capture_event_provider.cc",
+    "metrics/usage_scenario/video_capture_event_provider.h",
     "metrics/variations/chrome_variations_service_client.cc",
     "metrics/variations/chrome_variations_service_client.h",
     "metrics/variations/google_groups_manager_factory.cc",
@@ -1034,6 +1046,10 @@
     "performance_manager/metrics/metrics_provider_common.h",
     "performance_manager/observers/page_load_metrics_observer.cc",
     "performance_manager/observers/page_load_metrics_observer.h",
+    "performance_manager/persistence/site_data/site_data_cache_facade.cc",
+    "performance_manager/persistence/site_data/site_data_cache_facade.h",
+    "performance_manager/persistence/site_data/site_data_cache_facade_factory.cc",
+    "performance_manager/persistence/site_data/site_data_cache_facade_factory.h",
     "performance_manager/policies/discard_eligibility_policy.cc",
     "performance_manager/policies/discard_eligibility_policy.h",
     "performance_manager/policies/frame_throttling_policy.cc",
@@ -2038,6 +2054,7 @@
     "//chrome/browser/ui/tab_contents",
     "//chrome/browser/ui/tab_contents:impl",
     "//chrome/browser/ui/tabs:tabs_public",
+    "//chrome/browser/ui/tabs/tab_group_home:constants",
     "//chrome/browser/ui/webui",
     "//chrome/browser/ui/webui:configs",
     "//chrome/browser/ui/webui/bluetooth_internals",
@@ -3883,18 +3900,6 @@
       "metrics/power/process_monitor.h",
       "metrics/usage_scenario/chrome_responsiveness_calculator_delegate.cc",
       "metrics/usage_scenario/chrome_responsiveness_calculator_delegate.h",
-      "metrics/usage_scenario/system_event_provider.cc",
-      "metrics/usage_scenario/system_event_provider.h",
-      "metrics/usage_scenario/tab_usage_scenario_tracker.cc",
-      "metrics/usage_scenario/tab_usage_scenario_tracker.h",
-      "metrics/usage_scenario/usage_scenario.cc",
-      "metrics/usage_scenario/usage_scenario.h",
-      "metrics/usage_scenario/usage_scenario_data_store.cc",
-      "metrics/usage_scenario/usage_scenario_data_store.h",
-      "metrics/usage_scenario/usage_scenario_tracker.cc",
-      "metrics/usage_scenario/usage_scenario_tracker.h",
-      "metrics/usage_scenario/video_capture_event_provider.cc",
-      "metrics/usage_scenario/video_capture_event_provider.h",
       "new_tab_page/chrome_colors/chrome_colors_factory.cc",
       "new_tab_page/chrome_colors/chrome_colors_factory.h",
       "new_tab_page/chrome_colors/chrome_colors_service.cc",
@@ -3993,10 +3998,6 @@
       "performance_manager/mechanisms/page_loader.h",
       "performance_manager/metrics/metrics_provider_desktop.cc",
       "performance_manager/metrics/metrics_provider_desktop.h",
-      "performance_manager/persistence/site_data/site_data_cache_facade.cc",
-      "performance_manager/persistence/site_data/site_data_cache_facade.h",
-      "performance_manager/persistence/site_data/site_data_cache_facade_factory.cc",
-      "performance_manager/persistence/site_data/site_data_cache_facade_factory.h",
       "performance_manager/policies/background_tab_loading_policy.cc",
       "performance_manager/policies/background_tab_loading_policy.h",
       "performance_manager/policies/background_tab_loading_policy_helpers.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 1506274..119e0f4e 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -64,6 +64,7 @@
 #include "chrome/browser/task_manager/common/task_manager_features.h"
 #include "chrome/browser/tpcd/experiment/tpcd_experiment_features.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/browser/ui/tabs/tab_group_home/constants.h"
 #include "chrome/browser/ui/toasts/toast_features.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/unexpire_flags.h"
@@ -3276,9 +3277,6 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 // Feature variations for kIsolateSandboxedIframes.
-#if !BUILDFLAG(IS_ANDROID)
-// TODO(wjmaclean): Add FeatureParams for a per-frame grouping when support
-// for it is added.
 const FeatureEntry::FeatureParam kIsolateSandboxedIframesGroupingPerSite{
     "grouping", "per-site"};
 const FeatureEntry::FeatureParam kIsolateSandboxedIframesGroupingPerOrigin{
@@ -3294,7 +3292,6 @@
         {"with each sandboxed frame document in its own process",
          &kIsolateSandboxedIframesGroupingPerDocument, 1, nullptr},
 };
-#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Feature variation for kPdfInk2.
 #if BUILDFLAG(ENABLE_PDF_INK2)
@@ -9694,15 +9691,13 @@
      flag_descriptions::kEnableFakeMouseHeuristicDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ui::kEnableFakeMouseHeuristic)},
 #endif  // BUILDFLAG(IS_CHROMEOS)
-#if !BUILDFLAG(IS_ANDROID)
     {"enable-isolated-sandboxed-iframes",
      flag_descriptions::kIsolatedSandboxedIframesName,
-     flag_descriptions::kIsolatedSandboxedIframesDescription, kOsDesktop,
+     flag_descriptions::kIsolatedSandboxedIframesDescription, kOsAll,
      FEATURE_WITH_PARAMS_VALUE_TYPE(
          blink::features::kIsolateSandboxedIframes,
          kIsolateSandboxedIframesGroupingVariations,
          "IsolateSandboxedIframes" /* trial name */)},
-#endif
 
     {"reduce-accept-language", flag_descriptions::kReduceAcceptLanguageName,
      flag_descriptions::kReduceAcceptLanguageDescription, kOsAll,
@@ -11315,6 +11310,11 @@
      flag_descriptions::kHistorySyncAlternativeIllustrationDescription, kOsAll,
      FEATURE_VALUE_TYPE(tab_groups::kUseAlternateHistorySyncIllustration)},
 
+    {"left-click-opens-tab-group-bubble",
+     flag_descriptions::kLeftClickOpensTabGroupBubbleName,
+     flag_descriptions::kLeftClickOpensTabGroupBubbleDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(tab_groups::kLeftClickOpensTabGroupBubble)},
+
 #if BUILDFLAG(IS_CHROMEOS)
     {"cros-content-adjusted-refresh-rate",
      flag_descriptions::kCrosContentAdjustedRefreshRateName,
@@ -12419,6 +12419,14 @@
      FEATURE_VALUE_TYPE(password_manager::features::kAndroidSmsOtpFilling)},
 #endif  // BUILDFLAG(IS_ANDROID)
 
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \
+    BUILDFLAG(IS_CHROMEOS)
+    {"tab-group-home", tabs::flag_descriptions::kTabGroupHomeName,
+     tabs::flag_descriptions::kTabGroupHomeDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(tabs::kTabGroupHome)},
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) ||
+        // BUILDFLAG(IS_CHROME_OS)
+
     // Add new entries above this line.
 
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
diff --git a/chrome/browser/ai/ai_data_keyed_service.cc b/chrome/browser/ai/ai_data_keyed_service.cc
index 98b9b87..bddda9db 100644
--- a/chrome/browser/ai/ai_data_keyed_service.cc
+++ b/chrome/browser/ai/ai_data_keyed_service.cc
@@ -143,6 +143,7 @@
 
   auto options = optimization_guide::DefaultAIPageContentOptions();
   options->enable_experimental_actionable_data = true;
+  options->include_geometry = true;
   optimization_guide::OnAIPageContentDone callback = base::BindOnce(
       &OnGotAIPageContentWithActionableElementsForModelPrototyping,
       std::move(continue_callback));
diff --git a/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java b/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java
index a308288..8204f5a7 100644
--- a/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java
+++ b/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java
@@ -967,9 +967,12 @@
             editor.putBoolean(
                     SHARED_PREF_ENGAGEMENT_SIGNALS_BUTTON, mEngagementSignalsButton.isEnabled());
             editor.putBoolean(SHARED_PREF_SEARCH_IN_CCT, mSearchInCctCheckbox.isChecked());
-            editor.putBoolean(
-                    SHARED_PREF_OPEN_IN_BROWSER_BUTTON, mOpenInBrowserButtonCheckbox.isChecked());
-            editor.putBoolean(SHARED_PREF_EPHEMERAL_BROWSING, mEphemeralCctCheckbox.isChecked());
+            editor.putInt(
+                    SHARED_PREF_OPEN_IN_BROWSER_BUTTON,
+                    mOpenInBrowserButtonCheckbox.isChecked() ? CHECKED : UNCHECKED);
+            editor.putInt(
+                    SHARED_PREF_EPHEMERAL_BROWSING,
+                    mEphemeralCctCheckbox.isChecked() ? CHECKED : UNCHECKED);
             editor.apply();
         }
         super.onDestroy();
diff --git a/chrome/browser/android/ntp/most_visited_sites_bridge.cc b/chrome/browser/android/ntp/most_visited_sites_bridge.cc
index 29713585..66c37ab 100644
--- a/chrome/browser/android/ntp/most_visited_sites_bridge.cc
+++ b/chrome/browser/android/ntp/most_visited_sites_bridge.cc
@@ -241,6 +241,12 @@
   return most_visited_->HasCustomLink(key_url);
 }
 
+jboolean MostVisitedSitesBridge::ReorderCustomLink(JNIEnv* env,
+                                                   const GURL& key_url,
+                                                   jint new_pos) {
+  return most_visited_->ReorderCustomLink(key_url, new_pos);
+}
+
 void MostVisitedSitesBridge::AddOrRemoveBlockedUrl(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
diff --git a/chrome/browser/android/ntp/most_visited_sites_bridge.h b/chrome/browser/android/ntp/most_visited_sites_bridge.h
index c41b140d..c30ec0ac 100644
--- a/chrome/browser/android/ntp/most_visited_sites_bridge.h
+++ b/chrome/browser/android/ntp/most_visited_sites_bridge.h
@@ -55,6 +55,8 @@
 
   jboolean HasCustomLink(JNIEnv* env, const GURL& key_url);
 
+  jboolean ReorderCustomLink(JNIEnv* env, const GURL& key_url, jint new_pos);
+
   void AddOrRemoveBlockedUrl(JNIEnv* env,
                              const base::android::JavaParamRef<jobject>& obj,
                              const base::android::JavaParamRef<jobject>& j_url,
diff --git a/chrome/browser/ash/arc/input_overlay/ui/delete_edit_shortcut.cc b/chrome/browser/ash/arc/input_overlay/ui/delete_edit_shortcut.cc
index e7bdea7..1da39182 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/delete_edit_shortcut.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/delete_edit_shortcut.cc
@@ -22,6 +22,7 @@
 #include "ui/chromeos/styles/cros_tokens_color_mappings.h"
 #include "ui/color/color_provider.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/bubble/bubble_border.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
@@ -147,7 +148,7 @@
       std::make_unique<views::BubbleBorder>(arrow(), GetShadow());
   bubble_border->SetColor(background_color());
   if (GetParams().round_corners) {
-    bubble_border->SetCornerRadius(GetCornerRadius());
+    bubble_border->set_rounded_corners(gfx::RoundedCornersF(GetCornerRadius()));
   }
   bubble_border->set_avoid_shadow_overlap(true);
   bubble_border->set_insets(
diff --git a/chrome/browser/ash/arc/nearby_share/ui/progress_bar_dialog_view.cc b/chrome/browser/ash/arc/nearby_share/ui/progress_bar_dialog_view.cc
index 5ed9063bb..e0d4c23 100644
--- a/chrome/browser/ash/arc/nearby_share/ui/progress_bar_dialog_view.cc
+++ b/chrome/browser/ash/arc/nearby_share/ui/progress_bar_dialog_view.cc
@@ -14,6 +14,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/color_palette.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/bubble/bubble_border.h"
@@ -44,7 +45,7 @@
   auto border = std::make_unique<views::BubbleBorder>(
       views::BubbleBorder::NONE, views::BubbleBorder::STANDARD_SHADOW);
   border->SetColor(ash::kColorAshDialogBackgroundColor);
-  border->SetCornerRadius(kCornerRadius);
+  border->set_rounded_corners(gfx::RoundedCornersF(kCornerRadius));
   SetBackground(std::make_unique<views::BubbleBackground>(border.get()));
   SetBorder(std::move(border));
 
diff --git a/chrome/browser/ash/login/misconfigured_user_browsertest.cc b/chrome/browser/ash/login/misconfigured_user_browsertest.cc
index e577440a..7cf76d3 100644
--- a/chrome/browser/ash/login/misconfigured_user_browsertest.cc
+++ b/chrome/browser/ash/login/misconfigured_user_browsertest.cc
@@ -11,11 +11,11 @@
 #include "base/test/test_future.h"
 #include "base/values.h"
 #include "chrome/browser/ash/login/auth/chrome_safe_mode_delegate.h"
-#include "chrome/browser/ash/login/login_manager_test.h"
 #include "chrome/browser/ash/login/quick_unlock/quick_unlock_factory.h"
 #include "chrome/browser/ash/login/session/user_session_manager.h"
 #include "chrome/browser/ash/login/session/user_session_manager_test_api.h"
 #include "chrome/browser/ash/login/test/cryptohome_mixin.h"
+#include "chrome/browser/ash/login/test/embedded_test_server_setup_mixin.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
@@ -26,6 +26,7 @@
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
+#include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
 #include "chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h"
 #include "chromeos/ash/components/login/auth/auth_session_authenticator.h"
@@ -40,6 +41,7 @@
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/test/browser_test.h"
+#include "net/dns/mock_host_resolver.h"
 
 namespace ash {
 
@@ -51,51 +53,15 @@
 constexpr char kNewUserPassword[] = "password";
 constexpr char kNewUserEmail[] = "new-user@gmail.com";
 
-// TODO(b/315827147): After Local Passwords are launched it would be
-// possible to rewrite this test in a way without callback injection,
-// as getting right timing could be performed via Screens interaction.
-class FakeAuthSessionAuthenticator : public AuthSessionAuthenticator {
- public:
-  FakeAuthSessionAuthenticator(
-      AuthStatusConsumer* consumer,
-      std::unique_ptr<SafeModeDelegate> safe_mode_delegate,
-      base::RepeatingCallback<void(const AccountId&)> user_recorder,
-      PrefService* local_state,
-      bool new_user_can_become_owner,
-      base::OnceClosure on_record_auth_factor_added)
-      : AuthSessionAuthenticator(consumer,
-                                 std::move(safe_mode_delegate),
-                                 user_recorder,
-                                 new_user_can_become_owner,
-                                 local_state) {
-    on_record_auth_factor_added_ = std::move(on_record_auth_factor_added);
-  }
-
-  FakeAuthSessionAuthenticator(const FakeAuthSessionAuthenticator&) = delete;
-  FakeAuthSessionAuthenticator& operator=(const FakeAuthSessionAuthenticator&) =
-      delete;
-
- protected:
-  ~FakeAuthSessionAuthenticator() override = default;
-
- private:
-  // AuthSessionAuthenticator
-  void RecordFirstAuthFactorAdded(std::unique_ptr<UserContext> context,
-                                  AuthOperationCallback callback) override {
-    if (on_record_auth_factor_added_) {
-      std::move(on_record_auth_factor_added_).Run();
-    }
-  }
-
-  base::OnceClosure on_record_auth_factor_added_;
-};
-
 }  // namespace
 
-class MisconfiguredOwnerUserTest : public LoginManagerTest {
+class MisconfiguredOwnerUserTest : public MixinBasedInProcessBrowserTest {
  public:
-  explicit MisconfiguredOwnerUserTest(bool new_user_can_become_owner = true)
-      : new_user_can_become_owner_(new_user_can_become_owner) {}
+  void SetUpInProcessBrowserTestFixture() override {
+    MixinBasedInProcessBrowserTest::SetUpInProcessBrowserTestFixture();
+    // Forward account verification to the embedded server.
+    host_resolver()->AddRule("*", "127.0.0.1");
+  }
 
   void SetUpOnMainThread() override {
     add_auth_factor_waiter_ = std::make_unique<base::test::TestFuture<void>>();
@@ -107,7 +73,7 @@
         add_auth_factor_waiter_->GetCallback());
     test_api.SetSkipUserIntegrityNotification(true);
 
-    LoginManagerTest::SetUpOnMainThread();
+    MixinBasedInProcessBrowserTest::SetUpOnMainThread();
   }
 
  protected:
@@ -128,7 +94,8 @@
                                   FakeGaiaMixin::kEmptyUserServices);
   }
 
-  AccountId test_account_id_;
+  EmbeddedTestServerSetupMixin embedded_test_server_{&mixin_host_,
+                                                     embedded_test_server()};
   CryptohomeMixin cryptohome_mixin_{&mixin_host_};
   LoginManagerMixin login_manager_{&mixin_host_,
                                    {},
@@ -136,7 +103,6 @@
                                    &cryptohome_mixin_};
   FakeGaiaMixin fake_gaia_mixin_{&mixin_host_};
   std::unique_ptr<base::test::TestFuture<void>> add_auth_factor_waiter_;
-  bool new_user_can_become_owner_;
 };
 
 IN_PROC_BROWSER_TEST_F(MisconfiguredOwnerUserTest,
@@ -154,7 +120,7 @@
 
 class MisconfiguredUserTest : public MisconfiguredOwnerUserTest {
  public:
-  MisconfiguredUserTest() : MisconfiguredOwnerUserTest(false) {
+  MisconfiguredUserTest() {
     login_manager_.AppendRegularUsers(1);
     test_account_id_ = login_manager_.users().front().account_id;
     scoped_testing_cros_settings_.device_settings()->Set(
@@ -168,6 +134,7 @@
     MisconfiguredOwnerUserTest::InitiateUserCreation(email, password);
   }
 
+  AccountId test_account_id_;
   ash::ScopedTestingCrosSettings scoped_testing_cros_settings_;
 };
 
diff --git a/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.cc b/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.cc
index b2c5070..7a5e5a23 100644
--- a/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.cc
+++ b/chrome/browser/ash/login/quick_unlock/pin_storage_cryptohome.cc
@@ -8,6 +8,7 @@
 
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/task/single_thread_task_runner.h"
@@ -312,6 +313,8 @@
     std::optional<AuthenticationError> error) {
   if (error.has_value()) {
     LOG(ERROR) << "Failed to edit pin, code " << error->get_cryptohome_error();
+    // TODO(b:374099765): Remove this once the bug is fixed.
+    base::debug::DumpWithoutCrashing();
     std::move(callback).Run(std::move(user_context), std::move(error));
     return;
   }
diff --git a/chrome/browser/ash/login/saml/in_session_password_change_manager.cc b/chrome/browser/ash/login/saml/in_session_password_change_manager.cc
index 540d1f8f..9249246 100644
--- a/chrome/browser/ash/login/saml/in_session_password_change_manager.cc
+++ b/chrome/browser/ash/login/saml/in_session_password_change_manager.cc
@@ -9,10 +9,8 @@
 #include <string>
 #include <utility>
 
-#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/session/session_controller.h"
 #include "base/check.h"
-#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/location.h"
 #include "base/logging.h"
@@ -28,7 +26,6 @@
 #include "chrome/browser/browser_process_platform_part_ash.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/ash/in_session_password_change/password_change_dialogs.h"
-#include "chrome/common/chrome_features.h"
 #include "chromeos/ash/components/login/auth/auth_session_authenticator.h"
 #include "chromeos/ash/components/login/auth/public/authentication_error.h"
 #include "chromeos/ash/components/login/auth/public/key.h"
@@ -171,8 +168,7 @@
 // static
 std::unique_ptr<InSessionPasswordChangeManager>
 InSessionPasswordChangeManager::CreateIfEnabled(Profile* primary_profile) {
-  if (base::FeatureList::IsEnabled(::features::kInSessionPasswordChange) &&
-      primary_profile->GetPrefs()->GetBoolean(
+  if (primary_profile->GetPrefs()->GetBoolean(
           prefs::kSamlInSessionPasswordChangeEnabled)) {
     std::unique_ptr<InSessionPasswordChangeManager> manager =
         std::make_unique<InSessionPasswordChangeManager>(primary_profile);
diff --git a/chrome/browser/ash/login/saml/in_session_password_change_manager.h b/chrome/browser/ash/login/saml/in_session_password_change_manager.h
index 4c00fb0..f3df6b2 100644
--- a/chrome/browser/ash/login/saml/in_session_password_change_manager.h
+++ b/chrome/browser/ash/login/saml/in_session_password_change_manager.h
@@ -63,8 +63,7 @@
 // response from dialogs, and callbacks from subsystems.
 // This singleton is scoped to the primary user session - it will exist for as
 // long as the primary user session exists  (but only if the primary user's
-// InSessionPasswordChange policy is enabled and the kInSessionPasswordChange
-// feature is enabled).
+// InSessionPasswordChange policy is enabled).
 class InSessionPasswordChangeManager
     : public SessionActivationObserver,
       public PasswordSyncTokenFetcher::Consumer {
diff --git a/chrome/browser/ash/login/session/user_session_manager.cc b/chrome/browser/ash/login/session/user_session_manager.cc
index b3fca30..899af06 100644
--- a/chrome/browser/ash/login/session/user_session_manager.cc
+++ b/chrome/browser/ash/login/session/user_session_manager.cc
@@ -1676,19 +1676,14 @@
       }
     }
 
-    const bool in_session_password_change_feature_enabled =
-        base::FeatureList::IsEnabled(::features::kInSessionPasswordChange);
-
-    if (in_session_password_change_feature_enabled &&
-        user_context_.GetSamlPasswordAttributes().has_value()) {
+    if (user_context_.GetSamlPasswordAttributes().has_value()) {
       // Update password expiry data if new data came in during SAML login,
       // and the in-session password change feature is enabled:
       user_context_.GetSamlPasswordAttributes()->SaveToPrefs(
           profile->GetPrefs());
 
-    } else if (!in_session_password_change_feature_enabled ||
-               user_context_.GetAuthFlow() ==
-                   UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML) {
+    } else if (user_context_.GetAuthFlow() ==
+               UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML) {
       // These attributes are no longer relevant and should be deleted if
       // either a) the in-session password change feature is no longer enabled
       // or b) this user is no longer using SAML to log in.
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/TabBookmarker.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/TabBookmarker.java
index af80a3b..49a90b4 100644
--- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/TabBookmarker.java
+++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/TabBookmarker.java
@@ -113,7 +113,7 @@
 
     private void addOrEditBookmark(
             final Tab tabToBookmark, @BookmarkType int bookmarkType, boolean fromExplicitTrackUi) {
-        if (tabToBookmark == null || tabToBookmark.isFrozen()) {
+        if (tabToBookmark == null) {
             return;
         }
 
diff --git a/chrome/browser/chrome_browser_interface_binders_webui.cc b/chrome/browser/chrome_browser_interface_binders_webui.cc
index 1bd930e0..8621130 100644
--- a/chrome/browser/chrome_browser_interface_binders_webui.cc
+++ b/chrome/browser/chrome_browser_interface_binders_webui.cc
@@ -522,7 +522,8 @@
       NewTabPageUI, OmniboxPopupUI, BookmarksSidePanelUI, CustomizeChromeUI,
       UserEducationInternalsUI, ReadingListUI, TabSearchUI, WebuiGalleryUI,
       HistoryClustersSidePanelUI, ShoppingInsightsSidePanelUI,
-      media_router::AccessCodeCastUI, commerce::ProductSpecificationsUI>(map);
+      media_router::AccessCodeCastUI, commerce::ProductSpecificationsUI,
+      NewTabFooterUI>(map);
 
   RegisterWebUIControllerInterfaceBinder<
       new_tab_page::mojom::PageHandlerFactory, NewTabPageUI>(map);
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 0a2664d..e360fc26 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -1668,6 +1668,8 @@
   registry->RegisterBooleanPref(prefs::kReduceAcceptLanguageEnabled, true);
   registry->RegisterBooleanPref(policy::policy_prefs::kBuiltInAIAPIsEnabled,
                                 true);
+  registry->RegisterBooleanPref(
+      prefs::kClearWindowNameForNewBrowsingContextGroup, true);
 }
 
 // static
@@ -8599,13 +8601,14 @@
     const GURL& url,
     const net::SiteForCookies& site_for_cookies,
     const url::Origin& top_frame_origin,
-    const net::CookieSettingOverrides overrides) {
+    const net::CookieSettingOverrides overrides,
+    base::optional_ref<const net::CookiePartitionKey> cookie_partition_key) {
   scoped_refptr<content_settings::CookieSettings> cookie_settings =
       CookieSettingsFactory::GetForProfile(
           Profile::FromBrowserContext(&browser_context));
   CHECK(cookie_settings);
   return cookie_settings->IsFullCookieAccessAllowed(
-      url, site_for_cookies, top_frame_origin, overrides);
+      url, site_for_cookies, top_frame_origin, overrides, cookie_partition_key);
 }
 
 void ChromeContentBrowserClient::GetCloudIdentifiers(
@@ -8678,6 +8681,23 @@
   return true;
 }
 
+// TODO(crbug.com/40133611): ladan@ - Remove this method and its refrencesonce
+// ClearWindowNameForNewBrowsingContextGroup policy is removed.
+bool ChromeContentBrowserClient::
+    IsClearWindowNameForNewBrowsingContextGroupAllowed(
+        content::BrowserContext* browser_context) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  const PrefService::Preference* pref =
+      Profile::FromBrowserContext(browser_context)
+          ->GetPrefs()
+          ->FindPreference(prefs::kClearWindowNameForNewBrowsingContextGroup);
+
+  if (pref != nullptr && pref->IsManaged() && pref->GetValue()->is_bool()) {
+    return pref->GetValue()->GetBool();
+  }
+  return true;
+}
+
 void ChromeContentBrowserClient::SetIsMinimalMode(bool minimal) {
   is_minimal_mode_ = minimal;
 }
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 4d28838..47013a8 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -1055,7 +1055,9 @@
       const GURL& url,
       const net::SiteForCookies& site_for_cookies,
       const url::Origin& top_frame_origin,
-      const net::CookieSettingOverrides overrides) override;
+      const net::CookieSettingOverrides overrides,
+      base::optional_ref<const net::CookiePartitionKey> cookie_partition_key)
+      override;
 
   void GetCloudIdentifiers(
       const storage::FileSystemURL& url,
@@ -1071,6 +1073,9 @@
   bool ShouldReduceAcceptLanguage(
       content::BrowserContext* browser_context) override;
 
+  bool IsClearWindowNameForNewBrowsingContextGroupAllowed(
+      content::BrowserContext* browser_context) override;
+
   void SetIsMinimalMode(bool minimal) override;
 
   bool UseOutermostMainFrameOrEmbedderForSubCaptureTargets() const override;
diff --git a/chrome/browser/chromeos/extensions/smart_card_provider_private/smart_card_provider_private_api.cc b/chrome/browser/chromeos/extensions/smart_card_provider_private/smart_card_provider_private_api.cc
index c88f5c2..0a4d7c35 100644
--- a/chrome/browser/chromeos/extensions/smart_card_provider_private/smart_card_provider_private_api.cc
+++ b/chrome/browser/chromeos/extensions/smart_card_provider_private/smart_card_provider_private_api.cc
@@ -7,6 +7,7 @@
 #include <queue>
 #include <variant>
 
+#include "base/functional/bind.h"
 #include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "base/timer/timer.h"
@@ -380,6 +381,10 @@
   transaction_receivers_.set_disconnect_handler(base::BindRepeating(
       &SmartCardProviderPrivateAPI::OnMojoTransactionDisconnected,
       weak_ptr_factory_.GetWeakPtr()));
+
+  connection_watchers_.set_disconnect_handler(
+      base::BindRepeating(&SmartCardProviderPrivateAPI::OnMojoWatcherPipeClosed,
+                          weak_ptr_factory_.GetWeakPtr()));
 }
 
 SmartCardProviderPrivateAPI::~SmartCardProviderPrivateAPI() = default;
@@ -446,6 +451,7 @@
       connection_receivers_.current_receiver());
   if (it != connection_watchers_per_receiver_.end()) {
     connection_watchers_.Remove(it->second);
+    connection_watchers_per_receiver_.erase(it);
   }
 
   auto callback =
@@ -886,14 +892,13 @@
 
   if (mojo::Remote connection_watcher_remote(std::move(connection_watcher));
       connection_watcher_remote.is_bound()) {
-    connection_watcher_remote.set_disconnect_handler(
-        base::BindOnce(&SmartCardProviderPrivateAPI::OnMojoWatcherPipeClosed,
-                       weak_ptr_factory_.GetWeakPtr(), connection_receiver_id));
     // Creating a connection is also considered the first use of said
     // connection.
     connection_watcher_remote->NotifyConnectionUsed();
-    connection_watchers_per_receiver_[connection_receiver_id] =
-        connection_watchers_.Add(std::move(connection_watcher_remote));
+    mojo::RemoteSetElementId watcher_id =
+        connection_watchers_per_receiver_[connection_receiver_id] =
+            connection_watchers_.Add(std::move(connection_watcher_remote));
+    connection_receivers_per_watcher_[watcher_id] = connection_receiver_id;
   }
 
   return SmartCardConnectResult::NewSuccess(
@@ -1627,9 +1632,13 @@
 #undef REPORT_RESULT_FUNCTION_IMPL
 
 void SmartCardProviderPrivateAPI::OnMojoWatcherPipeClosed(
-    mojo::ReceiverId connection_id) {
-  connection_watchers_per_receiver_.erase(connection_id);
-  connection_receivers_.Remove(connection_id);
+    mojo::RemoteSetElementId watcher_id) {
+  auto it = connection_receivers_per_watcher_.find(watcher_id);
+  if (it == connection_receivers_per_watcher_.end()) {
+    return;
+  }
+  connection_receivers_.Remove(it->second);
+  connection_receivers_per_watcher_.erase(it);
 }
 
 void SmartCardProviderPrivateAPI::NotifyConnectionUsed() {
diff --git a/chrome/browser/chromeos/extensions/smart_card_provider_private/smart_card_provider_private_api.h b/chrome/browser/chromeos/extensions/smart_card_provider_private/smart_card_provider_private_api.h
index 0ac511e..2767a6be 100644
--- a/chrome/browser/chromeos/extensions/smart_card_provider_private/smart_card_provider_private_api.h
+++ b/chrome/browser/chromeos/extensions/smart_card_provider_private/smart_card_provider_private_api.h
@@ -328,7 +328,7 @@
 
   // The watcher connection closes - thus, the pipe fall is leveraged to kill
   // the connection.
-  void OnMojoWatcherPipeClosed(mojo::ReceiverId connection_id);
+  void OnMojoWatcherPipeClosed(mojo::RemoteSetElementId watcher_id);
 
   // This may only be called only when processing a received method call on
   // SmartCardConnection().
@@ -363,6 +363,9 @@
   std::map<mojo::ReceiverId, mojo::RemoteSetElementId>
       connection_watchers_per_receiver_;
 
+  std::map<mojo::RemoteSetElementId, mojo::ReceiverId>
+      connection_receivers_per_watcher_;
+
   mojo::AssociatedReceiverSet<device::mojom::SmartCardTransaction,
                               std::tuple<ContextId, Handle>>
       transaction_receivers_;
diff --git a/chrome/browser/chromeos/extensions/smart_card_provider_private/smart_card_provider_private_apitest.cc b/chrome/browser/chromeos/extensions/smart_card_provider_private/smart_card_provider_private_apitest.cc
index a3cd2461..e972aa5 100644
--- a/chrome/browser/chromeos/extensions/smart_card_provider_private/smart_card_provider_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/smart_card_provider_private/smart_card_provider_private_apitest.cc
@@ -267,6 +267,7 @@
       receivers_.Add(this, pending_remote.InitWithNewPipeAndPassReceiver());
       return pending_remote;
     }
+    void SeverPipes() { receivers_.Clear(); }
 
    private:
     void OnDisconnect() {
@@ -836,6 +837,19 @@
   EXPECT_EQ(disconnect_count + 1, connections_watcher_.GetTimesDisconnected());
 }
 
+IN_PROC_BROWSER_TEST_F(SmartCardProviderPrivateApiTest,
+                       WatcherDisconnectionSeversConnection) {
+  LoadFakeProviderExtension({kEstablishContextJs, kConnectJs});
+  auto [context, connection] = CreateContextAndConnection();
+  ASSERT_TRUE(connection.is_connected());
+
+  base::test::TestFuture<void> disconnect_future;
+  connection.set_disconnect_handler(disconnect_future.GetCallback());
+  connections_watcher_.SeverPipes();
+  ASSERT_TRUE(disconnect_future.Wait());
+  EXPECT_FALSE(connection.is_connected());
+}
+
 IN_PROC_BROWSER_TEST_F(SmartCardProviderPrivateApiTest, DisconnectNoProvider) {
   LoadFakeProviderExtension({kEstablishContextJs, kConnectJs});
 
diff --git a/chrome/browser/chromeos/policy/dlp/clipboard_bubble.cc b/chrome/browser/chromeos/policy/dlp/clipboard_bubble.cc
index 1277bae..c8860e95 100644
--- a/chrome/browser/chromeos/policy/dlp/clipboard_bubble.cc
+++ b/chrome/browser/chromeos/policy/dlp/clipboard_bubble.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/chromeos/policy/dlp/clipboard_bubble.h"
 
+#include "ash/public/cpp/new_window_delegate.h"
+#include "ash/public/cpp/style/color_provider.h"
 #include "base/functional/bind.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_clipboard_bubble_constants.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_policy_constants.h"
@@ -11,16 +13,15 @@
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
 #include "ui/compositor/layer.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/text_utils.h"
 #include "ui/views/bubble/bubble_border.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/link.h"
 #include "ui/views/controls/styled_label.h"
-#include "ash/public/cpp/new_window_delegate.h"
-#include "ash/public/cpp/style/color_provider.h"
-#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
 
 namespace policy {
 
@@ -177,7 +178,7 @@
   border_->layer()->SetFillsBoundsOpaquely(false);
   auto shadow_border = std::make_unique<views::BubbleBorder>(
       views::BubbleBorder::FLOAT, views::BubbleBorder::STANDARD_SHADOW);
-  shadow_border->SetCornerRadius(kBubbleCornerRadius);
+  shadow_border->set_rounded_corners(gfx::RoundedCornersF(kBubbleCornerRadius));
   shadow_border->SetColor(SK_ColorTRANSPARENT);
   shadow_border->set_insets(kBubbleBorderInsets);
   border_->SetSize({kBubbleWidth, INT_MAX});
diff --git a/chrome/browser/content_settings/cookie_settings_factory.cc b/chrome/browser/content_settings/cookie_settings_factory.cc
index 8389e0c..47c332f 100644
--- a/chrome/browser/content_settings/cookie_settings_factory.cc
+++ b/chrome/browser/content_settings/cookie_settings_factory.cc
@@ -90,26 +90,20 @@
       HostContentSettingsMapFactory::GetForProfile(profile);
 
   content_settings::CookieSettings::ComputeFedCmSharingPermissionsCallback
-      compute_fedcm_sharing_permissions =
-          base::FeatureList::IsEnabled(
-              blink::features::kFedCmWithStorageAccessAPI)
-              ? base::BindRepeating(
-                    [](Profile* profile, scoped_refptr<HostContentSettingsMap>
-                                             host_content_settings_map)
-                        -> ContentSettingsForOneType {
-                      // This is called by the CookieSettings ctor, and
-                      // FederatedIdentityPermissionContextFactory
-                      // (transitively) depends on CookieSettingsFactory so we
-                      // cannot depend on
-                      // FederatedIdentityPermissionContextFactory here.
+      compute_fedcm_sharing_permissions = base::BindRepeating(
+          [](Profile* profile,
+             scoped_refptr<HostContentSettingsMap> host_content_settings_map)
+              -> ContentSettingsForOneType {
+            // This is called by the CookieSettings ctor, and
+            // FederatedIdentityPermissionContextFactory (transitively) depends
+            // on CookieSettingsFactory so we cannot depend on
+            // FederatedIdentityPermissionContextFactory here.
 
-                      return FederatedIdentityAccountKeyedPermissionContext(
-                                 profile, host_content_settings_map.get())
-                          .GetSharingPermissionGrantsAsContentSettings();
-                    },
-                    profile, scoped_refptr(host_content_settings_map))
-              : content_settings::CookieSettings::
-                    NoFedCmSharingPermissionsCallback();
+            return FederatedIdentityAccountKeyedPermissionContext(
+                       profile, host_content_settings_map.get())
+                .GetSharingPermissionGrantsAsContentSettings();
+          },
+          profile, scoped_refptr(host_content_settings_map));
 
   return new content_settings::CookieSettings(
       host_content_settings_map, prefs,
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc
index 2ca95e6..f6edfd2 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -365,6 +365,17 @@
   JNIEnv* env = base::android::AttachCurrentThread();
   return Java_PdfUtils_shouldOpenPdfInline(env, incognito);
 }
+
+void OnDetermineSavePackagePathDone(
+    content::SavePackagePathPickedCallback callback,
+    const base::FilePath& file_path,
+    const base::FilePath& display_name) {
+  content::SavePackagePathPickedParams param;
+  param.file_path = file_path;
+  param.save_type = content::SavePageType::SAVE_PAGE_TYPE_AS_MHTML;
+  param.display_name = display_name;
+  std::move(callback).Run(param, base::DoNothing());
+}
 #endif  // BUILDFLAG(IS_ANDROID)
 
 void OnCheckExistingDownloadPathDone(download::DownloadTargetInfo target_info,
@@ -1096,9 +1107,15 @@
     content::SavePackagePathPickedCallback callback) {
 #if BUILDFLAG(IS_ANDROID)
   content::SavePackagePathPickedParams param;
-  param.file_path = suggested_path.ReplaceExtension("mhtml");
-  param.save_type = content::SavePageType::SAVE_PAGE_TYPE_AS_MHTML;
-  std::move(callback).Run(param, base::DoNothing());
+  if (!web_contents) {
+    std::move(callback).Run(param, base::DoNothing());
+    return;
+  }
+
+  download::DetermineSavePackagePath(
+      web_contents->GetURL(), suggested_path,
+      base::BindOnce(&OnDetermineSavePackagePathDone, std::move(callback)));
+
 #else
   // Deletes itself.
   new SavePackageFilePicker(web_contents, suggested_path, default_extension,
diff --git a/chrome/browser/download/insecure_download_blocking.cc b/chrome/browser/download/insecure_download_blocking.cc
index ee21f83..6d8b743 100644
--- a/chrome/browser/download/insecure_download_blocking.cc
+++ b/chrome/browser/download/insecure_download_blocking.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/common/webui_url_constants.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/download/public/common/download_stats.h"
@@ -406,12 +405,6 @@
     return;
   }
 
-  // TODO(crbug.com/399076164): debug only. Used to investigate why insecure
-  // downloads can be initiated from the NTP.
-  if (rfh->GetLastCommittedOrigin().host() == chrome::kChromeUINewTabPageHost) {
-    base::debug::DumpWithoutCrashing();
-  }
-
   rfh->AddMessageToConsole(
       blink::mojom::ConsoleMessageLevel::kError,
       base::StringPrintf(
diff --git a/chrome/browser/enterprise/client_certificates/browser_context_delegate.cc b/chrome/browser/enterprise/client_certificates/browser_context_delegate.cc
index bf8cc7c..b6672b1e 100644
--- a/chrome/browser/enterprise/client_certificates/browser_context_delegate.cc
+++ b/chrome/browser/enterprise/client_certificates/browser_context_delegate.cc
@@ -47,4 +47,8 @@
   return prefs::kProvisionManagedClientCertificateForBrowserPrefs;
 }
 
+std::string BrowserContextDelegate::GetLoggingContext() {
+  return "Browser";
+}
+
 }  // namespace client_certificates
diff --git a/chrome/browser/enterprise/client_certificates/browser_context_delegate.h b/chrome/browser/enterprise/client_certificates/browser_context_delegate.h
index 5f1eb9565..e430d58 100644
--- a/chrome/browser/enterprise/client_certificates/browser_context_delegate.h
+++ b/chrome/browser/enterprise/client_certificates/browser_context_delegate.h
@@ -20,6 +20,7 @@
   std::string GetIdentityName() override;
   std::string GetTemporaryIdentityName() override;
   std::string GetPolicyPref() override;
+  std::string GetLoggingContext() override;
 };
 
 }  // namespace client_certificates
diff --git a/chrome/browser/enterprise/client_certificates/profile_context_delegate.cc b/chrome/browser/enterprise/client_certificates/profile_context_delegate.cc
index ca88987..21f4194c 100644
--- a/chrome/browser/enterprise/client_certificates/profile_context_delegate.cc
+++ b/chrome/browser/enterprise/client_certificates/profile_context_delegate.cc
@@ -44,4 +44,8 @@
   return prefs::kProvisionManagedClientCertificateForUserPrefs;
 }
 
+std::string ProfileContextDelegate::GetLoggingContext() {
+  return "Profile";
+}
+
 }  // namespace client_certificates
diff --git a/chrome/browser/enterprise/client_certificates/profile_context_delegate.h b/chrome/browser/enterprise/client_certificates/profile_context_delegate.h
index c0d95f04..a0020eae 100644
--- a/chrome/browser/enterprise/client_certificates/profile_context_delegate.h
+++ b/chrome/browser/enterprise/client_certificates/profile_context_delegate.h
@@ -24,6 +24,7 @@
   std::string GetIdentityName() override;
   std::string GetTemporaryIdentityName() override;
   std::string GetPolicyPref() override;
+  std::string GetLoggingContext() override;
 
  private:
   const raw_ptr<Profile> profile_;
diff --git a/chrome/browser/feedback/android/family_info_feedback_source_unittest.cc b/chrome/browser/feedback/android/family_info_feedback_source_unittest.cc
index bfdd108..80e920a1 100644
--- a/chrome/browser/feedback/android/family_info_feedback_source_unittest.cc
+++ b/chrome/browser/feedback/android/family_info_feedback_source_unittest.cc
@@ -82,6 +82,10 @@
     j_feedback_source_ = CreateJavaObjectForTesting();
     supervised_user_service_ =
         SupervisedUserServiceFactory::GetForProfile(profile_.get());
+
+    // In unit tests, the service is not automatically initialized but is
+    // required to ensure proper flow of preference values.
+    supervised_user_service_->Init();
   }
 
  protected:
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 97d2bc3..775db982 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -6037,6 +6037,11 @@
     "expiry_milestone": 130
   },
   {
+    "name": "left-click-opens-tab-group-bubble",
+    "owners": [ "dpenning@chromium.org" ],
+    "expiry_milestone": 137
+  },
+  {
     "name": "left-hand-side-activity-indicators",
     "owners": [ "elklm@chromium.org", "//components/permissions/PERMISSIONS_OWNERS" ],
     "expiry_milestone": 135
@@ -9046,6 +9051,11 @@
     "expiry_milestone": 143
   },
   {
+    "name": "tab-group-home",
+    "owners": [ "chrome-shopping-eng@google.com" ],
+    "expiry_milestone": 146
+  },
+  {
     "name": "tab-group-indicator",
     "owners": [ "ewannpv@chromium.org", "bling-flags@google.com" ],
     "expiry_milestone": 140
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index e43e0f0..191efbe8 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -432,6 +432,12 @@
 const char kHistorySyncAlternativeIllustrationDescription[] =
     "Enables history sync alternative illustration.";
 
+const char kLeftClickOpensTabGroupBubbleName[] =
+    "Left Click to Open TabGroup Editor Bubble";
+const char kLeftClickOpensTabGroupBubbleDescription[] =
+    "Swaps the mouse action for opening a tab group editor bubble to left "
+    "click";
+
 const char kDeprecateUnloadName[] = "Deprecate the unload event";
 const char kDeprecateUnloadDescription[] =
     "Controls the default for Permissions-Policy unload. If enabled, unload "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 506c53ec..b63113f 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -283,6 +283,9 @@
 extern const char kHistorySyncAlternativeIllustrationName[];
 extern const char kHistorySyncAlternativeIllustrationDescription[];
 
+extern const char kLeftClickOpensTabGroupBubbleName[];
+extern const char kLeftClickOpensTabGroupBubbleDescription[];
+
 extern const char kDeprecateUnloadName[];
 extern const char kDeprecateUnloadDescription[];
 
diff --git a/chrome/browser/glic/resources/internal b/chrome/browser/glic/resources/internal
index df684dd..aeb59fb 160000
--- a/chrome/browser/glic/resources/internal
+++ b/chrome/browser/glic/resources/internal
@@ -1 +1 @@
-Subproject commit df684ddfd9137b4a3ee649314a02724adcbb29fd
+Subproject commit aeb59fb12f8363ded7b2f1052956e538808c5daf
diff --git a/chrome/browser/metrics/family_link_user_metrics_provider_unittest.cc b/chrome/browser/metrics/family_link_user_metrics_provider_unittest.cc
index cc82113..9d4fa41 100644
--- a/chrome/browser/metrics/family_link_user_metrics_provider_unittest.cc
+++ b/chrome/browser/metrics/family_link_user_metrics_provider_unittest.cc
@@ -64,8 +64,14 @@
                                 bool is_subject_to_parental_controls,
                                 bool is_opted_in_to_parental_supervision) {
     Profile* profile = test_profile_manager()->CreateTestingProfile(
-        test_profile, IdentityTestEnvironmentProfileAdaptor::
-                          GetIdentityTestEnvironmentFactories());
+        test_profile, std::unique_ptr<sync_preferences::PrefServiceSyncable>(),
+        base::UTF8ToUTF16(test_profile), /*avatar_id=*/0,
+        IdentityTestEnvironmentProfileAdaptor::
+            GetIdentityTestEnvironmentFactories(),
+        /*is_supervised_profile=*/is_subject_to_parental_controls,
+        /*is_new_profile=*/std::nullopt,
+        /*policy_service=*/std::nullopt, /*shared_url_loader_factory=*/nullptr);
+
     AccountInfo account = signin::MakePrimaryAccountAvailable(
         IdentityManagerFactory::GetForProfile(profile), test_email,
         signin::ConsentLevel::kSignin);
@@ -79,6 +85,10 @@
     signin::UpdateAccountInfoForAccount(
         IdentityManagerFactory::GetForProfile(profile), account);
 
+    // In unit tests, the service is not automatically initialized but is
+    // required to ensure proper flow of preference values.
+    SupervisedUserServiceFactory::GetForProfile(profile)->Init();
+
     if (is_subject_to_parental_controls) {
       supervised_user::EnableParentalControls(*profile->GetPrefs());
 #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter.cc b/chrome/browser/metrics/process_memory_metrics_emitter.cc
index 6984bc6..1cfb5f7 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter.cc
@@ -258,6 +258,15 @@
      MetricSize::kLarge, kEffectiveSize, EmitTo::kSizeInUmaOnly, nullptr},
     {"gpu/dawn/textures/msaa", "DawnSharedContext.MSAA", MetricSize::kLarge,
      kEffectiveSize, EmitTo::kSizeInUmaOnly, nullptr},
+    {"gpu/dawn/textures/msaa",
+     "DawnSharedContext.MSAA.Count",
+     MetricSize::kCustom,
+     MemoryAllocatorDump::kNameObjectCount,
+     EmitTo::kSizeInUmaOnly,
+     nullptr,
+     {0, 10000}},
+    {"gpu/dawn/textures/msaa", "DawnSharedContext.MSAA.Largest",
+     MetricSize::kLarge, "biggest_size", EmitTo::kSizeInUmaOnly, nullptr},
     {"gpu/dawn/buffers", "DawnSharedContext.Buffers", MetricSize::kLarge,
      kEffectiveSize, EmitTo::kSizeInUmaOnly, nullptr},
     {"gpu/discardable_cache", "ServiceDiscardableManager", MetricSize::kCustom,
diff --git a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.cc b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.cc
index 35b46e6..a88131b 100644
--- a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.cc
+++ b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker.cc
@@ -11,12 +11,16 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/visibility.h"
 #include "content/public/browser/web_contents.h"
-#include "extensions/browser/script_injection_tracker.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/common/extension_id.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "ui/display/screen.h"
 #include "url/origin.h"
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/browser/script_injection_tracker.h"
+#endif
+
 namespace metrics {
 
 namespace {
@@ -33,6 +37,9 @@
 
 extensions::ExtensionIdSet GetExtensionsThatRanContentScriptsInWebContents(
     content::WebContents* contents) {
+#if !BUILDFLAG(ENABLE_EXTENSIONS)
+  return {};
+#else
   content::RenderFrameHost* main_frame = contents->GetPrimaryMainFrame();
   if (!main_frame) {
     // WebContents is being destroyed.
@@ -78,6 +85,7 @@
                          GetExtensionsThatRanContentScriptsInProcess(*process));
   }
   return extensions;
+#endif
 }
 
 }  // namespace
@@ -343,8 +351,9 @@
             web_contents->GetVisibility() == content::Visibility::VISIBLE);
   // If |web_contents| is tracked in the list of visible WebContents then a
   // synthetic visibility change event should be emitted.
-  if (iter != visible_tabs_.end())
+  if (iter != visible_tabs_.end()) {
     OnTabBecameHidden(&iter);
+  }
 
   // Remove |web_contents| from the list of contents playing video. If
   // necessary, the data store was already informed that a video stopped playing
diff --git a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc
index c34db7e2..aab54d4 100644
--- a/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc
+++ b/chrome/browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc
@@ -9,6 +9,7 @@
 
 #include <memory>
 
+#include "base/location.h"
 #include "base/memory/raw_ptr.h"
 #include "base/test/bind.h"
 #include "base/test/run_until.h"
@@ -19,6 +20,32 @@
 #include "build/build_config.h"
 #include "chrome/browser/metrics/tab_stats/tab_stats_tracker.h"
 #include "chrome/browser/metrics/usage_scenario/usage_scenario_data_store.h"
+#include "chrome/browser/profiles/profile.h"
+#include "content/public/browser/media_player_id.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/visibility.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/content_features.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/display/screen.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+#if BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/android/tab_android.h"
+#include "chrome/browser/ui/android/tab_model/tab_model.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_test_helper.h"
+#include "chrome/test/base/android/android_browser_test.h"
+#else
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h"
 #include "chrome/browser/ui/browser.h"
@@ -26,20 +53,7 @@
 #include "chrome/browser/ui/tabs/tab_enums.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "content/public/browser/media_player_id.h"
-#include "content/public/browser/visibility.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/common/content_features.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/content_browser_test_utils.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/test/embedded_test_server/http_request.h"
-#include "net/test/embedded_test_server/http_response.h"
-#include "services/metrics/public/cpp/ukm_source_id.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
+#endif
 
 namespace metrics {
 
@@ -47,11 +61,15 @@
 
 constexpr base::TimeDelta kInterval = base::Minutes(2);
 
+#if !BUILDFLAG(IS_ANDROID)
+// TODO(crbug.com/412634171): Enable this when discarding is supported on
+// Android.
 void DiscardTab(content::WebContents* contents) {
   resource_coordinator::TabLifecycleUnitSource::GetTabLifecycleUnitExternal(
       contents)
       ->DiscardTab(mojom::LifecycleUnitDiscardReason::URGENT);
 }
+#endif
 
 // A WebContentsObserver that allows waiting for some media to start or stop
 // playing fullscreen.
@@ -137,9 +155,36 @@
   base::RunLoop audio_stopped_playing_loop_;
 };
 
+// Android Auto trybots report 2 displays, so
+// `time_playing_video_full_screen_single_monitor` will never be recorded there.
+void ExpectTimePlayingVideoFullScreenSingleMonitor(
+    base::TimeDelta expected_time,
+    const UsageScenarioDataStore::IntervalData& interval_data,
+    const base::Location& location = base::Location::Current()) {
+  SCOPED_TRACE(location.ToString());
+  auto* screen = display::Screen::GetScreen();
+  ASSERT_TRUE(screen);
+  SCOPED_TRACE(::testing::Message() << screen->GetNumDisplays() << " displays");
+  if (screen->GetNumDisplays() == 1) {
+    EXPECT_EQ(expected_time,
+              interval_data.time_playing_video_full_screen_single_monitor);
+  } else {
+    EXPECT_EQ(base::TimeDelta(),
+              interval_data.time_playing_video_full_screen_single_monitor);
+  }
+}
+
+using TabStripInterface = TabStatsTracker::TabStripInterface;
+
+#if BUILDFLAG(IS_ANDROID)
+using PlatformBrowserTest = AndroidBrowserTest;
+#else
+using PlatformBrowserTest = InProcessBrowserTest;
+#endif
+
 }  // namespace
 
-class TabUsageScenarioTrackerBrowserTest : public InProcessBrowserTest {
+class TabUsageScenarioTrackerBrowserTest : public PlatformBrowserTest {
  public:
   TabUsageScenarioTrackerBrowserTest() : data_store_(&tick_clock_) {
     // Ensure that |tick_clock_.NowTicks()| doesn't return 0 the first time it
@@ -156,13 +201,35 @@
     // This is required for the fullscreen video tests.
     embedded_test_server()->ServeFilesFromSourceDirectory(
         base::FilePath(FILE_PATH_LITERAL("content/test/data")));
-    InProcessBrowserTest::SetUp();
+    PlatformBrowserTest::SetUp();
   }
 
   void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
+    PlatformBrowserTest::SetUpOnMainThread();
     host_resolver()->AddRule("*", "127.0.0.1");
     ASSERT_TRUE(embedded_test_server()->Start());
+
+#if BUILDFLAG(IS_ANDROID)
+    ASSERT_FALSE(TabModelList::models().empty());
+    tab_strip_ = std::make_unique<TabStripInterface>(
+        TabModelList::models().front().get());
+
+    // The initial tab of the main TabModel will be in an inconsistent state,
+    // since its WebContents is created and navigates to an initial URL
+    // asynchronously. Wait for it to finish loading before observing it with
+    // TabUsageScenarioTracker.
+    ASSERT_EQ(tab_strip_->tab_model()->GetTabCount(), 1);
+    TabAndroid* initial_tab = tab_strip_->tab_model()->GetTabAt(0);
+    TabAndroidLoadedWaiter waiter(initial_tab);
+    ASSERT_TRUE(waiter.Wait());
+
+    // Make sure the tab isn't still playing audio from a previous test.
+    ASSERT_FALSE(initial_tab->web_contents()->IsCurrentlyAudible());
+#else
+    ASSERT_TRUE(browser());
+    tab_strip_ = std::make_unique<TabStripInterface>(browser());
+#endif
+
     tab_stats_tracker_ = TabStatsTracker::GetInstance();
     ASSERT_TRUE(tab_stats_tracker_);
     tab_usage_scenario_tracker_ =
@@ -172,22 +239,81 @@
   }
 
   void TearDownOnMainThread() override {
+    tab_strip_.reset();
     tab_stats_tracker_->RemoveObserver(tab_usage_scenario_tracker_.get());
     tab_usage_scenario_tracker_.reset();
     tab_stats_tracker_ = nullptr;
-    InProcessBrowserTest::TearDownOnMainThread();
+    PlatformBrowserTest::TearDownOnMainThread();
   }
 
+  TabStripInterface& tab_strip() { return *tab_strip_; }
+
  protected:
+  // Methods to manipulate Browser + TabStripModel (on desktop) or TabModel (on
+  // Android).
+
+#if BUILDFLAG(IS_ANDROID)
+
+  void NavigateNewTabToUrl(content::WebContents* contents,
+                           const GURL& url,
+                           bool wait_until_complete = true) {
+    // Navigate to `url` as a render-initiated navigation, so that it isn't
+    // considered a user interaction.
+    content::NavigationController::LoadURLParams load_params(url);
+    load_params.initiator_origin = url::Origin();
+    load_params.is_renderer_initiated = true;
+    if (wait_until_complete) {
+      content::NavigateToURLBlockUntilNavigationsComplete(contents, load_params,
+                                                          1);
+    } else {
+      contents->GetController().LoadURLWithParams(load_params);
+    }
+  }
+
+  bool AddTabToTabStrip(TabStripInterface& tab_strip,
+                        const GURL& url = GURL("about:blank")) {
+    content::WebContents* active_contents = tab_strip.GetActiveWebContents();
+    if (!active_contents) {
+      ADD_FAILURE() << "No active WebContents";
+      return false;
+    }
+
+    // Create the WebContents hidden so that there's no visibility notification
+    // until it's added to the tab strip.
+    content::WebContents::CreateParams create_params(tab_strip.GetProfile());
+    create_params.initially_hidden = true;
+    content::WebContents* new_contents =
+        content::WebContents::Create(create_params).release();
+
+    // CreateTab works with both OwningTestTabModel and the initial tab strip,
+    // which is a production TabModel.
+    tab_strip.tab_model()->CreateTab(
+        TabAndroid::FromWebContents(active_contents), new_contents,
+        /*select=*/true);
+
+    NavigateNewTabToUrl(new_contents, url);
+    return true;
+  }
+
+#else  // !BUILDFLAG(IS_ANDROID)
+
+  bool AddTabToTabStrip(TabStripInterface& tab_strip, const GURL& url) {
+    return AddTabAtIndexToBrowser(tab_strip.browser(), 1, url,
+                                  ui::PAGE_TRANSITION_TYPED);
+  }
+
+#endif  // !BUILDFLAG(IS_ANDROID)
+
   base::SimpleTestTickClock tick_clock_;
   raw_ptr<TabStatsTracker> tab_stats_tracker_{nullptr};
   UsageScenarioDataStoreImpl data_store_;
   std::unique_ptr<TabUsageScenarioTracker> tab_usage_scenario_tracker_;
+  std::unique_ptr<TabStripInterface> tab_strip_;
 };
 
 IN_PROC_BROWSER_TEST_F(TabUsageScenarioTrackerBrowserTest, BasicNavigations) {
   // Test with only one visible tab and one top level navigation.
-  auto* content0 = browser()->tab_strip_model()->GetWebContentsAt(0);
+  auto* content0 = tab_strip().GetWebContentsAt(0);
   EXPECT_TRUE(content::NavigateToURL(
       content0, embedded_test_server()->GetURL("/title1.html")));
   tick_clock_.Advance(kInterval);
@@ -202,9 +328,8 @@
       interval_data.time_playing_video_full_screen_single_monitor.is_zero());
   EXPECT_TRUE(interval_data.time_with_open_webrtc_connection.is_zero());
   EXPECT_TRUE(interval_data.time_playing_video_in_visible_tab.is_zero());
-  EXPECT_EQ(browser()
-                ->tab_strip_model()
-                ->GetActiveWebContents()
+  EXPECT_EQ(tab_strip()
+                .GetActiveWebContents()
                 ->GetPrimaryMainFrame()
                 ->GetPageUkmSourceId(),
             interval_data.source_id_for_longest_visible_origin);
@@ -213,14 +338,13 @@
 
   // Add a second tab that will become the visible one.
   tick_clock_.Advance(kInterval);
-  ASSERT_TRUE(AddTabAtIndex(1, embedded_test_server()->GetURL("/title2.html"),
-                            ui::PAGE_TRANSITION_LINK));
-  auto* contents1 = browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_EQ(
-      content::Visibility::VISIBLE,
-      browser()->tab_strip_model()->GetActiveWebContents()->GetVisibility());
+  ASSERT_TRUE(AddTabToTabStrip(tab_strip(),
+                               embedded_test_server()->GetURL("/title2.html")));
+  auto* contents1 = tab_strip().GetActiveWebContents();
+  EXPECT_EQ(content::Visibility::VISIBLE,
+            tab_strip().GetActiveWebContents()->GetVisibility());
   EXPECT_EQ(content::Visibility::HIDDEN,
-            browser()->tab_strip_model()->GetWebContentsAt(0)->GetVisibility());
+            tab_strip().GetWebContentsAt(0)->GetVisibility());
   tick_clock_.Advance(kInterval * 2);
   interval_data = data_store_.ResetIntervalData();
   EXPECT_EQ(2U, interval_data.max_tab_count);
@@ -238,17 +362,15 @@
             interval_data.source_id_for_longest_visible_origin_duration);
 
   // Activate the first tab and close it.
-  browser()->tab_strip_model()->ActivateTabAt(0);
+  tab_strip().ActivateTabAtForTesting(0);
   tick_clock_.Advance(kInterval * 2);
-  auto expected_source_id = browser()
-                                ->tab_strip_model()
-                                ->GetActiveWebContents()
+  auto expected_source_id = tab_strip()
+                                .GetActiveWebContents()
                                 ->GetPrimaryMainFrame()
                                 ->GetPageUkmSourceId();
-  int previous_tab_count = browser()->tab_strip_model()->count();
-  browser()->tab_strip_model()->CloseWebContentsAt(
-      0, TabCloseTypes::CLOSE_USER_GESTURE);
-  EXPECT_EQ(previous_tab_count - 1, browser()->tab_strip_model()->count());
+  size_t previous_tab_count = tab_strip().GetTabCount();
+  tab_strip().CloseTabAtForTesting(0);
+  EXPECT_EQ(previous_tab_count - 1, tab_strip().GetTabCount());
   tick_clock_.Advance(kInterval);
   interval_data = data_store_.ResetIntervalData();
   EXPECT_EQ(2U, interval_data.max_tab_count);
@@ -286,14 +408,13 @@
 }
 
 IN_PROC_BROWSER_TEST_F(TabUsageScenarioTrackerBrowserTest, TabCrash) {
-  ASSERT_TRUE(AddTabAtIndex(1, embedded_test_server()->GetURL("/title2.html"),
-                            ui::PAGE_TRANSITION_LINK));
+  ASSERT_TRUE(AddTabToTabStrip(tab_strip(),
+                               embedded_test_server()->GetURL("/title2.html")));
   EXPECT_EQ(content::Visibility::VISIBLE,
-            browser()->tab_strip_model()->GetWebContentsAt(1)->GetVisibility());
+            tab_strip().GetWebContentsAt(1)->GetVisibility());
   tick_clock_.Advance(kInterval);
-  auto expected_source_id = browser()
-                                ->tab_strip_model()
-                                ->GetActiveWebContents()
+  auto expected_source_id = tab_strip()
+                                .GetActiveWebContents()
                                 ->GetPrimaryMainFrame()
                                 ->GetPageUkmSourceId();
 
@@ -313,8 +434,16 @@
 
   // Induce a crash in the active tab.
   tick_clock_.Advance(kInterval);
-  content::CrashTab(browser()->tab_strip_model()->GetWebContentsAt(1));
-  EXPECT_TRUE(browser()->tab_strip_model()->GetWebContentsAt(1)->IsCrashed());
+  content::CrashTab(tab_strip().GetWebContentsAt(1));
+  EXPECT_TRUE(tab_strip().GetWebContentsAt(1)->IsCrashed());
+#if BUILDFLAG(IS_ANDROID)
+  // On Android, the Sad Tab overlay is handled in the Java layer and doesn't
+  // trigger in this test. Fake it by hiding the active tab. The test is still
+  // useful to validate that a tab being hidden during an interval updates
+  // `source_id_for_longest_visible_origin_duration` correctly, although it
+  // can't validate that a crashed tab actually becomes hidden on Android.
+  tab_strip().GetWebContentsAt(1)->WasHidden();
+#endif
   tick_clock_.Advance(kInterval);
   interval_data = data_store_.ResetIntervalData();
   EXPECT_EQ(2U, interval_data.max_tab_count);
@@ -331,6 +460,10 @@
             interval_data.source_id_for_longest_visible_origin_duration);
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+
+// TODO(crbug.com/412634171): Enable this when discarding is supported on
+// Android.
 class TabUsageScenarioTrackerDiscardBrowserTest
     : public TabUsageScenarioTrackerBrowserTest,
       public ::testing::WithParamInterface<bool> {
@@ -344,17 +477,25 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    TabUsageScenarioTrackerDiscardBrowserTest,
+    ::testing::Values(false, true),
+    [](const ::testing::TestParamInfo<
+        TabUsageScenarioTrackerDiscardBrowserTest::ParamType>& info) {
+      return info.param ? "RetainedWebContents" : "UnretainedWebContents";
+    });
+
 IN_PROC_BROWSER_TEST_P(TabUsageScenarioTrackerDiscardBrowserTest, TabDiscard) {
-  ASSERT_TRUE(AddTabAtIndex(1, embedded_test_server()->GetURL("/title2.html"),
-                            ui::PAGE_TRANSITION_LINK));
+  ASSERT_TRUE(AddTabToTabStrip(tab_strip(),
+                               embedded_test_server()->GetURL("/title2.html")));
   EXPECT_EQ(content::Visibility::VISIBLE,
-            browser()->tab_strip_model()->GetWebContentsAt(1)->GetVisibility());
+            tab_strip().GetWebContentsAt(1)->GetVisibility());
   tick_clock_.Advance(kInterval);
 
   auto interval_data = data_store_.ResetIntervalData();
-  auto expected_source_id = browser()
-                                ->tab_strip_model()
-                                ->GetActiveWebContents()
+  auto expected_source_id = tab_strip()
+                                .GetActiveWebContents()
                                 ->GetPrimaryMainFrame()
                                 ->GetPageUkmSourceId();
   EXPECT_EQ(2U, interval_data.max_tab_count);
@@ -372,7 +513,7 @@
 
   // Induce a discard of the active tab.
   tick_clock_.Advance(kInterval * 2);
-  DiscardTab(browser()->tab_strip_model()->GetWebContentsAt(1));
+  DiscardTab(tab_strip().GetWebContentsAt(1));
   tick_clock_.Advance(kInterval);
   interval_data = data_store_.ResetIntervalData();
   EXPECT_EQ(2U, interval_data.max_tab_count);
@@ -390,11 +531,10 @@
 
   // Do a navigation on the discarded tab.
   EXPECT_TRUE(
-      content::NavigateToURL(browser()->tab_strip_model()->GetWebContentsAt(1),
+      content::NavigateToURL(tab_strip().GetWebContentsAt(1),
                              embedded_test_server()->GetURL("/title2.html")));
-  expected_source_id = browser()
-                           ->tab_strip_model()
-                           ->GetWebContentsAt(1)
+  expected_source_id = tab_strip()
+                           .GetWebContentsAt(1)
                            ->GetPrimaryMainFrame()
                            ->GetPageUkmSourceId();
   tick_clock_.Advance(kInterval);
@@ -413,15 +553,14 @@
             interval_data.source_id_for_longest_visible_origin_duration);
 
   // Same tests but with this time the discarded tab is hidden.
-  browser()->tab_strip_model()->ActivateTabAt(0);
+  tab_strip().ActivateTabAtForTesting(0);
   EXPECT_EQ(content::Visibility::VISIBLE,
-            browser()->tab_strip_model()->GetWebContentsAt(0)->GetVisibility());
+            tab_strip().GetWebContentsAt(0)->GetVisibility());
   tick_clock_.Advance(kInterval * 2);
-  DiscardTab(browser()->tab_strip_model()->GetWebContentsAt(1));
+  DiscardTab(tab_strip().GetWebContentsAt(1));
   tick_clock_.Advance(kInterval);
-  expected_source_id = browser()
-                           ->tab_strip_model()
-                           ->GetWebContentsAt(0)
+  expected_source_id = tab_strip()
+                           .GetWebContentsAt(0)
                            ->GetPrimaryMainFrame()
                            ->GetPageUkmSourceId();
   interval_data = data_store_.ResetIntervalData();
@@ -440,7 +579,7 @@
 
   // Do a navigation on the discarded tab.
   EXPECT_TRUE(
-      content::NavigateToURL(browser()->tab_strip_model()->GetWebContentsAt(1),
+      content::NavigateToURL(tab_strip().GetWebContentsAt(1),
                              embedded_test_server()->GetURL("/title2.html")));
   tick_clock_.Advance(kInterval);
   interval_data = data_store_.ResetIntervalData();
@@ -463,16 +602,16 @@
   // Start a video in a tab and discard it while it's playing, ensure that
   // things are tracked properly.
   EXPECT_TRUE(
-      content::NavigateToURL(browser()->tab_strip_model()->GetWebContentsAt(0),
+      content::NavigateToURL(tab_strip().GetWebContentsAt(0),
                              embedded_test_server()->GetURL("/title2.html")));
   tick_clock_.Advance(kInterval);
-  ASSERT_TRUE(AddTabAtIndex(
-      1, embedded_test_server()->GetURL("/media/session/media-session.html"),
-      ui::PAGE_TRANSITION_LINK));
+  ASSERT_TRUE(AddTabToTabStrip(
+      tab_strip(),
+      embedded_test_server()->GetURL("/media/session/media-session.html")));
   EXPECT_EQ(content::Visibility::VISIBLE,
-            browser()->tab_strip_model()->GetWebContentsAt(1)->GetVisibility());
+            tab_strip().GetWebContentsAt(1)->GetVisibility());
 
-  auto* media_contents = browser()->tab_strip_model()->GetWebContentsAt(1);
+  auto* media_contents = tab_strip().GetWebContentsAt(1);
   MediaWaiter media_waiter(media_contents);
   EXPECT_TRUE(content::ExecJs(
       media_contents, "document.getElementById('long-video-loop').play();"));
@@ -485,7 +624,7 @@
   // Discard the tab, the data store's visible tab video timer should be reset
   // by the tracker.
   tick_clock_.Advance(kInterval * 2);
-  DiscardTab(browser()->tab_strip_model()->GetWebContentsAt(1));
+  DiscardTab(tab_strip().GetWebContentsAt(1));
   ASSERT_TRUE(base::test::RunUntil([&]() {
     return !data_store_.TrackingPlayingVideoInActiveTabForTesting();
   }));
@@ -510,16 +649,15 @@
   // Play full screen video in a tab and discard it while it's playing, ensure
   // that things are tracked properly.
   EXPECT_TRUE(
-      content::NavigateToURL(browser()->tab_strip_model()->GetWebContentsAt(0),
+      content::NavigateToURL(tab_strip().GetWebContentsAt(0),
                              embedded_test_server()->GetURL("/title2.html")));
   tick_clock_.Advance(kInterval);
-  ASSERT_TRUE(
-      AddTabAtIndex(1, embedded_test_server()->GetURL("/media/fullscreen.html"),
-                    ui::PAGE_TRANSITION_LINK));
+  ASSERT_TRUE(AddTabToTabStrip(
+      tab_strip(), embedded_test_server()->GetURL("/media/fullscreen.html")));
   EXPECT_EQ(content::Visibility::VISIBLE,
-            browser()->tab_strip_model()->GetWebContentsAt(1)->GetVisibility());
+            tab_strip().GetWebContentsAt(1)->GetVisibility());
 
-  auto* fullscreen_contents = browser()->tab_strip_model()->GetWebContentsAt(1);
+  auto* fullscreen_contents = tab_strip().GetWebContentsAt(1);
   FullscreenEventsWaiter fullscreen_waiter(fullscreen_contents);
   EXPECT_TRUE(
       content::ExecJs(fullscreen_contents, "makeFullscreen('small_video')"));
@@ -533,7 +671,7 @@
   // Discard the tab, the data store's full screen video timer should be reset
   // by the tracker.
   tick_clock_.Advance(kInterval * 2);
-  DiscardTab(browser()->tab_strip_model()->GetWebContentsAt(1));
+  DiscardTab(tab_strip().GetWebContentsAt(1));
   ASSERT_TRUE(base::test::RunUntil([&]() {
     return !data_store_.TrackingPlayingFullScreenVideoSingleMonitorForTesting();
   }));
@@ -543,8 +681,7 @@
   EXPECT_EQ(1U, interval_data.max_visible_window_count);
   EXPECT_EQ(2U, interval_data.top_level_navigation_count);
   EXPECT_EQ(0U, interval_data.tabs_closed_during_interval);
-  EXPECT_EQ(kInterval * 2,
-            interval_data.time_playing_video_full_screen_single_monitor);
+  ExpectTimePlayingVideoFullScreenSingleMonitor(kInterval * 2, interval_data);
   EXPECT_TRUE(interval_data.time_with_open_webrtc_connection.is_zero());
   EXPECT_TRUE(interval_data.time_playing_video_in_visible_tab.is_zero());
   EXPECT_EQ(expected_source_id,
@@ -553,9 +690,11 @@
             interval_data.source_id_for_longest_visible_origin_duration);
 }
 
+#endif  // !BUILDFLAG(IS_ANDROID)
+
 IN_PROC_BROWSER_TEST_F(TabUsageScenarioTrackerBrowserTest, FullScreenVideo) {
   // Play fullscreen video in a tab, ensure that things are tracked properly.
-  auto* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
+  auto* contents = tab_strip().GetWebContentsAt(0);
   FullscreenEventsWaiter waiter(contents);
   EXPECT_TRUE(content::NavigateToURL(
       contents, embedded_test_server()->GetURL("/media/fullscreen.html")));
@@ -572,8 +711,7 @@
   EXPECT_EQ(1U, interval_data.max_visible_window_count);
   EXPECT_EQ(1U, interval_data.top_level_navigation_count);
   EXPECT_EQ(0U, interval_data.tabs_closed_during_interval);
-  EXPECT_EQ(kInterval,
-            interval_data.time_playing_video_full_screen_single_monitor);
+  ExpectTimePlayingVideoFullScreenSingleMonitor(kInterval, interval_data);
   EXPECT_TRUE(interval_data.time_with_open_webrtc_connection.is_zero());
   // The |time_playing_video_in_visible_tab| value is not currently being
   // tracked.
@@ -586,7 +724,7 @@
 
 IN_PROC_BROWSER_TEST_F(TabUsageScenarioTrackerBrowserTest, VisibleTabVideo) {
   // Play video in a tab, ensure that things are tracked properly.
-  auto* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
+  auto* contents = tab_strip().GetWebContentsAt(0);
   MediaWaiter waiter(contents);
   EXPECT_TRUE(content::NavigateToURL(
       contents,
@@ -610,7 +748,14 @@
       interval_data.time_playing_video_full_screen_single_monitor.is_zero());
   EXPECT_TRUE(interval_data.time_with_open_webrtc_connection.is_zero());
   EXPECT_EQ(kInterval, interval_data.time_playing_video_in_visible_tab);
+
+  // TODO(crbug.com/412634171): Android (especially desktop Android) sometimes
+  // plays audio immediately on loading media-session.html. Find out why and
+  // reenable this expectation.
+#if !BUILDFLAG(IS_ANDROID)
   EXPECT_TRUE(interval_data.time_playing_audio.is_zero());
+#endif
+
   EXPECT_EQ(expected_source_id,
             interval_data.source_id_for_longest_visible_origin);
   EXPECT_EQ(kInterval,
@@ -619,7 +764,7 @@
 
 IN_PROC_BROWSER_TEST_F(TabUsageScenarioTrackerBrowserTest, TabAudio) {
   // Play audio in a tab, ensure that things are tracked properly.
-  auto* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
+  auto* contents = tab_strip().GetWebContentsAt(0);
   MediaWaiter waiter(contents);
   EXPECT_TRUE(content::NavigateToURL(
       contents,
@@ -655,31 +800,28 @@
   // Play fullscreen video in a tab and close it while it's playing, ensure that
   // things are tracked properly.
   EXPECT_TRUE(
-      content::NavigateToURL(browser()->tab_strip_model()->GetWebContentsAt(0),
+      content::NavigateToURL(tab_strip().GetWebContentsAt(0),
                              embedded_test_server()->GetURL("/title2.html")));
   tick_clock_.Advance(kInterval);
-  ASSERT_TRUE(
-      AddTabAtIndex(1, embedded_test_server()->GetURL("/media/fullscreen.html"),
-                    ui::PAGE_TRANSITION_LINK));
-  auto* contents = browser()->tab_strip_model()->GetWebContentsAt(1);
+  ASSERT_TRUE(AddTabToTabStrip(
+      tab_strip(), embedded_test_server()->GetURL("/media/fullscreen.html")));
+  auto* contents = tab_strip().GetWebContentsAt(1);
   FullscreenEventsWaiter waiter(contents);
   EXPECT_TRUE(content::ExecJs(contents, "makeFullscreen('small_video')"));
   waiter.Wait(true);
   tick_clock_.Advance(kInterval * 2);
   auto expected_source_id =
       contents->GetPrimaryMainFrame()->GetPageUkmSourceId();
-  int previous_tab_count = browser()->tab_strip_model()->count();
-  browser()->tab_strip_model()->CloseWebContentsAt(
-      1, TabCloseTypes::CLOSE_USER_GESTURE);
-  EXPECT_EQ(previous_tab_count - 1, browser()->tab_strip_model()->count());
+  size_t previous_tab_count = tab_strip().GetTabCount();
+  tab_strip().CloseTabAtForTesting(1);
+  EXPECT_EQ(previous_tab_count - 1, tab_strip().GetTabCount());
 
   auto interval_data = data_store_.ResetIntervalData();
   EXPECT_EQ(2U, interval_data.max_tab_count);
   EXPECT_EQ(1U, interval_data.max_visible_window_count);
   EXPECT_EQ(2U, interval_data.top_level_navigation_count);
   EXPECT_EQ(1U, interval_data.tabs_closed_during_interval);
-  EXPECT_EQ(kInterval * 2,
-            interval_data.time_playing_video_full_screen_single_monitor);
+  ExpectTimePlayingVideoFullScreenSingleMonitor(kInterval * 2, interval_data);
   EXPECT_TRUE(interval_data.time_with_open_webrtc_connection.is_zero());
   EXPECT_TRUE(interval_data.time_playing_video_in_visible_tab.is_zero());
   EXPECT_EQ(expected_source_id,
@@ -689,9 +831,8 @@
 
   tick_clock_.Advance(kInterval);
   interval_data = data_store_.ResetIntervalData();
-  expected_source_id = browser()
-                           ->tab_strip_model()
-                           ->GetActiveWebContents()
+  expected_source_id = tab_strip()
+                           .GetActiveWebContents()
                            ->GetPrimaryMainFrame()
                            ->GetPageUkmSourceId();
   EXPECT_EQ(1U, interval_data.max_tab_count);
@@ -712,7 +853,7 @@
                        FullScreenVideoCrash) {
   // Play fullscreen video in a tab and make the tab crash, ensure that things
   // are tracked properly.
-  auto* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
+  auto* contents = tab_strip().GetWebContentsAt(0);
   EXPECT_TRUE(content::NavigateToURL(
       contents, embedded_test_server()->GetURL("/media/fullscreen.html")));
   FullscreenEventsWaiter waiter(contents);
@@ -728,8 +869,7 @@
   EXPECT_EQ(1U, interval_data.max_visible_window_count);
   EXPECT_EQ(1U, interval_data.top_level_navigation_count);
   EXPECT_EQ(0U, interval_data.tabs_closed_during_interval);
-  EXPECT_EQ(kInterval,
-            interval_data.time_playing_video_full_screen_single_monitor);
+  ExpectTimePlayingVideoFullScreenSingleMonitor(kInterval, interval_data);
   EXPECT_TRUE(interval_data.time_with_open_webrtc_connection.is_zero());
   EXPECT_TRUE(interval_data.time_playing_video_in_visible_tab.is_zero());
   EXPECT_EQ(expected_source_id,
@@ -760,19 +900,29 @@
                        InitialVisibleNotification) {
   // This test causes a WebContents::OnVisibilityChanged(VISIBLE) signal to be
   // emitted for a tab that was already visible when adding it.
+#if BUILDFLAG(IS_ANDROID)
+  OwningTestTabModel tab_model2(tab_strip().GetProfile());
+  TabAndroid* new_tab = tab_model2.AddEmptyTab(0);
+
+  // Don't wait for the navigation, to mimic BROWSER_TEST_WAIT_FOR_BROWSER.
+  NavigateNewTabToUrl(new_tab->web_contents(),
+                      embedded_test_server()->GetURL("/title2.html"),
+                      /*wait_until_complete=*/false);
+
+  TabStripInterface tab_strip2(&tab_model2);
+#else
   ui_test_utils::NavigateToURLWithDisposition(
-      browser(), embedded_test_server()->GetURL("/title2.html"),
+      tab_strip().browser(), embedded_test_server()->GetURL("/title2.html"),
       WindowOpenDisposition::NEW_WINDOW,
       ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
-  Browser* browser2 = BrowserList::GetInstance()->get(1);
+  TabStripInterface tab_strip2(BrowserList::GetInstance()->get(1));
+#endif
 
-  int previous_browser1_tab_count = browser()->tab_strip_model()->count();
-  int previous_browser2_tab_count = browser2->tab_strip_model()->count();
-  browser2->tab_strip_model()->CloseWebContentsAt(
-      0, TabCloseTypes::CLOSE_USER_GESTURE);
-  EXPECT_EQ(previous_browser1_tab_count, browser()->tab_strip_model()->count());
-  EXPECT_EQ(previous_browser2_tab_count - 1,
-            browser2->tab_strip_model()->count());
+  size_t previous_browser1_tab_count = tab_strip().GetTabCount();
+  size_t previous_browser2_tab_count = tab_strip2.GetTabCount();
+  tab_strip2.CloseTabAtForTesting(0);
+  EXPECT_EQ(previous_browser1_tab_count, tab_strip().GetTabCount());
+  EXPECT_EQ(previous_browser2_tab_count - 1, tab_strip2.GetTabCount());
 
   tick_clock_.Advance(kInterval);
   auto interval_data = data_store_.ResetIntervalData();
@@ -785,9 +935,8 @@
       interval_data.time_playing_video_full_screen_single_monitor.is_zero());
   EXPECT_TRUE(interval_data.time_with_open_webrtc_connection.is_zero());
   EXPECT_TRUE(interval_data.time_playing_video_in_visible_tab.is_zero());
-  EXPECT_EQ(browser()
-                ->tab_strip_model()
-                ->GetActiveWebContents()
+  EXPECT_EQ(tab_strip()
+                .GetActiveWebContents()
                 ->GetPrimaryMainFrame()
                 ->GetPageUkmSourceId(),
             interval_data.source_id_for_longest_visible_origin);
@@ -795,13 +944,4 @@
             interval_data.source_id_for_longest_visible_origin_duration);
 }
 
-INSTANTIATE_TEST_SUITE_P(
-    ,
-    TabUsageScenarioTrackerDiscardBrowserTest,
-    ::testing::Values(false, true),
-    [](const ::testing::TestParamInfo<
-        TabUsageScenarioTrackerDiscardBrowserTest::ParamType>& info) {
-      return info.param ? "RetainedWebContents" : "UnretainedWebContents";
-    });
-
 }  // namespace metrics
diff --git a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/SiteChannelsManager.java b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/SiteChannelsManager.java
index 0d153fe..3e35e20 100644
--- a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/SiteChannelsManager.java
+++ b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/SiteChannelsManager.java
@@ -20,7 +20,8 @@
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.notifications.NotificationChannelStatus;
 import org.chromium.chrome.browser.notifications.NotificationSettingsBridge.SiteChannel;
-import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl;
+import org.chromium.components.browser_ui.notifications.BaseNotificationManagerProxy;
+import org.chromium.components.browser_ui.notifications.BaseNotificationManagerProxyFactory;
 import org.chromium.components.browser_ui.site_settings.WebsiteAddress;
 
 import java.util.ArrayList;
@@ -34,6 +35,8 @@
 
     private static @Nullable SiteChannelsManager sInstance;
 
+    private final BaseNotificationManagerProxy mNotificationManagerProxy;
+
     public static SiteChannelsManager getInstance() {
         if (sInstance == null) {
             sInstance = new SiteChannelsManager();
@@ -47,7 +50,9 @@
         ResettersForTesting.register(() -> sInstance = oldValue);
     }
 
-    private SiteChannelsManager() {}
+    private SiteChannelsManager() {
+        mNotificationManagerProxy = BaseNotificationManagerProxyFactory.create();
+    }
 
     /**
      * Creates a channel for the given origin. Don't call this if the channel for the origin already
@@ -71,7 +76,7 @@
                                                 ChromeChannelDefinitions.ChannelGroupId.SITES))
                         .toNotificationChannelGroup(
                                 ContextUtils.getApplicationContext().getResources());
-        NotificationManagerProxyImpl.getInstance().createNotificationChannelGroup(channelGroup);
+        mNotificationManagerProxy.createNotificationChannelGroup(channelGroup);
         SiteChannel siteChannel =
                 new SiteChannel(
                         createChannelId(origin, creationTime),
@@ -80,8 +85,7 @@
                         enabled
                                 ? NotificationChannelStatus.ENABLED
                                 : NotificationChannelStatus.BLOCKED);
-        NotificationManagerProxyImpl.getInstance()
-                .createNotificationChannel(siteChannel.toChannel());
+        mNotificationManagerProxy.createNotificationChannel(siteChannel.toChannel());
         return siteChannel;
     }
 
@@ -101,28 +105,34 @@
 
     /** Deletes all site channels. */
     public void deleteAllSiteChannels() {
-        NotificationManagerProxyImpl.getInstance()
-                .deleteAllNotificationChannels(
-                        channelId -> {
-                            return isValidSiteChannelId(channelId);
-                        });
+        mNotificationManagerProxy.deleteAllNotificationChannels(
+                channelId -> {
+                    return isValidSiteChannelId(channelId);
+                });
     }
 
     /** Deletes the channel associated with this channel ID. */
     public void deleteSiteChannel(String channelId) {
-        NotificationManagerProxyImpl.getInstance().deleteNotificationChannel(channelId);
+        mNotificationManagerProxy.deleteNotificationChannel(channelId);
     }
 
     /**
      * Gets the status of the channel associated with this channelId.
      *
-     * @return ALLOW, BLOCKED, or UNAVAILABLE (if the channel was never created or was deleted).
+     * @param channelId The ID of the notification channel to query
+     * @param callback Callback to run after getting the result. The result can be ALLOW, BLOCKED,
+     *     or UNAVAILABLE (if the channel was never created or was deleted).
      */
-    public @NotificationChannelStatus int getChannelStatus(String channelId) {
-        NotificationChannel channel =
-                NotificationManagerProxyImpl.getInstance().getNotificationChannel(channelId);
-        if (channel == null) return NotificationChannelStatus.UNAVAILABLE;
-        return toChannelStatus(channel.getImportance());
+    public void getChannelStatusAsync(String channelId, Callback<Integer> callback) {
+        mNotificationManagerProxy.getNotificationChannel(
+                channelId,
+                (channel) -> {
+                    if (channel == null) {
+                        callback.onResult(NotificationChannelStatus.UNAVAILABLE);
+                        return;
+                    }
+                    callback.onResult(toChannelStatus(channel.getImportance()));
+                });
     }
 
     /**
@@ -130,18 +140,16 @@
      * manager). This includes enabled and blocked channels.
      */
     public void getSiteChannelsAsync(Callback<SiteChannel[]> callback) {
-        NotificationManagerProxyImpl.getInstance()
-                .getNotificationChannels(
-                        (channels) -> {
-                            List<SiteChannel> siteChannels = new ArrayList<>();
-                            for (NotificationChannel channel : channels) {
-                                if (isValidSiteChannelId(channel.getId())) {
-                                    siteChannels.add(toSiteChannel(channel));
-                                }
-                            }
-                            callback.onResult(
-                                    siteChannels.toArray(new SiteChannel[siteChannels.size()]));
-                        });
+        mNotificationManagerProxy.getNotificationChannels(
+                (channels) -> {
+                    List<SiteChannel> siteChannels = new ArrayList<>();
+                    for (NotificationChannel channel : channels) {
+                        if (isValidSiteChannelId(channel.getId())) {
+                            siteChannels.add(toSiteChannel(channel));
+                        }
+                    }
+                    callback.onResult(siteChannels.toArray(new SiteChannel[siteChannels.size()]));
+                });
     }
 
     private static SiteChannel toSiteChannel(NotificationChannel channel) {
@@ -194,7 +202,7 @@
     /**
      * Retrieves the notification channel ID for a given origin.
      *
-     * @param origin The origin to be quried.
+     * @param origin The origin to be queried.
      * @param callback A callback to return the channel ID once the call completes.
      */
     public void getChannelIdForOriginAsync(String origin, Callback<String> callback) {
diff --git a/chrome/browser/page_info/page_info_features.cc b/chrome/browser/page_info/page_info_features.cc
index 322a0b99..5305634 100644
--- a/chrome/browser/page_info/page_info_features.cc
+++ b/chrome/browser/page_info/page_info_features.cc
@@ -17,15 +17,6 @@
       g_browser_process->GetApplicationLocale());
 }
 
-bool IsAboutThisSiteAsyncFetchingEnabled() {
-  return IsAboutThisSiteFeatureEnabled() &&
-         base::FeatureList::IsEnabled(kAboutThisSiteAsyncFetching);
-}
-
-BASE_FEATURE(kAboutThisSiteAsyncFetching,
-             "AboutThisSiteAsyncFetching",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kPrivacyPolicyInsights,
              "PrivacyPolicyInsights",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/page_info/page_info_features.h b/chrome/browser/page_info/page_info_features.h
index f4e1f645..896bbd3 100644
--- a/chrome/browser/page_info/page_info_features.h
+++ b/chrome/browser/page_info/page_info_features.h
@@ -12,14 +12,6 @@
 // enabled.
 bool IsAboutThisSiteFeatureEnabled();
 
-// Returns true if kAboutThisSiteAsyncFetching and dependent features are
-// enabled.
-bool IsAboutThisSiteAsyncFetchingEnabled();
-
-// Enables usage of the async fetching method for cacao and caching fetched
-// metadata in a TabHelper.
-BASE_DECLARE_FEATURE(kAboutThisSiteAsyncFetching);
-
 // Enables the privacy policy insights Learning Experiment UI.
 BASE_DECLARE_FEATURE(kPrivacyPolicyInsights);
 
diff --git a/chrome/browser/password_check/android/internal/BUILD.gn b/chrome/browser/password_check/android/internal/BUILD.gn
index ceceb68..f1d27f81 100644
--- a/chrome/browser/password_check/android/internal/BUILD.gn
+++ b/chrome/browser/password_check/android/internal/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/config/android/rules.gni")
-import("//build/config/locales.gni")
 import("//chrome/android/features/android_library_factory_tmpl.gni")
 import("//chrome/common/features.gni")
 import("//third_party/jni_zero/jni_zero.gni")
@@ -155,9 +154,4 @@
 java_strings_grd("java_strings_grd") {
   defines = chrome_grit_defines
   grd_file = "java/strings/android_password_check_strings.grd"
-  outputs =
-      [ "values/android_password_check_strings.xml" ] +
-      process_file_template(
-          android_bundle_locales_as_resources,
-          [ "values-{{source_name_part}}/android_password_check_strings.xml" ])
 }
diff --git a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade.cc b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade.cc
index 3d9888d..d9a1d612 100644
--- a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade.cc
+++ b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade.cc
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "base/functional/bind.h"
-#include "base/metrics/histogram_functions.h"
 #include "base/run_loop.h"
 #include "base/types/pass_key.h"
 #include "chrome/browser/history/history_service_factory.h"
@@ -90,10 +89,6 @@
     const history::DeletionInfo& deletion_info) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (deletion_info.IsAllHistory()) {
-    if (!browser_context_->IsOffTheRecord()) {
-      base::UmaHistogramBoolean(
-          "PerformanceManager.SiteDB.WriteScheduled.ClearAllSiteData", true);
-    }
     ClearAllSiteData();
     return;
   }
@@ -115,12 +110,6 @@
     return;
   }
 
-  if (!browser_context_->IsOffTheRecord()) {
-    base::UmaHistogramBoolean(
-        "PerformanceManager.SiteDB.WriteScheduled.ClearSiteDataForOrigins",
-        true);
-  }
-
   auto* cache =
       SiteDataCacheFacadeFactory::GetInstance()
           ->cache_factory()
diff --git a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.cc b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.cc
index 9f0ed110..6b28e230 100644
--- a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.cc
+++ b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.cc
@@ -26,7 +26,6 @@
 
 SiteDataCacheFacadeFactory* SiteDataCacheFacadeFactory::GetInstance() {
   static base::NoDestructor<SiteDataCacheFacadeFactory> instance;
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   return instance.get();
 }
 
@@ -61,7 +60,6 @@
               // Ash Internals.
               .WithAshInternals(ProfileSelection::kOwnInstance)
               .Build()) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DependsOn(HistoryServiceFactory::GetInstance());
 }
 
diff --git a/chrome/browser/performance_manager/policies/background_tab_loading_policy.cc b/chrome/browser/performance_manager/policies/background_tab_loading_policy.cc
index 779ae4be..8780830 100644
--- a/chrome/browser/performance_manager/policies/background_tab_loading_policy.cc
+++ b/chrome/browser/performance_manager/policies/background_tab_loading_policy.cc
@@ -12,6 +12,9 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/system/sys_info.h"
 #include "base/time/time.h"
+#include "base/trace_event/memory_pressure_level_proto.h"
+#include "base/trace_event/named_trigger.h"
+#include "base/trace_event/typed_macros.h"
 #include "chrome/browser/performance_manager/mechanisms/page_loader.h"
 #include "chrome/browser/performance_manager/policies/background_tab_loading_policy_helpers.h"
 #include "chrome/browser/performance_manager/public/background_tab_loading_policy.h"
@@ -95,6 +98,11 @@
 
 void ScheduleLoadForRestoredTabs(
     std::vector<content::WebContents*> web_contents_vector) {
+  // Trigger a slow-reports and collect a session restore trace if needed.
+  base::trace_event::EmitNamedTrigger("session-restore-config");
+  TRACE_EVENT("browser", "ScheduleLoadForRestoredTabs", "tabs_count",
+              web_contents_vector.size());
+
   DCHECK(!web_contents_vector.empty());
 
   std::vector<BackgroundTabLoadingPolicy::PageNodeData> page_node_data_vector;
@@ -171,6 +179,7 @@
 void BackgroundTabLoadingPolicy::OnLoadingStateChanged(
     const PageNode* page_node,
     PageNode::LoadingState previous_state) {
+  TRACE_EVENT("browser", "BackgroundTabLoadingPolicy::OnLoadingStateChanged");
   DCHECK_EQ(has_restored_tabs_to_load_, HasRestoredTabsToLoad());
 
   switch (page_node->GetLoadingState()) {
@@ -232,6 +241,7 @@
 
 void BackgroundTabLoadingPolicy::OnBeforePageNodeRemoved(
     const PageNode* page_node) {
+  TRACE_EVENT("browser", "BackgroundTabLoadingPolicy::OnBeforePageNodeRemoved");
   RemovePageNode(page_node);
 
   // There may be free loading slots, check and load more tabs if that's the
@@ -400,6 +410,8 @@
 
 void BackgroundTabLoadingPolicy::OnUsedInBackgroundAvailable(
     base::WeakPtr<PageNode> page_node) {
+  TRACE_EVENT("browser",
+              "BackgroundTabLoadingPolicy::OnUsedInBackgroundAvailable");
   if (!page_node) {
     // Ignore the value if the PageNode was deleted.
     return;
@@ -427,6 +439,7 @@
 }
 
 void BackgroundTabLoadingPolicy::StopLoadingTabs() {
+  TRACE_EVENT("browser", "BackgroundTabLoadingPolicy::StopLoadingTabs");
   // Clear out the remaining tabs to load and clean ourselves up.
   page_nodes_to_load_.clear();
   tabs_scored_ = 0;
@@ -439,6 +452,14 @@
 
 void BackgroundTabLoadingPolicy::OnMemoryPressure(
     base::MemoryPressureListener::MemoryPressureLevel new_level) {
+  TRACE_EVENT_INSTANT(
+      "browser", "BackgroundTabLoadingPolicy::OnMemoryPressure",
+      [&](perfetto::EventContext ctx) {
+        auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
+        auto* data = event->set_chrome_memory_pressure_notification();
+        data->set_level(
+            base::trace_event::MemoryPressureLevelToTraceEnum(new_level));
+      });
   switch (new_level) {
     case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
       break;
@@ -511,6 +532,7 @@
 }
 
 void BackgroundTabLoadingPolicy::InitiateLoad(const PageNode* page_node) {
+  TRACE_EVENT("browser", "BackgroundTabLoadingPolicy::InitiateLoad");
   // The page shouldn't already be loading.
   DCHECK(!base::Contains(page_nodes_load_initiated_, page_node));
   DCHECK(!base::Contains(page_nodes_loading_, page_node));
@@ -536,6 +558,7 @@
 }
 
 void BackgroundTabLoadingPolicy::MaybeLoadSomeTabs() {
+  TRACE_EVENT("browser", "BackgroundTabLoadingPolicy::MaybeLoadSomeTabs");
   // Continue to load tabs while possible. This is in a loop with a
   // recalculation of GetMaxNewTabLoads() as reentrancy can cause conditions
   // to change as each tab load is initiated.
@@ -572,6 +595,7 @@
 }
 
 void BackgroundTabLoadingPolicy::LoadNextTab() {
+  TRACE_EVENT("browser", "BackgroundTabLoadingPolicy::LoadNextTab");
   DCHECK(!page_nodes_to_load_.empty());
   DCHECK_EQ(tabs_scored_, page_nodes_to_load_.size());
 
diff --git a/chrome/browser/picture_in_picture/auto_pip_setting_view.cc b/chrome/browser/picture_in_picture/auto_pip_setting_view.cc
index a2197ce4..2389d56 100644
--- a/chrome/browser/picture_in_picture/auto_pip_setting_view.cc
+++ b/chrome/browser/picture_in_picture/auto_pip_setting_view.cc
@@ -250,7 +250,8 @@
   std::unique_ptr<views::BubbleBorder> bubble_border =
       std::make_unique<views::BubbleBorder>(
           arrow(), views::BubbleBorder::STANDARD_SHADOW);
-  bubble_border->SetCornerRadius(kBubbleBorderCornerRadius);
+  bubble_border->set_rounded_corners(
+      gfx::RoundedCornersF(kBubbleBorderCornerRadius));
   bubble_border->set_md_shadow_elevation(kBubbleBorderMdShadowElevation);
   bubble_border->set_draw_border_stroke(true);
 
diff --git a/chrome/browser/policy/BUILD.gn b/chrome/browser/policy/BUILD.gn
index 4f592ff..47dbcc2 100644
--- a/chrome/browser/policy/BUILD.gn
+++ b/chrome/browser/policy/BUILD.gn
@@ -286,6 +286,7 @@
       "test/automatic_fullscreen_policy_browsertest.cc",
       "test/autoplay_policy_browsertest.cc",
       "test/bookmark_bar_enabled_browsertest.cc",
+      "test/clear_window_name_for_new_browsing_context_group_browsertest.cc",
       "test/component_updater_policy_browsertest.cc",
       "test/data_url_policy_browsertest.cc",
       "test/developer_tools_policy_browsertest.cc",
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 0cf5200..dba44001 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -434,6 +434,9 @@
   { key::kAllowDeletingBrowserHistory,
     prefs::kAllowDeletingBrowserHistory,
     base::Value::Type::BOOLEAN },
+  { key::kClearWindowNameForNewBrowsingContextGroup,
+      prefs::kClearWindowNameForNewBrowsingContextGroup,
+      base::Value::Type::BOOLEAN },
 // Policies for all platforms - End
 #if BUILDFLAG(IS_ANDROID)
   { key::kAccessibilityPerformanceFilteringAllowed,
diff --git a/chrome/browser/policy/test/clear_window_name_for_new_browsing_context_group_browsertest.cc b/chrome/browser/policy/test/clear_window_name_for_new_browsing_context_group_browsertest.cc
new file mode 100644
index 0000000..78ea62f5
--- /dev/null
+++ b/chrome/browser/policy/test/clear_window_name_for_new_browsing_context_group_browsertest.cc
@@ -0,0 +1,102 @@
+// 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 "base/values.h"
+#include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/content_settings/core/common/content_settings.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/common/content_features.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "third_party/blink/public/common/features.h"
+#include "url/gurl.h"
+
+namespace policy {
+class ClearWindowNameForNewBrowsingContextGroupTestP
+    : public policy::PolicyTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  ClearWindowNameForNewBrowsingContextGroupTestP() {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kClearCrossSiteCrossBrowsingContextGroupWindowName);
+  }
+  void SetUpOnMainThread() override {
+    PolicyTest::SetUpOnMainThread();
+
+    content::SetupCrossSiteRedirector(embedded_test_server());
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+
+  void SetUpInProcessBrowserTestFixture() override {
+    PolicyTest::SetUpInProcessBrowserTestFixture();
+
+    policy::PolicyMap policies;
+    policies.Set(policy::key::kClearWindowNameForNewBrowsingContextGroup,
+                 policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
+                 policy::POLICY_SOURCE_CLOUD, base::Value(IsPolicyEnabled()),
+                 nullptr);
+    UpdateProviderPolicy(policies);
+  }
+
+  bool IsPolicyEnabled() { return GetParam(); }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Verify that cross-site main frame navigation that swaps BrowsingInstances
+// clears window.name when the corresponding policy is enabled.
+IN_PROC_BROWSER_TEST_P(ClearWindowNameForNewBrowsingContextGroupTestP,
+                       ClearWindowNameCrossSite) {
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+  content::WebContents* web_contents =
+      chrome_test_utils::GetActiveWebContents(this);
+
+  // Navigate to a.com/title1.html.
+  EXPECT_TRUE(content::NavigateToURL(web_contents, url_a));
+
+  // Set window.name.
+  EXPECT_TRUE(content::ExecJs(web_contents, "window.name='foo'"));
+  content::RenderFrameHost* frame_a = web_contents->GetPrimaryMainFrame();
+  scoped_refptr<content::SiteInstance> site_instance_a =
+      frame_a->GetSiteInstance();
+  EXPECT_EQ("foo", frame_a->GetFrameName());
+
+  // Navigate to b.com/title1.html. The navigation is cross-site, top-level and
+  // swaps BrowsingInstances, thus should clear window.name.
+  EXPECT_TRUE(NavigateToURL(web_contents, url_b));
+  content::RenderFrameHost* frame_b = web_contents->GetPrimaryMainFrame();
+  // Check that a.com/title1.html and b.com/title1.html are in different
+  // BrowsingInstances.
+  scoped_refptr<content::SiteInstance> site_instance_b =
+      frame_b->GetSiteInstance();
+  EXPECT_FALSE(site_instance_a->IsRelatedSiteInstance(site_instance_b.get()));
+  // window.name should be cleared if the policy is enabled.
+  if (IsPolicyEnabled()) {
+    EXPECT_EQ("", frame_b->GetFrameName());
+  } else {
+    EXPECT_EQ("foo", frame_b->GetFrameName());
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         ClearWindowNameForNewBrowsingContextGroupTestP,
+                         testing::Bool());
+
+}  // namespace policy
diff --git a/chrome/browser/privacy_sandbox/BUILD.gn b/chrome/browser/privacy_sandbox/BUILD.gn
index ed2b0215..21ddfda5f 100644
--- a/chrome/browser/privacy_sandbox/BUILD.gn
+++ b/chrome/browser/privacy_sandbox/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/args.gni")
+
 assert(!is_ios)
 
 source_set("headers") {
@@ -346,6 +348,27 @@
   public_deps = [ "notice:test_support" ]
 }
 
+if (!is_android && !is_chromeos_device) {
+  source_set("interactive_ui_tests") {
+    testonly = true
+    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+    sources = [ "browsing_topics_settings_interactive_uitest.cc" ]
+    deps = [
+      ":headers",
+      "//chrome/browser/profiles:profile",
+      "//chrome/browser/ui:browser_element_identifiers",
+      "//chrome/common",
+      "//chrome/test:test_support",
+      "//chrome/test:test_support_ui",
+      "//components/prefs",
+      "//components/privacy_sandbox",
+      "//components/privacy_sandbox:features",
+      "//components/privacy_sandbox:privacy_sandbox_prefs",
+      "//content/test:test_support",
+    ]
+  }
+}
+
 # TODO(crbug.com/408801109) move other privacy_sandbox sources
 # from chrome/test/BUILD.gn here.
 
diff --git a/chrome/browser/profiles/batch_upload/batch_upload_service.cc b/chrome/browser/profiles/batch_upload/batch_upload_service.cc
index 50eb46b..f8b2fa47 100644
--- a/chrome/browser/profiles/batch_upload/batch_upload_service.cc
+++ b/chrome/browser/profiles/batch_upload/batch_upload_service.cc
@@ -7,8 +7,10 @@
 #include <array>
 #include <map>
 
+#include "base/containers/contains.h"
 #include "base/containers/to_vector.h"
 #include "base/functional/bind.h"
+#include "base/functional/callback.h"
 #include "base/functional/callback_forward.h"
 #include "base/notreached.h"
 #include "base/strings/to_string.h"
@@ -35,22 +37,66 @@
 // This list contains all the data types that are available for the Batch Upload
 // dialog. Data types should not be repeated and the list is ordered based on
 // the priority of showing in the dialog.
-const std::array<syncer::DataType, 4> kBatchUploadAvailableTypesOrder{
+// An exception to the order of this list may happen to the first item, when the
+// entry point used to open the batch upload dialog is tied to a data type. This
+// data type will be shown first for consistency with the action from the entry
+// point. E.g. Bookmarks promo card entry point will force
+// `syncer::DataType::BOOKMARKS` to be the first data type shown (and not
+// repeated afterwards).
+const std::array<syncer::DataType, 5> kBatchUploadAvailableTypesOrder{
+    // clang-format off
     syncer::DataType::PASSWORDS,
     syncer::DataType::BOOKMARKS,
+    syncer::DataType::READING_LIST,
     syncer::DataType::CONTACT_INFO,
     syncer::DataType::THEMES,
+    // clang-format on
 };
 
+// Determines the primary type based on the entry point.
+// TODO(crbug.com/416219929): Future entry points may not be tied to a single
+// data type, this would need to return std::nullopt then.
+syncer::DataType PrimaryTypeFromEntryPoint(
+    BatchUploadService::EntryPoint entry_point) {
+  switch (entry_point) {
+    case BatchUploadService::EntryPoint::kPasswordManagerSettings:
+    case BatchUploadService::EntryPoint::kPasswordPromoCard:
+      return syncer::PASSWORDS;
+    case BatchUploadService::EntryPoint::kBookmarksManagerPromoCard:
+      return syncer::BOOKMARKS;
+  }
+}
+
 // Returns the list of data descriptions in `local_data_descriptions_map`
 // following the expected displayed order in the dialog.
-// Data descriptions with no local data will be filtered out.
+// `entry_point` is used to determine the primary (first) type to be displayed,
+// overriding the expected order for the first type only. Primary type should
+// have local data - since the entry point was available.
+// Other data descriptions with no local data will be filtered out.
 std::vector<syncer::LocalDataDescription>
 GetOrderedListOfNonEmptyDataDescriptions(
     std::map<syncer::DataType, syncer::LocalDataDescription>
-        local_data_descriptions_map) {
-  // TODO(crbug.com/361340640): make the data type entry point be the first one.
+        local_data_descriptions_map,
+    BatchUploadService::EntryPoint entry_point) {
   std::vector<syncer::LocalDataDescription> local_data_description_list;
+
+  // Treat the primary type first to make sure it is the first type for display,
+  // overriding the expected order given in `kBatchUploadAvailableTypesOrder`.
+  syncer::DataType primary_type = PrimaryTypeFromEntryPoint(entry_point);
+  CHECK(base::Contains(kBatchUploadAvailableTypesOrder, primary_type));
+  CHECK(local_data_descriptions_map.contains(primary_type));
+  syncer::LocalDataDescription& primary_local_data_description =
+      local_data_descriptions_map[primary_type];
+  CHECK(!primary_local_data_description.local_data_models.empty())
+      << "Primary data type should have local data since the entry point "
+         "is available.";
+  CHECK_EQ(primary_type, primary_local_data_description.type)
+      << "Non empty data description's data type and the keyed mapping "
+         "value should always match.";
+  local_data_description_list.push_back(
+      std::move(primary_local_data_description));
+  local_data_descriptions_map.erase(primary_type);
+
   // Reorder the result from the `local_data_descriptions_map` based on the
   // available types order.
   for (syncer::DataType type : kBatchUploadAvailableTypesOrder) {
@@ -124,20 +170,23 @@
   state_.dialog_state_->entry_point_ = entry_point;
   state_.dialog_state_->dialog_shown_callback_ = std::move(success_callback);
 
-  RequestLocalDataDescriptions();
+  GetLocalDataDescriptionsForAvailableTypes(
+      base::BindOnce(&BatchUploadService::OnGetLocalDataDescriptionsReady,
+                     base::Unretained(this)));
 }
 
-void BatchUploadService::RequestLocalDataDescriptions() {
+void BatchUploadService::GetLocalDataDescriptionsForAvailableTypes(
+    base::OnceCallback<
+        void(std::map<syncer::DataType, syncer::LocalDataDescription>)>
+        result_callback) {
   syncer::DataTypeSet data_types;
   // Iterate over all available enums.
   for (syncer::DataType type : kBatchUploadAvailableTypesOrder) {
     data_types.Put(type);
   }
 
-  sync_service_->GetLocalDataDescriptions(
-      data_types,
-      base::BindOnce(&BatchUploadService::OnGetLocalDataDescriptionsReady,
-                     base::Unretained(this)));
+  sync_service_->GetLocalDataDescriptions(data_types,
+                                          std::move(result_callback));
 }
 
 void BatchUploadService::OnGetLocalDataDescriptionsReady(
@@ -150,7 +199,8 @@
 
   delegate_->ShowBatchUploadDialog(
       state_.dialog_state_->browser_,
-      GetOrderedListOfNonEmptyDataDescriptions(std::move(local_data_map)),
+      GetOrderedListOfNonEmptyDataDescriptions(
+          std::move(local_data_map), state_.dialog_state_->entry_point_),
       state_.dialog_state_->entry_point_,
       /*complete_callback=*/
       base::BindOnce(&BatchUploadService::OnBatchUploadDialogResult,
diff --git a/chrome/browser/profiles/batch_upload/batch_upload_service.h b/chrome/browser/profiles/batch_upload/batch_upload_service.h
index d63aa00f..6a8b9438 100644
--- a/chrome/browser/profiles/batch_upload/batch_upload_service.h
+++ b/chrome/browser/profiles/batch_upload/batch_upload_service.h
@@ -38,6 +38,8 @@
   ~BatchUploadService() override;
 
   // Lists the different entry points to the Batch Upload Dialog.
+  // TODO(crbug.com/416219929): Currently all existing entry points are tied to
+  // a data type. In the future, neutral entry points may be added.
   // These values are persisted to logs. Entries should not be renumbered and
   // numeric values should never be reused.
   //
@@ -64,15 +66,19 @@
   // Returns whether the dialog is currently showing on a browser.
   bool IsDialogOpened() const;
 
+  // Gets all the local data for the available types.
+  // Available types are the sync types that the batch upload supports.
+  // This function is asynchronous and the results are returned in
+  // `result_callback`.
+  void GetLocalDataDescriptionsForAvailableTypes(
+      base::OnceCallback<
+          void(std::map<syncer::DataType, syncer::LocalDataDescription>)>
+          result_callback);
+
   // Gets the ordered list of all available types in BatchUpload for testing.
   static std::vector<syncer::DataType> AvailableTypesOrderForTesting();
 
  private:
-  // Iterates over all available types that can be displayed in the dialog and
-  // request the `syncer::LocalDataDescription that contains the list of items.
-  // The result is returned asynchronously.
-  void RequestLocalDataDescriptions();
-
   // Callback that returns a map of `syncer::LocalDataDescription` for the data
   // types that can be shown in the Batch Upload dialog.
   void OnGetLocalDataDescriptionsReady(
diff --git a/chrome/browser/profiles/batch_upload/batch_upload_service_unittest.cc b/chrome/browser/profiles/batch_upload/batch_upload_service_unittest.cc
index c570f114..f73bd99 100644
--- a/chrome/browser/profiles/batch_upload/batch_upload_service_unittest.cc
+++ b/chrome/browser/profiles/batch_upload/batch_upload_service_unittest.cc
@@ -204,6 +204,44 @@
   EXPECT_FALSE(service.IsDialogOpened());
 }
 
+TEST_F(BatchUploadServiceTest, GetLocalDataDescriptionsForAvailableTypes) {
+  SigninWithFullInfo();
+  BatchUploadService& service = CreateService();
+
+  // Make sure all available data types have return descriptions so that the
+  // order is properly tested.
+  SetLocalDataDescriptionForAllAvailableTypes();
+
+  // Lists the requested types.
+  EXPECT_CALL(sync_service_mock(),
+              GetLocalDataDescriptions(
+                  syncer::DataTypeSet{
+                      syncer::DataType::PASSWORDS, syncer::DataType::BOOKMARKS,
+                      syncer::DataType::READING_LIST,
+                      syncer::DataType::CONTACT_INFO, syncer::DataType::THEMES},
+                  _))
+      .Times(1);
+
+  base::MockCallback<base::OnceCallback<void(
+      std::map<syncer::DataType, syncer::LocalDataDescription>)>>
+      result_callback;
+  // Order is not tested.
+  std::map<syncer::DataType, syncer::LocalDataDescription>
+      expected_description_map{
+          {syncer::PASSWORDS, GetReturnDescription(syncer::PASSWORDS)},
+          {syncer::BOOKMARKS, GetReturnDescription(syncer::BOOKMARKS)},
+          {syncer::READING_LIST, GetReturnDescription(syncer::READING_LIST)},
+          {syncer::CONTACT_INFO, GetReturnDescription(syncer::CONTACT_INFO)},
+          {syncer::THEMES, GetReturnDescription(syncer::THEMES)},
+      };
+  EXPECT_CALL(result_callback, Run(expected_description_map));
+  service.GetLocalDataDescriptionsForAvailableTypes(result_callback.Get());
+}
+
+// TODO(crbug.com/416219929): This test uses the password related entry point,
+// which is in line with the default primary data type. When adding a neutral
+// entry point (not tied to any specific data type), this entry point should be
+// used in this test to be able to more accurately test the order.
 TEST_F(BatchUploadServiceTest, LocalDataForAllAvailableTypesMainOrder) {
   SigninWithFullInfo();
   BatchUploadService& service = CreateService();
@@ -216,22 +254,22 @@
               GetLocalDataDescriptions(
                   syncer::DataTypeSet{
                       syncer::DataType::PASSWORDS, syncer::DataType::BOOKMARKS,
+                      syncer::DataType::READING_LIST,
                       syncer::DataType::CONTACT_INFO, syncer::DataType::THEMES},
-                  _))
-      .Times(1);
+                  _));
   // Order is tested.
   std::vector<syncer::LocalDataDescription> expected_descriptions{
       GetReturnDescription(syncer::PASSWORDS),
       GetReturnDescription(syncer::BOOKMARKS),
+      GetReturnDescription(syncer::READING_LIST),
       GetReturnDescription(syncer::CONTACT_INFO),
       GetReturnDescription(syncer::THEMES),
   };
   EXPECT_CALL(delegate_mock(),
-              ShowBatchUploadDialog(_, expected_descriptions, _, _))
-      .Times(1);
+              ShowBatchUploadDialog(_, expected_descriptions, _, _));
 
   base::MockCallback<base::OnceCallback<void(bool)>> opened_callback;
-  EXPECT_CALL(opened_callback, Run(true)).Times(1);
+  EXPECT_CALL(opened_callback, Run(true));
 
   service.OpenBatchUpload(
       nullptr, BatchUploadService::EntryPoint::kPasswordManagerSettings,
@@ -239,6 +277,71 @@
   EXPECT_TRUE(service.IsDialogOpened());
 }
 
+TEST_F(BatchUploadServiceTest, LocalDataOrderBasedOnEntryPoint) {
+  SigninWithFullInfo();
+  BatchUploadService& service = CreateService();
+
+  SetReturnDescriptions(syncer::PASSWORDS, 1);
+  SetReturnDescriptions(syncer::BOOKMARKS, 1);
+  SetReturnDescriptions(syncer::CONTACT_INFO, 1);
+
+  EXPECT_CALL(sync_service_mock(), GetLocalDataDescriptions(_, _)).Times(2);
+
+  // Password entry point.
+  {
+    // Order is tested - passwords is first.
+    std::vector<syncer::LocalDataDescription> expected_descriptions{
+        GetReturnDescription(syncer::PASSWORDS),
+        GetReturnDescription(syncer::BOOKMARKS),
+        GetReturnDescription(syncer::CONTACT_INFO),
+    };
+    // Used to close the dialog.
+    BatchUploadSelectedDataTypeItemsCallback returned_complete_callback;
+    EXPECT_CALL(delegate_mock(),
+                ShowBatchUploadDialog(_, expected_descriptions, _, _))
+        .WillOnce(
+            [&](Browser* browser,
+                const std::vector<syncer::LocalDataDescription>&
+                    local_data_description_list,
+                BatchUploadService::EntryPoint entry_point,
+                BatchUploadSelectedDataTypeItemsCallback complete_callback) {
+              returned_complete_callback = std::move(complete_callback);
+            });
+
+    base::MockCallback<base::OnceCallback<void(bool)>> opened_callback;
+    EXPECT_CALL(opened_callback, Run(true));
+
+    service.OpenBatchUpload(
+        nullptr, BatchUploadService::EntryPoint::kPasswordManagerSettings,
+        opened_callback.Get());
+    EXPECT_TRUE(service.IsDialogOpened());
+    // Returning empty will close the dialog without any action.
+    std::move(returned_complete_callback).Run({});
+  }
+
+  ASSERT_FALSE(service.IsDialogOpened());
+
+  // Bookmarks entry point.
+  {
+    // Order is tested - bookmarks is first.
+    std::vector<syncer::LocalDataDescription> expected_descriptions{
+        GetReturnDescription(syncer::BOOKMARKS),
+        GetReturnDescription(syncer::PASSWORDS),
+        GetReturnDescription(syncer::CONTACT_INFO),
+    };
+    EXPECT_CALL(delegate_mock(),
+                ShowBatchUploadDialog(_, expected_descriptions, _, _));
+
+    base::MockCallback<base::OnceCallback<void(bool)>> opened_callback;
+    EXPECT_CALL(opened_callback, Run(true));
+
+    service.OpenBatchUpload(
+        nullptr, BatchUploadService::EntryPoint::kBookmarksManagerPromoCard,
+        opened_callback.Get());
+    EXPECT_TRUE(service.IsDialogOpened());
+  }
+}
+
 TEST_F(BatchUploadServiceTest, EmptyLocalDataReturned) {
   SigninWithFullInfo();
   BatchUploadService& service = CreateService();
@@ -260,15 +363,15 @@
   BatchUploadService& service = CreateService();
   base::MockCallback<base::OnceCallback<void(bool)>> opened_callback;
   SetReturnDescriptions(syncer::PASSWORDS, 0);
-  const syncer::LocalDataDescription& contact_info =
-      SetReturnDescriptions(syncer::CONTACT_INFO, 2);
+  const syncer::LocalDataDescription& passwords =
+      SetReturnDescriptions(syncer::PASSWORDS, 2);
 
   EXPECT_CALL(sync_service_mock(), GetLocalDataDescriptions(_, _)).Times(1);
-  // Only expect `CONTACT_INFO` since return `PASSWORDS` are empty.
+  // Only expect `PASSWORDS` since descriptions of `CONTACT_INFO` are empty.
   EXPECT_CALL(
       delegate_mock(),
       ShowBatchUploadDialog(
-          _, std::vector<syncer::LocalDataDescription>{contact_info}, _, _));
+          _, std::vector<syncer::LocalDataDescription>{passwords}, _, _));
   EXPECT_CALL(opened_callback, Run(true)).Times(1);
   service.OpenBatchUpload(
       nullptr, BatchUploadService::EntryPoint::kPasswordManagerSettings,
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 fdbca07..7c400e0 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -132,6 +132,7 @@
 #include "chrome/browser/password_manager/password_manager_settings_service_factory.h"
 #include "chrome/browser/password_manager/password_reuse_manager_factory.h"
 #include "chrome/browser/password_manager/profile_password_store_factory.h"
+#include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.h"
 #include "chrome/browser/permissions/notifications_engagement_service_factory.h"
 #include "chrome/browser/permissions/one_time_permissions_tracker_factory.h"
 #include "chrome/browser/permissions/origin_keyed_permission_action_service_factory.h"
@@ -271,6 +272,7 @@
 #include "printing/buildflags/buildflags.h"
 #include "services/network/public/cpp/features.h"
 #include "third_party/blink/public/common/features_generated.h"
+#include "ui/base/device_form_factor.h"
 
 // Per-platform #include blocks, in alphabetical order.
 
@@ -322,7 +324,6 @@
 #include "chrome/browser/new_tab_page/promos/promo_service_factory.h"
 #include "chrome/browser/passage_embeddings/passage_embeddings_coordinator_factory.h"
 #include "chrome/browser/payments/payment_request_display_manager_factory.h"
-#include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.h"
 #include "chrome/browser/privacy_sandbox/privacy_sandbox_survey_desktop_controller_factory.h"
 #include "chrome/browser/profile_resetter/reset_report_uploader_factory.h"
 #include "chrome/browser/screen_ai/screen_ai_service_router_factory.h"
@@ -1110,7 +1111,11 @@
   payments::HasEnrolledInstrumentQueryFactory::GetInstance();
 #if !BUILDFLAG(IS_ANDROID)
   payments::PaymentRequestDisplayManagerFactory::GetInstance();
-  performance_manager::SiteDataCacheFacadeFactory::GetInstance();
+#endif
+  if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_DESKTOP) {
+    performance_manager::SiteDataCacheFacadeFactory::GetInstance();
+  }
+#if !BUILDFLAG(IS_ANDROID)
   PerformanceControlsHatsServiceFactory::GetInstance();
 #endif
   PermissionActionsHistoryFactory::GetInstance();
diff --git a/chrome/browser/readaloud/android/BUILD.gn b/chrome/browser/readaloud/android/BUILD.gn
index 3a30f9af..9040bb8 100644
--- a/chrome/browser/readaloud/android/BUILD.gn
+++ b/chrome/browser/readaloud/android/BUILD.gn
@@ -293,6 +293,7 @@
     "java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuSheetContent.java",
     "java/src/org/chromium/chrome/browser/readaloud/player/expanded/NegativeFeedbackMenuSheetContent.java",
     "java/src/org/chromium/chrome/browser/readaloud/player/expanded/OptionsMenuSheetContent.java",
+    "java/src/org/chromium/chrome/browser/readaloud/player/expanded/PlaybackModeIphController.java",
     "java/src/org/chromium/chrome/browser/readaloud/player/expanded/SingleMenuSheetContent.java",
     "java/src/org/chromium/chrome/browser/readaloud/player/expanded/SpeedMenuSheetContent.java",
     "java/src/org/chromium/chrome/browser/readaloud/player/expanded/VoiceMenu.java",
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinator.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinator.java
index 33c114a..8ab1ae02 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinator.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/PlayerCoordinator.java
@@ -6,6 +6,7 @@
 
 import android.content.Context;
 import android.view.ViewStub;
+
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.BundleUtils;
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerCoordinator.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerCoordinator.java
index f97422c..c9a4617 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerCoordinator.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerCoordinator.java
@@ -107,7 +107,10 @@
                 model,
                 new ExpandedPlayerMediator(model),
                 new ExpandedPlayerSheetContent(
-                        context, delegate.getBottomSheetController(), model));
+                        context,
+                        delegate.getBottomSheetController(),
+                        model,
+                        new PlaybackModeIphController(delegate.getUserEducationHelper())));
     }
 
     @VisibleForTesting
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContent.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContent.java
index 2068909..edb38d2 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContent.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContent.java
@@ -74,14 +74,20 @@
     private final LinearLayout mErrorLayout;
     private final LinearLayout mLoadingLayout;
 
+    private final PlaybackModeIphController mPlaybackModeIphController;
+
     public ExpandedPlayerSheetContent(
-            Context context, BottomSheetController bottomSheetController, PropertyModel model) {
+            Context context,
+            BottomSheetController bottomSheetController,
+            PropertyModel model,
+            PlaybackModeIphController playbackModeIphController) {
         this(
                 context,
                 bottomSheetController,
                 LayoutInflater.from(context)
                         .inflate(R.layout.readaloud_expanded_player_layout, null),
-                model);
+                model,
+                playbackModeIphController);
     }
 
     @VisibleForTesting
@@ -89,11 +95,14 @@
             Context context,
             BottomSheetController bottomSheetController,
             View contentView,
-            PropertyModel model) {
+            PropertyModel model,
+            PlaybackModeIphController playbackModeIphController) {
         mContext = context;
         mBottomSheetController = bottomSheetController;
         mContentView = contentView;
         mModel = model;
+        mPlaybackModeIphController = playbackModeIphController;
+
         Resources res = mContext.getResources();
         mSpeedButton = (TextView) mContentView.findViewById(R.id.readaloud_playback_speed);
         mContentView
@@ -119,6 +128,7 @@
         mScrollView = (ScrollView) mContentView.findViewById(R.id.scroll_view);
         mModeSelectorButton = mContentView.findViewById(R.id.readaloud_mode_selector);
         mModeSelectorButton.setSelected(mIsModeActive);
+        mPlaybackModeIphController.setAnchorView(mModeSelectorButton);
 
         mLoadingTextView = mContentView.findViewById(R.id.readaloud_loading_text);
 
@@ -230,9 +240,13 @@
     }
 
     public void show() {
-        mBottomSheetController.requestShowContent(this, /* animate= */ true);
+        boolean shown = mBottomSheetController.requestShowContent(this, /* animate= */ true);
         // Reset scrolling if needed.
         mScrollView.scrollTo(0, 0);
+
+        if (shown) {
+          mPlaybackModeIphController.maybeShowPlaybackModeIph();
+        }
     }
 
     public void hide() {
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java
index 0ce5e6a..65ef336 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java
@@ -54,6 +54,7 @@
     @Mock private OptionsMenuSheetContent mOptionsMenu;
     @Mock private SpeedMenuSheetContent mSpeedMenu;
     @Mock private View.OnClickListener mOnClickListener;
+    @Mock private PlaybackModeIphController mPlaybackModeIphController;
 
     private Context mContext;
     private Drawable mPlayDrawable;
@@ -96,7 +97,7 @@
         mErrorLayout = (LinearLayout) mContentView.findViewById(R.id.error_layout);
         mContent =
                 new ExpandedPlayerSheetContent(
-                        mActivity, mBottomSheetController, mContentView, mModel);
+                        mActivity, mBottomSheetController, mContentView, mModel, mPlaybackModeIphController);
         mContent.setOptionsMenuSheetContent(mOptionsMenu);
         mContent.setSpeedMenuSheetContent(mSpeedMenu);
         // PlayerMediator is responsible for setting initial speed.
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/PlaybackModeIphController.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/PlaybackModeIphController.java
new file mode 100644
index 0000000..42e3b0a8
--- /dev/null
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/PlaybackModeIphController.java
@@ -0,0 +1,56 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.readaloud.player.expanded;
+
+import android.view.View;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.task.TaskTraits;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.readaloud.player.R;
+import org.chromium.chrome.browser.user_education.IphCommandBuilder;
+import org.chromium.chrome.browser.user_education.UserEducationHelper;
+import org.chromium.components.feature_engagement.FeatureConstants;
+
+@NullMarked
+class PlaybackModeIphController {
+  private static final int TOOLTIP_DELAY_MILLIS = 500;
+
+  private final UserEducationHelper mUserEducationHelper;
+  private @Nullable View mAnchorView;
+
+  PlaybackModeIphController(UserEducationHelper userEducationHelper) {
+    mUserEducationHelper = userEducationHelper;
+  }
+
+  public void setAnchorView(View anchorView) {
+    mAnchorView = anchorView;
+  }
+
+  /** Request to show IPH for playback mode button. */
+  void maybeShowPlaybackModeIph() {
+    if (mAnchorView == null || mAnchorView.getVisibility() != View.VISIBLE) {
+      return;
+    }
+
+    PostTask.postDelayedTask(
+        TaskTraits.UI_DEFAULT,
+        () -> {
+          if (mAnchorView == null || mAnchorView.getVisibility() != View.VISIBLE) {
+            return;
+          }
+          mUserEducationHelper.requestShowIph(
+              new IphCommandBuilder(
+                      mAnchorView.getContext().getResources(),
+                      FeatureConstants.READ_ALOUD_PLAYBACK_MODE_FEATURE,
+                      R.string.readaloud_playback_mode_iph,
+                      R.string.readaloud_playback_mode_iph)
+                  .setAnchorView(mAnchorView)
+                  .setShowTextBubble(true)
+                  .build());
+        },
+        /* delay= */ TOOLTIP_DELAY_MILLIS);
+  }
+}
diff --git a/chrome/browser/readaloud/android/resources/BUILD.gn b/chrome/browser/readaloud/android/resources/BUILD.gn
index f54ade2..53f39a5 100644
--- a/chrome/browser/readaloud/android/resources/BUILD.gn
+++ b/chrome/browser/readaloud/android/resources/BUILD.gn
@@ -2,15 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/locales.gni")
 import("//chrome/common/features.gni")
 import("//tools/grit/grit_rule.gni")
 
 java_strings_grd("ui_strings_grd") {
   defines = chrome_grit_defines
   grd_file = "android_readaloud_strings.grd"
-  outputs = [ "values/android_readaloud_strings.xml" ] +
-            process_file_template(
-                android_bundle_locales_as_resources,
-                [ "values-{{source_name_part}}/android_readaloud_strings.xml" ])
 }
diff --git a/chrome/browser/readaloud/android/resources/android_readaloud_strings.grd b/chrome/browser/readaloud/android/resources/android_readaloud_strings.grd
index 41366f825..d6c7128 100644
--- a/chrome/browser/readaloud/android/resources/android_readaloud_strings.grd
+++ b/chrome/browser/readaloud/android/resources/android_readaloud_strings.grd
@@ -342,6 +342,9 @@
       <message name="IDS_READALOUD_EXPANDED_PLAYER_IPH" desc="Text shown on in-product-help bubble to inform a user that tapping on the mini player will expand it, allowing to access more options.">
           Tap to expand the player for more options.
       </message>
+      <message name="IDS_READALOUD_PLAYBACK_MODE_IPH" desc="Text shown on IPH for playback mode selector.">
+          Tap to switch playback mode
+      </message>
       <message name="IDS_READALOUD_VOICE_DISPLAY_NAME_RUBY" desc="Display name of a text-to-speech voice named Ruby.">
           Ruby
       </message>
diff --git a/chrome/browser/readaloud/android/resources/android_readaloud_strings_grd/IDS_READALOUD_PLAYBACK_MODE_IPH.png.sha1 b/chrome/browser/readaloud/android/resources/android_readaloud_strings_grd/IDS_READALOUD_PLAYBACK_MODE_IPH.png.sha1
new file mode 100644
index 0000000..959341a
--- /dev/null
+++ b/chrome/browser/readaloud/android/resources/android_readaloud_strings_grd/IDS_READALOUD_PLAYBACK_MODE_IPH.png.sha1
@@ -0,0 +1 @@
+292be5477f5f8962f235948a848d854e4f1bcdc1
\ No newline at end of file
diff --git a/chrome/browser/recent_tabs/internal/BUILD.gn b/chrome/browser/recent_tabs/internal/BUILD.gn
index 38d57ede..23d8855e 100644
--- a/chrome/browser/recent_tabs/internal/BUILD.gn
+++ b/chrome/browser/recent_tabs/internal/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/config/android/rules.gni")
-import("//build/config/locales.gni")
 import("//chrome/common/features.gni")
 import("//tools/grit/grit_rule.gni")
 
@@ -102,11 +101,6 @@
 java_strings_grd("java_strings_grd") {
   defines = chrome_grit_defines
   grd_file = "android/java/strings/android_restore_tabs_strings.grd"
-  outputs =
-      [ "values/android_restore_tabs_strings.xml" ] +
-      process_file_template(
-          android_bundle_locales_as_resources,
-          [ "values-{{source_name_part}}/android_restore_tabs_strings.xml" ])
 }
 
 robolectric_library("junit") {
diff --git a/chrome/browser/resources/bookmarks/BUILD.gn b/chrome/browser/resources/bookmarks/BUILD.gn
index 2bd2a59..357d4a7 100644
--- a/chrome/browser/resources/bookmarks/BUILD.gn
+++ b/chrome/browser/resources/bookmarks/BUILD.gn
@@ -7,7 +7,11 @@
 build_webui("build") {
   grd_prefix = "bookmarks"
 
-  static_files = [ "bookmarks.html" ]
+  static_files = [
+    "bookmarks.html",
+    "images/batch_upload_bookmarks_promo.svg",
+    "images/batch_upload_bookmarks_promo_dark.svg",
+  ]
 
   ts_files = [
     "actions.ts",
@@ -32,8 +36,6 @@
     "item.ts",
     "list.html.ts",
     "list.ts",
-    "promo_card.html.ts",
-    "promo_card.ts",
     "reducers.ts",
     "router.ts",
     "store.ts",
@@ -44,6 +46,13 @@
     "util.ts",
   ]
 
+  if (!is_chromeos) {
+    ts_files += [
+      "promo_card.html.ts",
+      "promo_card.ts",
+    ]
+  }
+
   icons_html_files = [ "icons.html" ]
   html_to_wrapper_template = "detect"
 
@@ -53,12 +62,15 @@
     "folder_node.css",
     "item.css",
     "list.css",
-    "promo_card.css",
     "shared_style_lit.css",
     "shared_vars.css",
     "toolbar.css",
   ]
 
+  if (!is_chromeos) {
+    css_files += [ "promo_card.css" ]
+  }
+
   ts_composite = true
   ts_definitions = [
     "//tools/typescript/definitions/bookmark_manager_private.d.ts",
diff --git a/chrome/browser/resources/bookmarks/browser_proxy.ts b/chrome/browser/resources/bookmarks/browser_proxy.ts
index 3cea3a2..386ff30 100644
--- a/chrome/browser/resources/bookmarks/browser_proxy.ts
+++ b/chrome/browser/resources/bookmarks/browser_proxy.ts
@@ -9,8 +9,7 @@
 // This is the data structure that is received from the browser.
 export interface BatchUploadPromoData {
   canShow: boolean;
-  localBookmarksCount: number;
-  email: string;
+  promoSubtitle: string;
 }
 
 export interface BrowserProxy {
diff --git a/chrome/browser/resources/bookmarks/images/batch_upload_bookmarks_promo.svg b/chrome/browser/resources/bookmarks/images/batch_upload_bookmarks_promo.svg
new file mode 100644
index 0000000..b497ba8
--- /dev/null
+++ b/chrome/browser/resources/bookmarks/images/batch_upload_bookmarks_promo.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="200" height="108" fill="none" viewBox="0 0 200 108"><rect width="42" height="31" x="147.129" y="64.541" fill="#D2E3FC" rx="15.5" transform="rotate(13.447 147.129 64.541)"/><rect width="28" height="21" x="28" y="77.297" fill="#D2E3FC" rx="10.5" transform="rotate(-12.566 28 77.297)"/><rect width="34.987" height="25.371" x="142.923" y="57.995" fill="#D2E3FC" stroke="#1B6EF3" stroke-width="2" rx="3" transform="rotate(13.113 142.923 57.995)"/><path fill="#1A73E8" d="m132.028 84.053 43.228 10.07a2.22 2.22 0 0 1-2.665 1.657l-38.905-9.062a2.22 2.22 0 0 1-1.658-2.665Z"/><mask id="a" width="42" height="35" x="136" y="57" maskUnits="userSpaceOnUse" style="mask-type:alpha"><rect width="36.987" height="27.371" x="142.176" y="56.794" fill="#D2E3FC" rx="4" transform="rotate(13.113 142.176 56.794)"/></mask><g mask="url(#a)"><path fill="#000" d="m175.526 79.24-36.843-.48c-4.473-.058-5.653 6.152-1.471 7.74l35.454 13.453c2.45.93 5.117-.708 5.396-3.314l1.39-12.974a4 4 0 0 0-3.926-4.426Z"/></g><rect width="34.987" height="25.371" x="142.923" y="57.995" fill="#D2E3FC" stroke="#1B6EF3" stroke-width="2" rx="3" transform="rotate(13.113 142.923 57.995)"/><mask id="b" width="42" height="35" x="136" y="57" maskUnits="userSpaceOnUse" style="mask-type:alpha"><rect width="36.987" height="27.371" x="142.176" y="56.794" fill="#D2E3FC" rx="4" transform="rotate(13.113 142.176 56.794)"/></mask><g mask="url(#b)"><path fill="#AECBFA" d="m175.526 79.24-36.843-.48c-4.473-.058-5.653 6.152-1.471 7.74l35.454 13.453c2.45.93 5.117-.708 5.396-3.314l1.39-12.974a4 4 0 0 0-3.926-4.426Z"/></g><rect width="34.987" height="25.371" x="142.923" y="57.995" stroke="#1B6EF3" stroke-width="2" rx="3" transform="rotate(13.113 142.923 57.995)"/><rect width="14.806" height="29.035" x="34.428" y="60.062" fill="#D2E3FC" stroke="#1B6EF3" stroke-width="2" rx="3" transform="rotate(-12.516 34.428 60.062)"/><mask id="c" width="18" height="29" x="36" y="58" maskUnits="userSpaceOnUse" style="mask-type:alpha"><rect width="12.806" height="27.035" x="35.621" y="60.821" fill="#D2E3FC" rx="2" transform="rotate(-12.516 35.621 60.821)"/></mask><g mask="url(#c)"><path fill="#AECBFA" d="m57.327 78.599-20.85-2.363a4 4 0 0 0-4.439 4.29l1.085 13.733a4 4 0 0 0 5.056 3.54l20.222-5.607a4 4 0 0 0 2.918-4.17l-.455-5.764a4 4 0 0 0-3.537-3.66Z"/></g><path fill="#1B6EF3" d="m87.648 34.962-3.628-11.55a1.4 1.4 0 0 1 .096-1.108c.185-.354.47-.592.855-.713l6.468-2.031a1.4 1.4 0 0 1 1.109.095 1.4 1.4 0 0 1 .712.855l3.628 11.55-5.2-.397-4.04 3.3Zm.74-2.495 2.828-2.305 3.64.274-2.982-9.49-6.468 2.031 2.981 9.49Zm-2.982-9.49 6.468-2.031-3.234 1.015-3.234 1.016Zm20.304 6.525 2.873-.876 2.164 2.109.024-2.986 2.632-1.329-2.819-.96-.499-2.968-1.814 2.402-2.943-.45 1.739 2.399-1.357 2.659Zm-2.851 2.397 2.511-4.911-3.257-4.515 5.524.853 3.321-4.4.908 5.456 5.299 1.775-4.975 2.5-.041 5.516-3.969-3.9-5.321 1.626Z"/><circle cx="3.562" cy="3.562" r="3.562" fill="#FBBC04" transform="scale(1 -1) rotate(40.924 84.545 80.835)"/><circle cx="2" cy="2" r="2" fill="#D2E3FC" transform="scale(1 -1) rotate(40.924 96.146 50.482)"/><path fill="#FCC934" d="M65.894 36.557a1 1 0 0 1 .66 1.602l-2.644 3.43a1 1 0 0 1-1.717-.23l-1.648-4.004a1 1 0 0 1 1.057-1.372l4.292.574Z"/><circle cx="140.768" cy="54.109" r="1.626" fill="#D2E3FC" transform="rotate(-157.147 140.768 54.11)"/><circle cx="53.079" cy="55.13" r="1.626" fill="#D2E3FC" transform="rotate(-157.147 53.08 55.13)"/><circle cx="137.698" cy="47.749" r="2.098" fill="#D2E3FC" transform="rotate(-157.147 137.698 47.749)"/><circle cx="131.879" cy="40.93" r="3" fill="#5BB974" transform="rotate(-157.147 131.879 40.93)"/><path stroke="#D2E3FC" stroke-linecap="round" stroke-width="2" d="m99.95 6 .499 5.5m21.732 3.714-2.464 2.21M77.725 16.17l2.099 2.296m.126 22.092L82.048 38m36.7 1.537L116.949 37"/><rect width="7" height="7" x="121.572" y="28" fill="#34A853" rx="2" transform="rotate(13.403 121.572 28)"/><path stroke="#D2E3FC" stroke-linecap="round" stroke-width="2" d="M57.95 62c16.5-22 52.32-31.504 76.999-1.91"/><path stroke="#D2E3FC" stroke-linecap="round" stroke-width="2" d="M63.95 66c14-19 45.499-25 64.999-3"/><path fill="#D2E3FC" d="M136.382 57.056a1 1 0 0 0-1.614-.642l-3.588 2.86a1 1 0 0 0 .253 1.711l4.232 1.684a1 1 0 0 0 1.359-1.07l-.642-4.543ZM63.261 62.18a1 1 0 0 1 1.72-.242l2.807 3.631a1 1 0 0 1-.653 1.602l-4.51.63a1 1 0 0 1-1.067-1.36l1.703-4.262Z"/></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/bookmarks/images/batch_upload_bookmarks_promo_dark.svg b/chrome/browser/resources/bookmarks/images/batch_upload_bookmarks_promo_dark.svg
new file mode 100644
index 0000000..b418dcf
--- /dev/null
+++ b/chrome/browser/resources/bookmarks/images/batch_upload_bookmarks_promo_dark.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="200" height="108" fill="none" viewBox="0 0 200 108"><rect width="42" height="31" x="147.129" y="64.541" fill="#3C4043" rx="15.5" transform="rotate(13.447 147.129 64.541)"/><rect width="28" height="21" x="28" y="77.297" fill="#3C4043" rx="10.5" transform="rotate(-12.566 28 77.297)"/><rect width="34.987" height="25.371" x="142.923" y="57.995" fill="#185ABC" stroke="#1B6EF3" stroke-width="2" rx="3" transform="rotate(13.113 142.923 57.995)"/><path fill="#1A73E8" d="m132.028 84.053 43.228 10.07a2.22 2.22 0 0 1-2.665 1.657l-38.905-9.062a2.22 2.22 0 0 1-1.658-2.665Z"/><mask id="a" width="42" height="35" x="136" y="57" maskUnits="userSpaceOnUse" style="mask-type:alpha"><rect width="36.987" height="27.371" x="142.176" y="56.794" fill="#D2E3FC" rx="4" transform="rotate(13.113 142.176 56.794)"/></mask><g mask="url(#a)"><path fill="#000" d="m175.526 79.24-36.843-.48c-4.473-.058-5.653 6.152-1.471 7.74l35.454 13.453c2.45.93 5.117-.708 5.396-3.314l1.39-12.974a4 4 0 0 0-3.926-4.426Z"/></g><rect width="34.987" height="25.371" x="142.923" y="57.995" fill="#185ABC" stroke="#1B6EF3" stroke-width="2" rx="3" transform="rotate(13.113 142.923 57.995)"/><mask id="b" width="42" height="35" x="136" y="57" maskUnits="userSpaceOnUse" style="mask-type:alpha"><rect width="36.987" height="27.371" x="142.176" y="56.794" fill="#D2E3FC" rx="4" transform="rotate(13.113 142.176 56.794)"/></mask><g mask="url(#b)"><path fill="#1967D2" d="m175.526 79.24-36.843-.48c-4.473-.058-5.653 6.152-1.471 7.74l35.454 13.453c2.45.93 5.117-.708 5.396-3.314l1.39-12.974a4 4 0 0 0-3.926-4.426Z"/></g><rect width="34.987" height="25.371" x="142.923" y="57.995" stroke="#4285F4" stroke-width="2" rx="3" transform="rotate(13.113 142.923 57.995)"/><rect width="14.806" height="29.035" x="34.428" y="60.062" fill="#185ABC" stroke="#4285F4" stroke-width="2" rx="3" transform="rotate(-12.516 34.428 60.062)"/><mask id="c" width="18" height="29" x="36" y="58" maskUnits="userSpaceOnUse" style="mask-type:alpha"><rect width="12.806" height="27.035" x="35.621" y="60.821" fill="#D2E3FC" rx="2" transform="rotate(-12.516 35.621 60.821)"/></mask><g mask="url(#c)"><path fill="#1967D2" d="m57.327 78.599-20.85-2.363a4 4 0 0 0-4.439 4.29l1.085 13.733a4 4 0 0 0 5.056 3.54l20.222-5.607a4 4 0 0 0 2.918-4.17l-.455-5.764a4 4 0 0 0-3.537-3.66Z"/></g><path fill="#4285F4" d="m87.648 34.962-3.628-11.55a1.4 1.4 0 0 1 .096-1.108c.185-.354.47-.592.855-.713l6.468-2.031a1.4 1.4 0 0 1 1.109.095 1.4 1.4 0 0 1 .712.855l3.628 11.55-5.2-.397-4.04 3.3Zm.74-2.495 2.828-2.305 3.64.274-2.982-9.49-6.468 2.031 2.981 9.49Zm-2.982-9.49 6.468-2.031-3.234 1.015-3.234 1.016Zm20.304 6.525 2.873-.876 2.164 2.109.024-2.986 2.632-1.329-2.819-.96-.499-2.968-1.814 2.402-2.943-.45 1.739 2.399-1.357 2.659Zm-2.851 2.397 2.511-4.911-3.257-4.515 5.524.853 3.321-4.4.908 5.456 5.299 1.775-4.975 2.5-.041 5.516-3.969-3.9-5.321 1.626Z"/><circle cx="3.562" cy="3.562" r="3.562" fill="#FBBC04" transform="scale(1 -1) rotate(40.924 84.545 80.835)"/><circle cx="2" cy="2" r="2" fill="#174EA6" transform="scale(1 -1) rotate(40.924 96.146 50.482)"/><path fill="#F9AB00" d="M65.894 36.557a1 1 0 0 1 .66 1.602l-2.644 3.43a1 1 0 0 1-1.717-.23l-1.648-4.004a1 1 0 0 1 1.057-1.372l4.292.574Z"/><circle cx="140.768" cy="54.109" r="1.626" fill="#174EA6" transform="rotate(-157.147 140.768 54.11)"/><circle cx="53.079" cy="55.13" r="1.626" fill="#174EA6" transform="rotate(-157.147 53.08 55.13)"/><circle cx="137.698" cy="47.749" r="2.098" fill="#174EA6" transform="rotate(-157.147 137.698 47.749)"/><circle cx="131.879" cy="40.93" r="3" fill="#1E8E3E" transform="rotate(-157.147 131.879 40.93)"/><path stroke="#174EA6" stroke-linecap="round" stroke-width="2" d="m99.95 6 .499 5.5m21.732 3.714-2.464 2.21M77.725 16.17l2.099 2.296m.126 22.092L82.048 38m36.7 1.537L116.949 37"/><rect width="7" height="7" x="121.572" y="28" fill="#34A853" rx="2" transform="rotate(13.403 121.572 28)"/><path stroke="#174EA6" stroke-linecap="round" stroke-width="2" d="M57.95 62c16.5-22 52.32-31.504 76.999-1.91"/><path stroke="#174EA6" stroke-linecap="round" stroke-width="2" d="M63.95 66c14-19 45.499-25 64.999-3"/><path fill="#174EA6" d="M136.382 57.056a1 1 0 0 0-1.614-.642l-3.588 2.86a1 1 0 0 0 .253 1.711l4.232 1.684a1 1 0 0 0 1.359-1.07l-.642-4.543ZM63.261 62.18a1 1 0 0 1 1.72-.242l2.807 3.631a1 1 0 0 1-.653 1.602l-4.51.63a1 1 0 0 1-1.067-1.36l1.703-4.262Z"/></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/bookmarks/list.css b/chrome/browser/resources/bookmarks/list.css
index 0eb62f6..2b02a6d 100644
--- a/chrome/browser/resources/bookmarks/list.css
+++ b/chrome/browser/resources/bookmarks/list.css
@@ -31,6 +31,7 @@
 
 #promoCard {
   margin-bottom: 18px;
+  padding: 0px;
 }
 
 .centered-message {
diff --git a/chrome/browser/resources/bookmarks/list.html.ts b/chrome/browser/resources/bookmarks/list.html.ts
index 544158a..4200989 100644
--- a/chrome/browser/resources/bookmarks/list.html.ts
+++ b/chrome/browser/resources/bookmarks/list.html.ts
@@ -9,10 +9,12 @@
 export function getHtml(this: BookmarksListElement) {
   // clang-format off
   return html`<!--_html_template_start_-->
-<promo-card id="promoCard" class="card"
-    ?hidden="${!this.shouldShowPromoCard_}"
-    @on-should-show-promo-card="${this.updateShouldShowPromoCard_}">
-</promo-card>
+<if expr="not is_chromeos">
+  <promo-card id="promoCard" class="card"
+      ?hidden="${!this.shouldShowPromoCard_}"
+      @on-should-show-promo-card="${this.updateShouldShowPromoCard_}">
+  </promo-card>
+</if>
 <cr-lazy-list id="list" class="card"
     .items="${this.displayedIds_}"
     .scrollTarget="${this}"
diff --git a/chrome/browser/resources/bookmarks/list.ts b/chrome/browser/resources/bookmarks/list.ts
index 809f049..d7cab1dd 100644
--- a/chrome/browser/resources/bookmarks/list.ts
+++ b/chrome/browser/resources/bookmarks/list.ts
@@ -5,7 +5,9 @@
 import 'chrome://resources/cr_elements/cr_lazy_list/cr_lazy_list.js';
 import '/strings.m.js';
 import './item.js';
+// <if expr="not is_chromeos">
 import './promo_card.js';
+// </if>
 
 import {getInstance as getAnnouncerInstance} from 'chrome://resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.js';
 import type {CrLazyListElement} from 'chrome://resources/cr_elements/cr_lazy_list/cr_lazy_list.js';
diff --git a/chrome/browser/resources/bookmarks/promo_card.css b/chrome/browser/resources/bookmarks/promo_card.css
index 8feda9e..774885e 100644
--- a/chrome/browser/resources/bookmarks/promo_card.css
+++ b/chrome/browser/resources/bookmarks/promo_card.css
@@ -14,12 +14,28 @@
 }
 
 #promoCard {
+  column-gap: 8px;
   display: flex;
   flex-direction: row;
+  padding: 12px 16px;
 }
+
 #image {
-  height: 120px;
-  width: 248px;
+  content: url(images/batch_upload_bookmarks_promo.svg);
+  margin: auto;
+}
+
+#title {
+  font-size: 14px;
+  font-weight: 500;
+  line-height: 20px;
+  margin: 0px;
+}
+
+#description {
+  font-size: 12px;
+  font-weight: 400;
+  line-height: 16px;
 }
 
 #promoContent {
@@ -27,18 +43,17 @@
   flex: 1;
   flex-direction: column;
   justify-content: center;
-  margin-block: 16px;
-  margin-inline-end: 6px;
-  margin-inline-start: 24px;
+  row-gap: 4px;
 }
 
 #actionButton {
-  margin-top: 6px;
-  width: fit-content
+  margin-bottom: 4px;
+  margin-top: 8px;
+  width: fit-content;
 }
 
-#closeButton {
-  margin-top: 10px;
-  margin-inline-end: 10px;
-  margin-bottom: auto;
+@media (prefers-color-scheme: dark) {
+  #image {
+    content: url(images/batch_upload_bookmarks_promo_dark.svg);
+  }
 }
diff --git a/chrome/browser/resources/bookmarks/promo_card.html.ts b/chrome/browser/resources/bookmarks/promo_card.html.ts
index 55af42f5..96488a7 100644
--- a/chrome/browser/resources/bookmarks/promo_card.html.ts
+++ b/chrome/browser/resources/bookmarks/promo_card.html.ts
@@ -7,32 +7,23 @@
 import type {PromoCardElement} from './promo_card.js';
 
 export function getHtml(this: PromoCardElement) {
-  // TODO(crbug.com/411439295): replace fixed strings with translatable strings.
   return html`<!--_html_template_start_-->
 <div id="promoCard" role="dialog">
-  <picture id="image">
-    <source class="banner" srcset="use_real_image.svg "
-        media="(prefers-color-scheme: dark)">
-    <img class="banner" alt="" src="use_real_image_svg.svg ">
-  </picture>
+  <img id="image" class="banner" alt="">
   <div id="promoContent">
     <h2 id="title" class="label">
-      Get your bookmarks and more on all your devices
+      $i18n{bookmarkPromoCardTitle}
     </h2>
     <div id="description" class="cr-secondary-text label">
-      ${
-      this.batchUploadPromoData_
-          .localBookmarksCount} bookmarks and other items are saved only to this device. To use them on your other devices, save them in your Google account, ${
-      this.batchUploadPromoData_.email}
+      ${this.batchUploadPromoData_.promoSubtitle}
     </div>
     <cr-button id="actionButton"
         class="action-button" @click="${this.onSaveToAccountClick_}">
-      Save to account
+      $i18n{saveToAccount}
     </cr-button>
   </div>
   <cr-icon-button id="closeButton" class="icon-clear no-overlap"
-      @click="${this.onCloseClick_}" title="Close"
-      aria-label="Aria close">
+      @click="${this.onCloseClick_}" title="$i18n{close}">
   </cr-icon-button>
 </div>
 <!--_html_template_end_-->`;
diff --git a/chrome/browser/resources/bookmarks/promo_card.ts b/chrome/browser/resources/bookmarks/promo_card.ts
index e422916c..38540e0 100644
--- a/chrome/browser/resources/bookmarks/promo_card.ts
+++ b/chrome/browser/resources/bookmarks/promo_card.ts
@@ -26,7 +26,6 @@
   };
 }
 
-// TODO(crbug.com/411439295): Add `I18nMixinLit` when using real strings.
 const PromoCardElementBase = WebUiListenerMixinLit(CrLitElement);
 
 export class PromoCardElement extends PromoCardElementBase {
@@ -50,8 +49,7 @@
 
   protected accessor batchUploadPromoData_: BatchUploadPromoData = {
     canShow: false,
-    localBookmarksCount: 0,
-    email: '',
+    promoSubtitle: '',
   };
 
   override connectedCallback() {
diff --git a/chrome/browser/resources/chromeos/BUILD.gn b/chrome/browser/resources/chromeos/BUILD.gn
index ec9fd76..8ebe116 100644
--- a/chrome/browser/resources/chromeos/BUILD.gn
+++ b/chrome/browser/resources/chromeos/BUILD.gn
@@ -28,6 +28,7 @@
     "edu_coexistence:resources",
     "emoji_picker:resources",
     "enterprise_reporting:resources",
+    "floating_workspace:resources",
     "gaia_action_buttons:resources",
     "healthd_internals:resources",
     "input_method:resources",
diff --git a/chrome/browser/resources/chromeos/floating_workspace/BUILD.gn b/chrome/browser/resources/chromeos/floating_workspace/BUILD.gn
new file mode 100644
index 0000000..c20ebf1
--- /dev/null
+++ b/chrome/browser/resources/chromeos/floating_workspace/BUILD.gn
@@ -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.
+
+import("//ui/webui/resources/tools/build_webui.gni")
+
+build_webui("build") {
+  grd_prefix = "floating_workspace"
+
+  static_files = [ "floating_workspace.html" ]
+
+  web_component_files = [ "app.ts" ]
+  ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
+
+  ts_deps = [
+    "//ash/webui/common/resources/cr_elements:build_ts",
+    "//chrome/browser/resources/chromeos/login:build_ts",
+    "//third_party/cros-components:cros_components_ts",
+    "//third_party/polymer/v3_0:library",
+    "//ui/webui/resources/cr_components/color_change_listener:build_ts",
+    "//ui/webui/resources/js:build_ts",
+    "//ui/webui/resources/mojo:build_ts",
+  ]
+  ts_path_mappings = [ "/components/*|" + rebase_path(
+                           "${root_gen_dir}/chrome/browser/resources/chromeos/login/tsc/components/*",
+                           target_gen_dir) ]
+
+  webui_context_type = "trusted"
+}
diff --git a/chrome/browser/resources/chromeos/floating_workspace/OWNERS b/chrome/browser/resources/chromeos/floating_workspace/OWNERS
new file mode 100644
index 0000000..c7981330
--- /dev/null
+++ b/chrome/browser/resources/chromeos/floating_workspace/OWNERS
@@ -0,0 +1,2 @@
+slutskii@google.com
+andreydav@google.com
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/floating_workspace/app.html b/chrome/browser/resources/chromeos/floating_workspace/app.html
new file mode 100644
index 0000000..8939c2573
--- /dev/null
+++ b/chrome/browser/resources/chromeos/floating_workspace/app.html
@@ -0,0 +1,38 @@
+<!--
+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.
+-->
+
+<style include="oobe-dialog-host-styles">
+  #floatingDialog {
+    width: 100%;
+    height: 100%;
+    background-color: var(--cros-sys-app_base_shaded);
+  }
+
+  #checkingAnimation {
+    max-height: 90%;
+  }
+</style>
+
+<oobe-adaptive-dialog id="floatingDialog"
+  tabindex="0" aria-live="polite" footer-shrinkable>
+  <iron-icon slot="icon" icon="oobe-32:googleg"></iron-icon>
+  <h1 slot="title">
+    [[titleString]]
+  </h1>
+  <paper-progress slot="progress" id="checking-progress" indeterminate>
+  </paper-progress>
+  <div slot="content" class="flex layout vertical center-justified center">
+    <oobe-cr-lottie id="checkingAnimation"
+    animation-url="animations/checking_for_update.json" playing=true>
+    </oobe-cr-lottie>
+  </div>
+
+  <div slot="bottom-buttons">
+    <oobe-text-button id="cancelButton" on-click="onCancelButtonClick_"
+    text-key="floatingWorkspaceStartupDialogButton" border>
+    </oobe-text-button>
+  </div>
+</oobe-adaptive-dialog>
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/floating_workspace/app.ts b/chrome/browser/resources/chromeos/floating_workspace/app.ts
new file mode 100644
index 0000000..17459d40
--- /dev/null
+++ b/chrome/browser/resources/chromeos/floating_workspace/app.ts
@@ -0,0 +1,97 @@
+// 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 '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import '//resources/polymer/v3_0/paper-progress/paper-progress.js';
+import '/components/oobe_cr_lottie.js';
+import '/components/oobe_icons.html.js';
+import '/components/common_styles/oobe_dialog_host_styles.css.js';
+import '/components/dialogs/oobe_adaptive_dialog.js';
+
+import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {OobeAdaptiveDialog} from '/components/dialogs/oobe_adaptive_dialog.js';
+import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
+
+import {getTemplate} from './app.html.js';
+
+const EXPECTED_LOAD_TIME_MILLISEC = 5000;
+
+export interface FloatingWorkspace {
+  $: {
+    floatingDialog: OobeAdaptiveDialog,
+  };
+}
+
+const FloatingWorkspaceBase = I18nMixin(PolymerElement);
+
+export class FloatingWorkspace extends FloatingWorkspaceBase {
+  static get is() {
+    return 'floating-workspace' as const;
+  }
+
+  static get template(): HTMLTemplateElement {
+    return getTemplate();
+  }
+
+  // Main string of the dialog. It is changed after
+  // EXPECTED_LOAD_TIME_MILLISEC of time.
+  private titleString: string|'';
+
+
+  constructor() {
+    super();
+    this.titleString = this.i18n('floatingWorkspaceStartupDialogTitle');
+  }
+
+  override ready(): void {
+    super.ready();
+
+    // Needed to set the right size of <oobe-adaptive-dialog/> when the dialog
+    // is shown and when the window resolution changes (e.g. switching to
+    // landscape mode).
+    window.addEventListener('orientationchange', () => {
+      this.onWindowResolutionChange_();
+    });
+    window.addEventListener('resize', () => {
+      this.onWindowResolutionChange_();
+    });
+    this.onWindowResolutionChange_();
+
+    // Change title when floating workspace takes too long.
+    setTimeout(this.onNoResponse.bind(this), EXPECTED_LOAD_TIME_MILLISEC);
+    this.$.floatingDialog.onBeforeShow();
+    this.$.floatingDialog.show();
+  }
+
+  private onNoResponse(): void {
+    this.titleString =
+        this.i18n('floatingWorkspaceStartupDialogLongResponseTitle');
+  }
+
+  private onWindowResolutionChange_(): void {
+    if (!document.documentElement.hasAttribute('screen')) {
+      document.documentElement.style.setProperty(
+          '--oobe-oobe-dialog-height-base', window.innerHeight + 'px');
+      document.documentElement.style.setProperty(
+          '--oobe-oobe-dialog-width-base', window.innerWidth + 'px');
+      if (window.innerWidth > window.innerHeight) {
+        document.documentElement.setAttribute('orientation', 'horizontal');
+      } else {
+        document.documentElement.setAttribute('orientation', 'vertical');
+      }
+    }
+  }
+
+  private onCancelButtonClick_(): void {
+    chrome.send('dialogClose', ['stopRestoringSession']);
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    [FloatingWorkspace.is]: FloatingWorkspace;
+  }
+}
+
+customElements.define(FloatingWorkspace.is, FloatingWorkspace);
diff --git a/chrome/browser/resources/chromeos/floating_workspace/floating_workspace.html b/chrome/browser/resources/chromeos/floating_workspace/floating_workspace.html
new file mode 100644
index 0000000..0e0874b
--- /dev/null
+++ b/chrome/browser/resources/chromeos/floating_workspace/floating_workspace.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html dir="$i18n{textdirection}" lang="$i18n{language}">
+
+<head>
+  <meta charset="utf-8">
+  <meta name="color-scheme" content="light dark">
+  <title></title>
+  <link rel="stylesheet" href="chrome://theme/colors.css?sets=sys">
+  <link rel="stylesheet" href="chrome://theme/typography.css">
+  <link rel="stylesheet" href="./oobe.css">
+</head>
+
+<body class="jelly-enabled">
+  <floating-workspace></floating-workspace>
+  <script type="module" src="app.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/chrome/browser/resources/new_tab_footer/BUILD.gn b/chrome/browser/resources/new_tab_footer/BUILD.gn
index eaa2216..b6f0cb4 100644
--- a/chrome/browser/resources/new_tab_footer/BUILD.gn
+++ b/chrome/browser/resources/new_tab_footer/BUILD.gn
@@ -23,6 +23,7 @@
   ts_composite = true
   ts_deps = [
     "//third_party/lit/v3_0:build_ts",
+    "//ui/webui/resources/cr_components/color_change_listener:build_ts",
     "//ui/webui/resources/js:build_ts",
     "//ui/webui/resources/mojo:build_ts",
   ]
diff --git a/chrome/browser/resources/new_tab_footer/app.css b/chrome/browser/resources/new_tab_footer/app.css
index ce758cc..7b2b17a 100644
--- a/chrome/browser/resources/new_tab_footer/app.css
+++ b/chrome/browser/resources/new_tab_footer/app.css
@@ -7,14 +7,6 @@
  * #scheme=relative
  * #css_wrapper_metadata_end */
 
-:host {
-  background-color: var(--color-new-tab-footer-background);
-  display: block;
-  height: 56px;
-  overflow-y: hidden;
-  width: 100%;
-}
-
 #centerContainer {
   display: flex;
   justify-content: center;
diff --git a/chrome/browser/resources/new_tab_footer/app.ts b/chrome/browser/resources/new_tab_footer/app.ts
index b7964b1..b2a0ea47 100644
--- a/chrome/browser/resources/new_tab_footer/app.ts
+++ b/chrome/browser/resources/new_tab_footer/app.ts
@@ -5,6 +5,7 @@
 import '/strings.m.js';
 
 import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
+import {ColorChangeUpdater} from 'chrome://resources/cr_components/color_change_listener/colors_css_updater.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 
 import {getCss} from './app.css.js';
@@ -42,6 +43,10 @@
     this.getNtpExtensionAttribution_();
   }
 
+  override firstUpdated() {
+    ColorChangeUpdater.forDocument().start();
+  }
+
   private async getNtpExtensionAttribution_() {
     this.extensionAttribution_ = (await NewTabFooterDocumentProxy.getInstance()
                                       .handler.getNtpExtensionAttribution())
diff --git a/chrome/browser/resources/new_tab_footer/new_tab_footer.html b/chrome/browser/resources/new_tab_footer/new_tab_footer.html
index 8ff1d4e..f39351c 100644
--- a/chrome/browser/resources/new_tab_footer/new_tab_footer.html
+++ b/chrome/browser/resources/new_tab_footer/new_tab_footer.html
@@ -4,6 +4,7 @@
     <meta charset="utf-8">
     <style>
       body {
+        background-color: var(--color-new-tab-footer-background);
         margin: 0;
       }
     </style>
diff --git a/chrome/browser/resources/pdf/constants.ts b/chrome/browser/resources/pdf/constants.ts
index dbb1a440..7e9556c 100644
--- a/chrome/browser/resources/pdf/constants.ts
+++ b/chrome/browser/resources/pdf/constants.ts
@@ -56,8 +56,6 @@
 export enum TextStyle {
   BOLD = 'bold',
   ITALIC = 'italic',
-  UNDERLINE = 'underline',
-  STRIKETHROUGH = 'strikethrough',
 }
 
 export type TextStyles = {
diff --git a/chrome/browser/resources/pdf/controller.ts b/chrome/browser/resources/pdf/controller.ts
index 257b103..c9be83854 100644
--- a/chrome/browser/resources/pdf/controller.ts
+++ b/chrome/browser/resources/pdf/controller.ts
@@ -111,6 +111,7 @@
   save(requestType: SaveRequestType): Promise<{
     fileName: string,
     dataToSave: ArrayBuffer,
+    bypassSaveFileForTesting?: boolean,
     editModeForTesting?: boolean,
   }|null>;
 
diff --git a/chrome/browser/resources/pdf/elements/icons.html b/chrome/browser/resources/pdf/elements/icons.html
index 4a866732..5e149da 100644
--- a/chrome/browser/resources/pdf/elements/icons.html
+++ b/chrome/browser/resources/pdf/elements/icons.html
@@ -54,8 +54,6 @@
     <g id="text-annotate" viewBox="0 -960 960 960"><path d="M280-160v-520H80v-120h520v120H400v520H280Zm360 0v-320H520v-120h360v120H760v320H640Z"></path></g>
     <g id="text-format-bold" viewBox="0 -960 960 960"><path d="M272-200v-560h221q65 0 120 40t55 111q0 51-23 78.5T602-491q25 11 55.5 41t30.5 90q0 89-65 124.5T501-200H272Zm121-112h104q48 0 58.5-24.5T566-372q0-11-10.5-35.5T494-432H393v120Zm0-228h93q33 0 48-17t15-38q0-24-17-39t-44-15h-95v109Z"></path></g>
     <g id="text-format-italic" viewBox="0 -960 960 960"><path d="M200-200v-100h160l120-360H320v-100h400v100H580L460-300h140v100H200Z"></path></g>
-    <g id="text-format-strikethrough" viewBox="0 -960 960 960"><path d="M486-160q-76 0-135-45t-85-123l88-38q14 48 48.5 79t85.5 31q42 0 76-20t34-64q0-18-7-33t-19-27h112q5 14 7.5 28.5T694-340q0 86-61.5 133T486-160ZM80-480v-80h800v80H80Zm402-326q66 0 115.5 32.5T674-674l-88 39q-9-29-33.5-52T484-710q-41 0-68 18.5T386-640h-96q2-69 54.5-117.5T482-806Z"></path></g>
-    <g id="text-format-underline" viewBox="0 -960 960 960"><path d="M200-120v-80h560v80H200Zm280-160q-101 0-157-63t-56-167v-330h103v336q0 56 28 91t82 35q54 0 82-35t28-91v-336h103v330q0 104-56 167t-157 63Z"></path></g>
 </if>
   </defs>
   </svg>
diff --git a/chrome/browser/resources/pdf/elements/ink_text_box.ts b/chrome/browser/resources/pdf/elements/ink_text_box.ts
index f806fc9..5dd8bc3 100644
--- a/chrome/browser/resources/pdf/elements/ink_text_box.ts
+++ b/chrome/browser/resources/pdf/elements/ink_text_box.ts
@@ -22,7 +22,7 @@
   };
 }
 
-enum TextBoxState {
+export enum TextBoxState {
   INACTIVE = 0,  // No active text annotation being edited; box is hidden.
   NEW = 1,  // Box initialized with an annotation, but user has not made edits.
   EDITED = 2,  // User has edited the annotation (position, text, style).
@@ -107,7 +107,7 @@
     super.disconnectedCallback();
     // This element is disconnected when the user exits text annotation mode.
     // Send the current annotation to the backend.
-    this.commitTextAnnotation_();
+    this.commitTextAnnotation();
     this.eventTracker_.removeAll();
   }
 
@@ -131,6 +131,7 @@
 
     if (changedPrivateProperties.has('state_')) {
       this.hidden = this.state_ === TextBoxState.INACTIVE;
+      this.fire('state-changed', this.state_);
     }
   }
 
@@ -186,7 +187,7 @@
     }
   }
 
-  private commitTextAnnotation_() {
+  commitTextAnnotation() {
     // If this is a new/inactive box or a new box edited to empty, nothing to do
     // unless it was initialized from an existing annotation. If this was
     // an existing annotation, we need to notify the backend to re-render it,
@@ -199,18 +200,20 @@
 
     // Notify the backend.
     assert(this.attributes_);
-    Ink2Manager.getInstance().commitTextAnnotation({
-      text: this.textValue_,
-      id: this.id_,
-      pageNumber: this.pageNumber_,
-      textAttributes: this.attributes_,
-      textBoxRect: {
-        height: this.height_,
-        locationX: this.locationX_,
-        locationY: this.locationY_,
-        width: this.width_,
-      },
-    });
+    Ink2Manager.getInstance().commitTextAnnotation(
+        {
+          text: this.textValue_,
+          id: this.id_,
+          pageNumber: this.pageNumber_,
+          textAttributes: this.attributes_,
+          textBoxRect: {
+            height: this.height_,
+            locationX: this.locationX_,
+            locationY: this.locationY_,
+            width: this.width_,
+          },
+        },
+        this.state_ === TextBoxState.EDITED);
 
     this.state_ = TextBoxState.INACTIVE;
   }
@@ -219,7 +222,7 @@
     // If we are already editing an annotation, commit it first before
     // switching to the new one.
     if (this.state_ !== TextBoxState.INACTIVE) {
-      this.commitTextAnnotation_();
+      this.commitTextAnnotation();
     }
 
     // Update is in screen coordinates.
@@ -350,14 +353,6 @@
         newAttributes.styles.italic ? 'italic' : 'normal';
     this.$.textbox.style.fontWeight =
         newAttributes.styles.bold ? 'bold' : 'normal';
-    let textDecoration = '';
-    if (newAttributes.styles.underline) {
-      textDecoration += 'underline ';
-    }
-    if (newAttributes.styles.strikethrough) {
-      textDecoration += 'line-through';
-    }
-    this.$.textbox.style.textDecoration = textDecoration || 'none';
     this.$.textbox.style.color = colorToHex(newAttributes.color);
   }
 
diff --git a/chrome/browser/resources/pdf/elements/text_styles_selector.ts b/chrome/browser/resources/pdf/elements/text_styles_selector.ts
index d034cef9..0b0dce2 100644
--- a/chrome/browser/resources/pdf/elements/text_styles_selector.ts
+++ b/chrome/browser/resources/pdf/elements/text_styles_selector.ts
@@ -36,8 +36,6 @@
   protected accessor currentStyles_: TextStyles = {
     [TextStyle.BOLD]: false,
     [TextStyle.ITALIC]: false,
-    [TextStyle.UNDERLINE]: false,
-    [TextStyle.STRIKETHROUGH]: false,
   };
 
   protected getTextStyles_(): TextStyle[] {
diff --git a/chrome/browser/resources/pdf/elements/viewer_toolbar.html b/chrome/browser/resources/pdf/elements/viewer_toolbar.html
index 53483944..7f80032 100644
--- a/chrome/browser/resources/pdf/elements/viewer_toolbar.html
+++ b/chrome/browser/resources/pdf/elements/viewer_toolbar.html
@@ -73,12 +73,12 @@
             ?disabled="${!this.annotationAvailable}"
             title="$i18n{ink2Draw}"></cr-icon-button>
         <span class="vertical-separator"></span>
-        <cr-icon-button id="undo" ?disabled="${!this.canUndoAnnotation_}"
+        <cr-icon-button id="undo" ?disabled="${!this.computeEnableUndo_()}"
             iron-icon="pdf:undo"
             @click="${this.undo}"
             aria-label="$i18n{annotationUndo}"
             title="$i18n{annotationUndo}"></cr-icon-button>
-        <cr-icon-button id="redo" ?disabled="${!this.canRedoAnnotation_}"
+        <cr-icon-button id="redo" ?disabled="${!this.computeEnableRedo_()}"
             iron-icon="pdf:redo"
             @click="${this.redo}"
             aria-label="$i18n{annotationRedo}"
diff --git a/chrome/browser/resources/pdf/elements/viewer_toolbar.ts b/chrome/browser/resources/pdf/elements/viewer_toolbar.ts
index ed23b8b..edd59a02 100644
--- a/chrome/browser/resources/pdf/elements/viewer_toolbar.ts
+++ b/chrome/browser/resources/pdf/elements/viewer_toolbar.ts
@@ -116,6 +116,7 @@
       // </if> enable_ink
 
       // <if expr="enable_pdf_ink2">
+      enableUndoRedo: {type: Boolean},
       hasInk2Edits: {type: Boolean},
       pdfInk2Enabled: {type: Boolean},
       canRedoAnnotation_: {type: Boolean},
@@ -160,6 +161,7 @@
 
   // <if expr="enable_pdf_ink2">
   // Ink2 reactive properties
+  accessor enableUndoRedo: boolean = true;
   accessor hasInk2Edits: boolean = false;
   accessor pdfInk2Enabled: boolean = false;
   protected accessor canRedoAnnotation_: boolean = false;
@@ -501,11 +503,19 @@
     this.canRedoAnnotation_ = false;
   }
 
+  protected computeEnableUndo_(): boolean {
+    return this.canUndoAnnotation_ && this.enableUndoRedo;
+  }
+
+  protected computeEnableRedo_(): boolean {
+    return this.canRedoAnnotation_ && this.enableUndoRedo;
+  }
+
   /**
    * Undo an annotation stroke, if possible.
    */
   undo() {
-    if (!this.canUndoAnnotation_) {
+    if (!this.computeEnableUndo_()) {
       return;
     }
 
@@ -526,7 +536,7 @@
    * Redo an annotation stroke, if possible.
    */
   redo() {
-    if (!this.canRedoAnnotation_) {
+    if (!this.computeEnableRedo_()) {
       return;
     }
 
diff --git a/chrome/browser/resources/pdf/ink2_manager.ts b/chrome/browser/resources/pdf/ink2_manager.ts
index bb78eaa..62c0ba8 100644
--- a/chrome/browser/resources/pdf/ink2_manager.ts
+++ b/chrome/browser/resources/pdf/ink2_manager.ts
@@ -7,7 +7,7 @@
 
 import type {AnnotationBrush, Color, Point, TextAnnotation, TextAttributes, TextBoxRect, TextStyles} from './constants.js';
 import {AnnotationBrushType, TextAlignment, TextStyle} from './constants.js';
-import {PluginController} from './controller.js';
+import {PluginController, PluginControllerEventType} from './controller.js';
 import type {Viewport} from './viewport.js';
 
 export interface ViewportParams {
@@ -30,9 +30,7 @@
 }
 
 export function stylesEqual(style1: TextStyles, style2: TextStyles): boolean {
-  return style1.bold === style2.bold && style1.italic === style2.italic &&
-      style1.underline === style2.underline &&
-      style1.strikethrough === style2.strikethrough;
+  return style1.bold === style2.bold && style1.italic === style2.italic;
 }
 
 export class Ink2Manager extends EventTarget {
@@ -49,8 +47,6 @@
     styles: {
       [TextStyle.BOLD]: false,
       [TextStyle.ITALIC]: false,
-      [TextStyle.UNDERLINE]: false,
-      [TextStyle.STRIKETHROUGH]: false,
     },
   };
   private brushResolver_: PromiseResolver<void>|null = null;
@@ -304,7 +300,7 @@
    * Updates the stored annotation and notifies the plugin of the new or
    * modified annotation.
    */
-  commitTextAnnotation(annotation: TextAnnotation) {
+  commitTextAnnotation(annotation: TextAnnotation, edited: boolean) {
     annotation.textBoxRect = this.screenToPageCoordinates_(
         annotation.pageNumber, annotation.textBoxRect);
 
@@ -324,6 +320,15 @@
     }
     this.pluginController_.finishTextAnnotation(annotation);
     this.existingAnnotationAttributes_ = null;
+
+    if (edited) {
+      // Using PluginController's event target to dispatch this event, even
+      // though it originates here, because PluginController dispatches this
+      // event for normal ink strokes and this way clients only need to listen
+      // on one instance.
+      this.pluginController_.getEventTarget().dispatchEvent(
+          new CustomEvent(PluginControllerEventType.FINISH_INK_STROKE));
+    }
   }
 
   /**
diff --git a/chrome/browser/resources/pdf/pdf_viewer.html b/chrome/browser/resources/pdf/pdf_viewer.html
index 85df746c..25cd9f9 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.html
+++ b/chrome/browser/resources/pdf/pdf_viewer.html
@@ -9,7 +9,8 @@
     .strings="${this.strings}"
     .hasEnteredAnnotationMode="${this.hasEnteredAnnotationMode_}"
 <if expr="enable_pdf_ink2">
-    .hasInk2Edits="${this.hasInk2Edits_}"
+    .enableUndoRedo="${!this.isTextboxActive_()}"
+    .hasInk2Edits="${this.hasInk2AnnotationEdits_()}"
 </if>
     .rotated="${this.isRotated_()}"
     .formFieldFocus="${this.formFieldFocus_}"
@@ -67,7 +68,8 @@
       <if expr="enable_pdf_ink2">
         ${this.isInTextAnnotationMode_() ? html`
           <div class="text-box-container">
-            <ink-text-box></ink-text-box>
+            <ink-text-box @state-changed="${this.onTextBoxStateChanged_}">
+            </ink-text-box>
           </div>` : ''}
       </if>
       <div id="content">
diff --git a/chrome/browser/resources/pdf/pdf_viewer.ts b/chrome/browser/resources/pdf/pdf_viewer.ts
index 9bf4f03..2238295 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.ts
+++ b/chrome/browser/resources/pdf/pdf_viewer.ts
@@ -48,6 +48,9 @@
 // <if expr="enable_ink">
 import type {ContentController} from './controller.js';
 // </if>
+// <if expr="enable_pdf_ink2">
+import {TextBoxState} from './elements/ink_text_box.js';
+// </if>
 import type {ChangePageAndXyDetail, ChangePageDetail, NavigateDetail} from './elements/viewer_bookmark.js';
 import {ChangePageOrigin} from './elements/viewer_bookmark.js';
 import type {ViewerErrorDialogElement} from './elements/viewer_error_dialog.js';
@@ -199,7 +202,7 @@
       hasEnteredAnnotationMode_: {type: Boolean},
 
       // <if expr="enable_pdf_ink2">
-      hasInk2Edits_: {type: Boolean},
+      hasCommittedInk2Edits_: {type: Boolean},
       // </if>
 
       formFieldFocus_: {type: String},
@@ -218,6 +221,11 @@
       showPasswordDialog_: {type: Boolean},
       showPropertiesDialog_: {type: Boolean},
       sidenavCollapsed_: {type: Boolean},
+
+      // <if expr="enable_pdf_ink2">
+      textboxState_: {type: Number},
+      // </if>
+
       title_: {type: String},
       twoUpViewEnabled_: {type: Boolean},
 
@@ -259,7 +267,7 @@
   protected accessor hasEdits_: boolean = false;
   protected accessor hasEnteredAnnotationMode_: boolean = false;
   // <if expr="enable_pdf_ink2">
-  protected accessor hasInk2Edits_: boolean = false;
+  protected accessor hasCommittedInk2Edits_: boolean = false;
   private hasSavedEdits_: boolean = false;
   // </if>
   protected accessor formFieldFocus_: FormFieldFocusType =
@@ -290,6 +298,9 @@
   private sidenavRestoreState_: boolean = false;
   // </if>
 
+  // <if expr="enable_pdf_ink2">
+  protected accessor textboxState_: TextBoxState = TextBoxState.INACTIVE;
+  // </if>
   protected accessor title_: string = '';
   protected toolbarEnabled_: boolean = false;
   protected accessor twoUpViewEnabled_: boolean = false;
@@ -1116,7 +1127,7 @@
   // <if expr="enable_pdf_ink2">
   /** Handles a new ink stroke in annotation mode. */
   private handleFinishInkStroke_() {
-    this.hasInk2Edits_ = true;
+    this.hasCommittedInk2Edits_ = true;
     this.pluginController_.getEventTarget().dispatchEvent(
         new CustomEvent(PluginControllerEventType.FINISH_INK_STROKE));
     this.setShowBeforeUnloadDialog_(true);
@@ -1241,7 +1252,8 @@
     let shouldSaveWithAnnotation = this.hasEnteredAnnotationMode_;
     // <if expr="enable_pdf_ink2">
     if (this.pdfInk2Enabled_) {
-      shouldSaveWithAnnotation = this.hasInk2Edits_;
+      shouldSaveWithAnnotation = this.hasCommittedInk2Edits_ ||
+          this.textboxState_ === TextBoxState.EDITED;
     }
     // </if>
 
@@ -1306,12 +1318,13 @@
 
   // <if expr="enable_pdf_ink2">
   protected onStrokesUpdated_(e: CustomEvent<number>) {
-    this.hasInk2Edits_ = e.detail > 0;
+    this.hasCommittedInk2Edits_ = e.detail > 0;
 
     // If the user already saved, always show the beforeunload dialog if the
     // strokes have updated. If the user hasn't saved, only show the
     // beforeunload dialog if there's edits.
-    this.setShowBeforeUnloadDialog_(this.hasSavedEdits_ || this.hasInk2Edits_);
+    this.setShowBeforeUnloadDialog_(
+        this.hasSavedEdits_ || this.hasCommittedInk2Edits_);
   }
   // </if>
 
@@ -1368,6 +1381,16 @@
     }
     // </if> enable_ink
 
+    // <if expr="enable_pdf_ink2">
+    // If there is an open textbox, call commitTextAnnotation(). This will fire
+    // a message to the plugin with the annotation, if it has been edited.
+    if (this.textboxState_ !== TextBoxState.INACTIVE) {
+      const textbox = this.shadowRoot.querySelector('ink-text-box');
+      assert(textbox);
+      textbox.commitTextAnnotation();
+    }
+    // </if>
+
     const result = await this.currentController.save(requestType);
     if (result === null) {
       // The content controller handled the save internally.
@@ -1380,6 +1403,14 @@
       fileName = fileName + '.pdf';
     }
 
+    // <if expr="enable_pdf_ink2">
+    if (result.bypassSaveFileForTesting) {
+      // Only set by the mock plugin.
+      this.onSaveSuccessful_(requestType);
+      return;
+    }
+    // </if>
+
     // Create blob before callback to avoid race condition.
     const blob = new Blob([result.dataToSave], {type: 'application/pdf'});
     if (this.pdfUseShowSaveFilePicker_) {
@@ -1468,7 +1499,7 @@
         break;
       case SaveRequestType.ORIGINAL:
         // <if expr="enable_pdf_ink2">
-        if (this.hasInk2Edits_) {
+        if (this.hasCommittedInk2Edits_) {
           record(UserAction.SAVE_ORIGINAL);
           break;
         }
@@ -1521,6 +1552,10 @@
   }
 
   // <if expr="enable_pdf_ink2">
+  protected isTextboxActive_(): boolean {
+    return this.textboxState_ !== TextBoxState.INACTIVE;
+  }
+
   protected isInTextAnnotationMode_(): boolean {
     return this.annotationMode_ === AnnotationMode.TEXT;
   }
@@ -1542,6 +1577,18 @@
     return this.inInk2AnnotationMode_() && this.useSidePanelForInk_;
   }
 
+  protected hasInk2AnnotationEdits_(): boolean {
+    return this.textboxState_ === TextBoxState.EDITED ||
+        this.hasCommittedInk2Edits_;
+  }
+
+  protected onTextBoxStateChanged_(e: CustomEvent<TextBoxState>) {
+    this.textboxState_ = e.detail;
+    if (e.detail === TextBoxState.EDITED) {
+      this.setShowBeforeUnloadDialog_(true);
+    }
+  }
+
   /**
    * @returns Whether the PDF viewer has Ink2 enabled and is in annotation mode.
    */
diff --git a/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts b/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts
index 7b7919d..2bb0178 100644
--- a/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts
+++ b/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts
@@ -28,6 +28,7 @@
 export {InkBrushSelectorElement} from './elements/ink_brush_selector.js';
 export {InkColorSelectorElement} from './elements/ink_color_selector.js';
 export {InkSizeSelectorElement, HIGHLIGHTER_SIZES, PEN_SIZES} from './elements/ink_size_selector.js';
+export {InkTextBoxElement, TextBoxState} from './elements/ink_text_box.js';
 export {SelectableIconButtonElement} from './elements/selectable_icon_button.js';
 export {TextAlignmentSelectorElement} from './elements/text_alignment_selector.js';
 export {TextStylesSelectorElement} from './elements/text_styles_selector.js';
diff --git a/chrome/browser/resources/settings/autofill_page/autofill_section.html b/chrome/browser/resources/settings/autofill_page/autofill_section.html
index 055127b..e7bcbf2 100644
--- a/chrome/browser/resources/settings/autofill_page/autofill_section.html
+++ b/chrome/browser/resources/settings/autofill_page/autofill_section.html
@@ -66,26 +66,35 @@
       <div id="addressList" class="vertical-list" role="none">
         <template is="dom-repeat" items="[[addresses]]">
           <div class="list-item" role="listitem">
-            <div class="start">
-              <span id="addressSummary">
-                <span class="ellipses">
-                  [[item.metadata.summaryLabel]]
+            <template is="dom-if" if="[[!isAccountHomeWorkAddress_(item)]]">
+              <div class="start">
+                <span id="addressSummary">
+                  <span class="ellipses">
+                    [[item.metadata.summaryLabel]]
+                  </span>
+                  <span class="ellipses">
+                    [[item.metadata.summarySublabel]]
+                  </span>
                 </span>
-                <span class="ellipses">
-                  [[item.metadata.summarySublabel]]
-                </span>
-              </span>
-              <cr-icon icon="cr20:cloud-off"
+                <cr-icon icon="cr20:cloud-off"
                   hidden$="[[!isCloudOffVisible_(item, accountInfo_)]]"
                   aria-label="$i18n{localAddressIconA11yLabel}"
                   role="img">
-              </cr-icon>
-            </div>
-            <cr-icon-button class="icon-more-vert address-menu"
+                </cr-icon>
+              </div>
+              <cr-icon-button class="icon-more-vert address-menu"
                 on-click="onAddressMenuClick_"
                 title="[[moreActionsTitle_(item.metadata.summaryLabel,
                  item.metadata.summarySublabel)]]">
-            </cr-icon-button>
+              </cr-icon-button>
+            </template>
+            <cr-link-row class="cr-row" id="homeWorkAddress" class="hr"
+              label="[[item.metadata.summaryLabel]][[item.metadata.summarySublabel]]"
+              hidden="[[!isAccountHomeWorkAddress_(item)]]"
+              on-click="onAccountHomeWorkAddressClick_"
+              role-description="$i18n{subpageArrowRoleDescription}"
+              external>
+            </cr-link-row>
           </div>
         </template>
       </div>
diff --git a/chrome/browser/resources/settings/autofill_page/autofill_section.ts b/chrome/browser/resources/settings/autofill_page/autofill_section.ts
index 7e40539..9e699a06 100644
--- a/chrome/browser/resources/settings/autofill_page/autofill_section.ts
+++ b/chrome/browser/resources/settings/autofill_page/autofill_section.ts
@@ -251,6 +251,19 @@
     this.autofillManager_.saveAddress(event.detail);
   }
 
+  private isAccountHomeWorkAddress_(
+      address: chrome.autofillPrivate.AddressEntry) {
+    return address.metadata?.recordType ===
+        chrome.autofillPrivate.AddressRecordType.ACCOUNT_HOME ||
+        address.metadata?.recordType ===
+        chrome.autofillPrivate.AddressRecordType.ACCOUNT_WORK;
+  }
+
+  private onAccountHomeWorkAddressClick_() {
+    OpenWindowProxyImpl.getInstance().openUrl(
+        this.i18n('googleAccountHomeAddressUrl'));
+  }
+
   private isCloudOffVisible_(
       address: chrome.autofillPrivate.AddressEntry,
       accountInfo: chrome.autofillPrivate.AccountInfo|null): boolean {
diff --git a/chrome/browser/safe_browsing/android/download_protection_metrics_data.cc b/chrome/browser/safe_browsing/android/download_protection_metrics_data.cc
index 7d02156f..65d686b 100644
--- a/chrome/browser/safe_browsing/android/download_protection_metrics_data.cc
+++ b/chrome/browser/safe_browsing/android/download_protection_metrics_data.cc
@@ -9,6 +9,28 @@
 
 namespace safe_browsing {
 
+// static
+DownloadProtectionMetricsData::AndroidDownloadProtectionOutcome
+DownloadProtectionMetricsData::ConvertDownloadCheckResultReason(
+    DownloadCheckResultReason reason) {
+  switch (reason) {
+    case DownloadCheckResultReason::REASON_EMPTY_URL_CHAIN:
+      return AndroidDownloadProtectionOutcome::kEmptyUrlChain;
+    case DownloadCheckResultReason::REASON_INVALID_URL:
+      return AndroidDownloadProtectionOutcome::kInvalidUrl;
+    case DownloadCheckResultReason::REASON_UNSUPPORTED_URL_SCHEME:
+      return AndroidDownloadProtectionOutcome::kUnsupportedUrlScheme;
+    case DownloadCheckResultReason::REASON_REMOTE_FILE:
+      return AndroidDownloadProtectionOutcome::kRemoteFile;
+    case DownloadCheckResultReason::REASON_LOCAL_FILE:
+      return AndroidDownloadProtectionOutcome::kLocalFile;
+    case DownloadCheckResultReason::REASON_NOT_BINARY_FILE:
+      return AndroidDownloadProtectionOutcome::kDownloadNotSupportedType;
+    default:
+      NOTREACHED();
+  }
+}
+
 DownloadProtectionMetricsData::DownloadProtectionMetricsData() = default;
 
 DownloadProtectionMetricsData::~DownloadProtectionMetricsData() {
diff --git a/chrome/browser/safe_browsing/android/download_protection_metrics_data.h b/chrome/browser/safe_browsing/android/download_protection_metrics_data.h
index 250e222..3c6b80c1 100644
--- a/chrome/browser/safe_browsing/android/download_protection_metrics_data.h
+++ b/chrome/browser/safe_browsing/android/download_protection_metrics_data.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_SAFE_BROWSING_ANDROID_DOWNLOAD_PROTECTION_METRICS_DATA_H_
 
 #include "base/supports_user_data.h"
+#include "chrome/browser/safe_browsing/download_protection/download_protection_util.h"
 
 namespace download {
 class DownloadItem;
@@ -57,6 +58,10 @@
   };
   // LINT.ThenChange(//tools/metrics/histograms/metadata/sb_client/enums.xml:SBClientDownloadAndroidDownloadProtectionOutcome)
 
+  // Converts corresponding enum values.
+  static AndroidDownloadProtectionOutcome ConvertDownloadCheckResultReason(
+      DownloadCheckResultReason reason);
+
   // The histogram is logged in the dtor if it has not yet been logged.
   ~DownloadProtectionMetricsData() override;
 
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
index 62e36ce1..0a2b169 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/feature_list.h"
+#include "base/files/file_path.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
@@ -54,7 +55,10 @@
 
 namespace {
 
-#if !BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
+// File suffix for APKs.
+const base::FilePath::CharType kApkSuffix[] = FILE_PATH_LITERAL(".apk");
+#else   // BUILDFLAG(IS_ANDROID)
 bool ShouldUploadToDownloadFeedback(DownloadCheckResult result) {
   switch (result) {
     case DownloadCheckResult::DANGEROUS_HOST:
@@ -82,7 +86,7 @@
       return false;
   }
 }
-#endif  // !BUILDFLAG(IS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 
 }  // namespace
 
@@ -147,7 +151,7 @@
 // static
 bool CheckClientDownloadRequest::IsSupportedDownload(
     const download::DownloadItem& item,
-    const base::FilePath& target_path,
+    const base::FilePath& file_name,
     DownloadCheckResultReason* reason) {
   if (item.GetUrlChain().empty()) {
     *reason = REASON_EMPTY_URL_CHAIN;
@@ -169,16 +173,20 @@
     *reason = final_url.has_host() ? REASON_REMOTE_FILE : REASON_LOCAL_FILE;
     return false;
   }
-  // On Android, ignore REASON_NOT_BINARY_FILE, because it is derived from
-  // FileTypePolicies, which are currently only applicable to desktop platforms.
-  // TODO(chlily): Refactor/fix FileTypePolicies and then remove this exception.
-#if !BUILDFLAG(IS_ANDROID)
+  // On Android, do not use FileTypePolicies, which are currently only
+  // applicable to desktop platforms. Instead, hardcode the APK filetype check
+  // for Android here.
   // This check should be last, so we know the earlier checks passed.
-  if (!FileTypePolicies::GetInstance()->IsCheckedBinaryFile(target_path)) {
+  // TODO(chlily): Refactor/fix FileTypePolicies and then remove this
+  // platform-specific hardcoded behavior.
+#if BUILDFLAG(IS_ANDROID)
+  if (!file_name.MatchesExtension(kApkSuffix)) {
+#else
+  if (!FileTypePolicies::GetInstance()->IsCheckedBinaryFile(file_name)) {
+#endif
     *reason = REASON_NOT_BINARY_FILE;
     return false;
   }
-#endif
   return true;
 }
 
@@ -189,7 +197,24 @@
 
 bool CheckClientDownloadRequest::IsSupportedDownload(
     DownloadCheckResultReason* reason) {
-  return IsSupportedDownload(*item_, item_->GetTargetFilePath(), reason);
+  bool is_supported_download =
+      IsSupportedDownload(*item_,
+#if BUILDFLAG(IS_ANDROID)
+                          /*file_name=*/item_->GetFileNameToReportUser(),
+#else
+                          /*file_name=*/item_->GetTargetFilePath(),
+#endif
+                          reason);
+
+#if BUILDFLAG(IS_ANDROID)
+  if (!is_supported_download) {
+    DownloadProtectionMetricsData::SetOutcome(
+        item_, DownloadProtectionMetricsData::ConvertDownloadCheckResultReason(
+                   *reason));
+  }
+#endif
+
+  return is_supported_download;
 }
 
 download::DownloadItem* CheckClientDownloadRequest::item() const {
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request.h b/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
index 46c45fba..bc411fd 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request.h
@@ -50,8 +50,10 @@
   void OnDownloadDestroyed(download::DownloadItem* download) override;
   void OnDownloadUpdated(download::DownloadItem* download) override;
 
+  // Returns whether `item` is eligible for CheckClientDownloadRequest.
+  // Note: Behavior is platform-specific.
   static bool IsSupportedDownload(const download::DownloadItem& item,
-                                  const base::FilePath& target_path,
+                                  const base::FilePath& file_name,
                                   DownloadCheckResultReason* reason);
 
   download::DownloadItem* item() const override;
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_delegate.h b/chrome/browser/safe_browsing/download_protection/download_protection_delegate.h
index 029a1463..be97111 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_delegate.h
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_delegate.h
@@ -42,15 +42,19 @@
   virtual bool ShouldCheckDownloadUrl(download::DownloadItem* item) const = 0;
 
   // Returns whether the download item should be checked by
-  // CheckClientDownload() based on user preferences.
+  // CheckClientDownload() based on user preferences, properties of the file,
+  // and potentially random sampling.
+  // TODO(chlily): Implementations of this method currently rely on the checks
+  // in IsSupportedDownload. This is redundant. Refactor this logic to eliminate
+  // IsSupportedDownload().
   virtual bool ShouldCheckClientDownload(
       download::DownloadItem* item) const = 0;
 
   // Returns whether the download item should be checked by
   // CheckClientDownload() based on whether the file supports the check.
-  // May modify the DownloadItem with a SupportsUserData::Data.
-  // TODO(chlily): Refactor and/or rename this, as it currently contains logic
-  // based on things other than the file itself (i.e. random sampling).
+  // TODO(chlily): Remove this method. The only place where it is called seems
+  // to be vestigial, and does not affect whether CheckClientDownload ultimately
+  // happens.
   virtual bool IsSupportedDownload(download::DownloadItem& item,
                                    const base::FilePath& target_path) const = 0;
 
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_delegate_android.cc b/chrome/browser/safe_browsing/download_protection/download_protection_delegate_android.cc
index 1aa03ec..5a41eb5f 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_delegate_android.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_delegate_android.cc
@@ -43,9 +43,6 @@
 // Content-Type HTTP header field for the request.
 const char kProtobufContentType[] = "application/x-protobuf";
 
-// File suffix for APKs.
-const base::FilePath::CharType kApkSuffix[] = FILE_PATH_LITERAL(".apk");
-
 bool IsDownloadRequestUrlValid(const GURL& url) {
   return url.is_valid() && url.SchemeIs(url::kHttpsScheme) &&
          google_util::IsGoogleAssociatedDomainUrl(url);
@@ -107,23 +104,6 @@
   return base::RandDouble() * 100 < sample_percentage;
 }
 
-Outcome ConvertDownloadCheckResultReason(DownloadCheckResultReason reason) {
-  switch (reason) {
-    case DownloadCheckResultReason::REASON_EMPTY_URL_CHAIN:
-      return Outcome::kEmptyUrlChain;
-    case DownloadCheckResultReason::REASON_INVALID_URL:
-      return Outcome::kInvalidUrl;
-    case DownloadCheckResultReason::REASON_UNSUPPORTED_URL_SCHEME:
-      return Outcome::kUnsupportedUrlScheme;
-    case DownloadCheckResultReason::REASON_REMOTE_FILE:
-      return Outcome::kRemoteFile;
-    case DownloadCheckResultReason::REASON_LOCAL_FILE:
-      return Outcome::kLocalFile;
-    default:
-      NOTREACHED();
-  }
-}
-
 void LogGetReferringAppInfoResult(internal::GetReferringAppInfoResult result) {
   base::UmaHistogramEnumeration(
       "SBClientDownload.Android.GetReferringAppInfo.Result", result);
@@ -149,7 +129,20 @@
     DownloadProtectionMetricsData::SetOutcome(item, Outcome::kMisconfigured);
     return false;
   }
-  return is_enabled;
+  if (!is_enabled) {
+    return false;
+  }
+
+  if (!IsSupportedDownload(*item, item->GetFileNameToReportUser())) {
+    return false;
+  }
+
+  bool should_sample = should_sample_override_.value_or(ShouldSample());
+  if (!should_sample) {
+    DownloadProtectionMetricsData::SetOutcome(item, Outcome::kNotSampled);
+  }
+  should_sample_override_ = std::nullopt;
+  return should_sample;
 }
 
 bool DownloadProtectionDelegateAndroid::IsSupportedDownload(
@@ -158,30 +151,19 @@
   // On Android, the target path is likely a content-URI. Therefore, use the
   // display name instead. This assumes the DownloadItem's display name has
   // already been populated by InProgressDownloadManager.
+  // TODO(chlily): The display name may not be populated properly at the point
+  // when this is called.
   base::FilePath file_name = item.GetFileNameToReportUser();
 
   DownloadCheckResultReason reason = REASON_MAX;
   if (!CheckClientDownloadRequest::IsSupportedDownload(item, file_name,
                                                        &reason)) {
     DownloadProtectionMetricsData::SetOutcome(
-        &item, ConvertDownloadCheckResultReason(reason));
+        &item, DownloadProtectionMetricsData::ConvertDownloadCheckResultReason(
+                   reason));
     return false;
   }
-
-  // For Android download protection, only check APK files (as defined by having
-  // a filename ending in a ".apk" extension).
-  if (!file_name.MatchesExtension(kApkSuffix)) {
-    DownloadProtectionMetricsData::SetOutcome(
-        &item, Outcome::kDownloadNotSupportedType);
-    return false;
-  }
-
-  bool should_sample = should_sample_override_.value_or(ShouldSample());
-  if (!should_sample) {
-    DownloadProtectionMetricsData::SetOutcome(&item, Outcome::kNotSampled);
-  }
-  should_sample_override_ = std::nullopt;
-  return should_sample;
+  return true;
 }
 
 void DownloadProtectionDelegateAndroid::PreSerializeRequest(
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_delegate_desktop.cc b/chrome/browser/safe_browsing/download_protection/download_protection_delegate_desktop.cc
index f5071df..4fe1e8b 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_delegate_desktop.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_delegate_desktop.cc
@@ -66,7 +66,8 @@
 
 bool DownloadProtectionDelegateDesktop::ShouldCheckClientDownload(
     download::DownloadItem* item) const {
-  return IsSafeBrowsingEnabledForDownloadProfile(item);
+  return IsSafeBrowsingEnabledForDownloadProfile(item) &&
+         IsSupportedDownload(*item, item->GetTargetFilePath());
 }
 
 bool DownloadProtectionDelegateDesktop::IsSupportedDownload(
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
index de3b474..7decb7e 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
@@ -181,6 +181,9 @@
 const base::FilePath::CharType kTempContentUri[] =
     FILE_PATH_LITERAL("content://media/temp/foo.bar");
 
+// Default APK filename for Android tests.
+const base::FilePath::CharType kApkFilename[] = FILE_PATH_LITERAL("a.apk");
+
 const char kAndroidDownloadProtectionOutcomeHistogram[] =
     "SBClientDownload.Android.DownloadProtectionOutcome";
 #endif
@@ -644,29 +647,42 @@
                                      request_deep_scan);
   }
 
+  // Common setup code for MockDownloadItem.
+  // `tmp_path_literal` and `final_path_literal` are ignored on Android.
   void PrepareBasicDownloadItem(
       NiceMockDownloadItem* item,
       const std::vector<std::string> url_chain_items,
       const std::string& referrer_url,
       const base::FilePath::StringType& tmp_path_literal,
-      const base::FilePath::StringType& final_path_literal) {
+      const base::FilePath::StringType& final_path_literal,
+      std::optional<base::FilePath> display_name = std::nullopt) {
+#if BUILDFLAG(IS_ANDROID)
+    // For realism on Android, the final path and temp path are typically
+    // content-URIs, and only the display name should be used.
+    base::FilePath tmp_path = base::FilePath(kTempContentUri);
+    base::FilePath final_path = base::FilePath(kContentUri);
+
+    // Supply a default display name for Android tests that is eligible for
+    // download protection.
+    display_name = display_name.value_or(base::FilePath(kApkFilename));
+#else
     base::FilePath tmp_path = temp_dir_.GetPath().Append(tmp_path_literal);
     base::FilePath final_path = temp_dir_.GetPath().Append(final_path_literal);
+#endif
     PrepareBasicDownloadItemWithFullPaths(item, url_chain_items, referrer_url,
-                                          tmp_path, final_path);
+                                          tmp_path, final_path, display_name);
   }
 
 #if BUILDFLAG(IS_ANDROID)
-  // This function is for realism on Android, where the final path and temp path
-  // are typically content-URIs, and only the display name should be used.
   void PrepareBasicDownloadItemWithContentUri(
       NiceMockDownloadItem* item,
       const std::vector<std::string> url_chain_items,
       const std::string& referrer_url,
       const base::FilePath& display_name) {
-    PrepareBasicDownloadItemWithFullPaths(
-        item, url_chain_items, referrer_url, base::FilePath(kTempContentUri),
-        base::FilePath(kContentUri), display_name);
+    PrepareBasicDownloadItem(item, url_chain_items, referrer_url,
+                             /*ignored*/ base::FilePath::StringType(),
+                             /*ignored*/ base::FilePath::StringType(),
+                             display_name);
   }
 #endif
 
@@ -5689,7 +5705,7 @@
       PrepareBasicDownloadItemWithContentUri(
           &item, /*url_chain_items=*/{"http://www.evil.com/bla.apk"},
           /*referrer_url=*/"",
-          /*display_name=*/base::FilePath(FILE_PATH_LITERAL("a.apk")));
+          /*display_name=*/base::FilePath(kApkFilename));
 
       std::move(verify_item).Run(&item);
 
@@ -5774,7 +5790,7 @@
       &item, /*url_chain_items=*/
       {"https://www.example.test/", "http://www.evil.com/bla.apk"},
       /*referrer_url=*/"https://www.google.com",
-      /*display_name=*/base::FilePath(FILE_PATH_LITERAL("a.apk")));
+      /*display_name=*/base::FilePath(kApkFilename));
 
   content::DownloadItemUtils::AttachInfoForTesting(&item, profile(), nullptr);
 
@@ -5813,6 +5829,55 @@
   TestCheckClientDownload(expected_url);
 }
 
+TEST_P(AndroidDownloadProtectionTest,
+       NoCheckClientDownloadForNonSupportedType) {
+  // Response to any requests will be DANGEROUS.
+  PrepareResponse(ClientDownloadResponse::DANGEROUS, net::HTTP_OK, net::OK);
+  // Override the random sampling.
+  OverrideNextShouldSample(true);
+
+  ResetHistogramTester();
+  {
+    NiceMockDownloadItem item;
+    content::DownloadItemUtils::AttachInfoForTesting(&item, profile(), nullptr);
+    PrepareBasicDownloadItemWithContentUri(
+        &item, /*url_chain_items=*/{"http://www.evil.com/bla.apk"},
+        /*referrer_url=*/"",
+        /*display_name=*/
+        base::FilePath(FILE_PATH_LITERAL("not_supported_filetype.dex")));
+
+    ExpectNoCheckClientDownload(&item);
+  }
+  // The histogram is logged when the item goes out of scope.
+  ExpectHistogramUniqueSample(ShouldAndroidDownloadProtectionBeActive()
+                                  ? Outcome::kDownloadNotSupportedType
+                                  : Outcome::kDownloadProtectionDisabled);
+}
+
+TEST_P(AndroidDownloadProtectionTest, NoCheckClientDownloadNotSampled) {
+  // Response to any requests will be DANGEROUS.
+  PrepareResponse(ClientDownloadResponse::DANGEROUS, net::HTTP_OK, net::OK);
+  // Override the random sampling to guarantee we won't sample.
+  OverrideNextShouldSample(false);
+
+  ResetHistogramTester();
+  {
+    NiceMockDownloadItem item;
+    content::DownloadItemUtils::AttachInfoForTesting(&item, profile(), nullptr);
+    PrepareBasicDownloadItemWithContentUri(
+        &item, /*url_chain_items=*/{"http://www.evil.com/bla.apk"},
+        /*referrer_url=*/"",
+        /*display_name=*/
+        base::FilePath(kApkFilename));
+
+    ExpectNoCheckClientDownload(&item);
+  }
+  // The histogram is logged when the item goes out of scope.
+  ExpectHistogramUniqueSample(ShouldAndroidDownloadProtectionBeActive()
+                                  ? Outcome::kNotSampled
+                                  : Outcome::kDownloadProtectionDisabled);
+}
+
 // Tests the various false outcomes of IsSupportedDownload().
 TEST_P(AndroidDownloadProtectionTest, IsSupportedDownloadFalse) {
   ResetHistogramTester();
@@ -5823,7 +5888,7 @@
     PrepareBasicDownloadItemWithContentUri(
         &item_empty_url_chain, /*url_chain_items=*/{},
         /*referrer_url=*/"",
-        /*display_name=*/base::FilePath(FILE_PATH_LITERAL("a.apk")));
+        /*display_name=*/base::FilePath(kApkFilename));
     std::vector<GURL> empty_url_chain;
     EXPECT_CALL(item_empty_url_chain, GetUrlChain())
         .WillRepeatedly(ReturnRef(empty_url_chain));
@@ -5841,7 +5906,7 @@
     PrepareBasicDownloadItemWithContentUri(
         &item_invalid_url, /*url_chain_items=*/{"bogus_url"},
         /*referrer_url=*/"",
-        /*display_name=*/base::FilePath(FILE_PATH_LITERAL("a.apk")));
+        /*display_name=*/base::FilePath(kApkFilename));
 
     EXPECT_FALSE(
         download_service_->IsSupportedDownload(item_invalid_url, final_path_));
@@ -5857,7 +5922,7 @@
         &item_unsupported_url_scheme,
         /*url_chain_items=*/{"unsupported://blah"},
         /*referrer_url=*/"",
-        /*display_name=*/base::FilePath(FILE_PATH_LITERAL("a.apk")));
+        /*display_name=*/base::FilePath(kApkFilename));
 
     EXPECT_FALSE(download_service_->IsSupportedDownload(
         item_unsupported_url_scheme, final_path_));
@@ -5873,7 +5938,7 @@
         &item_remote_file_url,
         /*url_chain_items=*/{"file://drive.test/download"},
         /*referrer_url=*/"",
-        /*display_name=*/base::FilePath(FILE_PATH_LITERAL("a.apk")));
+        /*display_name=*/base::FilePath(kApkFilename));
 
     EXPECT_FALSE(download_service_->IsSupportedDownload(item_remote_file_url,
                                                         final_path_));
@@ -5888,7 +5953,7 @@
     PrepareBasicDownloadItemWithContentUri(
         &item_local_file_url, /*url_chain_items=*/{"file:///download"},
         /*referrer_url=*/"",
-        /*display_name=*/base::FilePath(FILE_PATH_LITERAL("a.apk")));
+        /*display_name=*/base::FilePath(kApkFilename));
 
     EXPECT_FALSE(download_service_->IsSupportedDownload(item_local_file_url,
                                                         final_path_));
@@ -5910,24 +5975,6 @@
         item_display_name_not_apk, final_path_));
   }
   ExpectHistogramUniqueSample(Outcome::kDownloadNotSupportedType);
-
-  ResetHistogramTester();
-  {
-    // An otherwise eligible item that is just not sampled.
-    NiceMockDownloadItem item_eligible;
-    OverrideNextShouldSample(false);
-    content::DownloadItemUtils::AttachInfoForTesting(&item_eligible, profile(),
-                                                     nullptr);
-    PrepareBasicDownloadItemWithContentUri(
-        &item_eligible,
-        /*url_chain_items=*/{"https://evil.com/bla.apk"},
-        /*referrer_url=*/"",
-        /*display_name=*/base::FilePath(FILE_PATH_LITERAL("a.apk")));
-
-    EXPECT_FALSE(
-        download_service_->IsSupportedDownload(item_eligible, final_path_));
-  }
-  ExpectHistogramUniqueSample(Outcome::kNotSampled);
 }
 
 // Tests that CheckClientDownload is called even if the download is not a
diff --git a/chrome/browser/safety_hub/android/BUILD.gn b/chrome/browser/safety_hub/android/BUILD.gn
index 709fa7d..2f48d32 100644
--- a/chrome/browser/safety_hub/android/BUILD.gn
+++ b/chrome/browser/safety_hub/android/BUILD.gn
@@ -31,7 +31,6 @@
     "java/src/org/chromium/chrome/browser/safety_hub/PermissionsData.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsDataSource.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsModuleMediator.java",
-    "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsNoPasswordsModuleHelper.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsSignedOutModuleHelper.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsUnavailableAllPasswordsModuleHelper.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsUnavailableCompromisedPasswordsModuleHelper.java",
@@ -49,7 +48,6 @@
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsCheckingModuleHelper.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsDataSource.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsModuleMediator.java",
-    "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsNoPasswordsModuleHelper.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsUnavailableAllPasswordsModuleHelper.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubMagicStackBuilder.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubMagicStackCoordinator.java",
@@ -66,6 +64,7 @@
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubModuleProperties.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubModuleViewBinder.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubNoCompromisedPasswordsModuleHelper.java",
+    "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubNoSavedPasswordsModuleHelper.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubNotificationsFragment.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubNotificationsModuleMediator.java",
     "java/src/org/chromium/chrome/browser/safety_hub/SafetyHubNotificationsPreference.java",
diff --git a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsDataSource.java b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsDataSource.java
index 96e4b558..7318943 100644
--- a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsDataSource.java
+++ b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsDataSource.java
@@ -131,6 +131,9 @@
             assert mCompromisedPasswordCount == INVALID_BREACHED_CREDENTIALS_COUNT;
             return ModuleType.SIGNED_OUT;
         }
+        if (getTotalPasswordCount() == 0) {
+            return ModuleType.NO_SAVED_PASSWORDS;
+        }
         if (mCompromisedPasswordCount == INVALID_BREACHED_CREDENTIALS_COUNT) {
             if (isWeakAndReusedFeatureEnabled
                     && mWeakPasswordCount == 0
@@ -140,9 +143,6 @@
 
             return ModuleType.UNAVAILABLE_PASSWORDS;
         }
-        if (getTotalPasswordCount() == 0) {
-            return ModuleType.NO_SAVED_PASSWORDS;
-        }
         if (mCompromisedPasswordCount > 0) {
             return ModuleType.HAS_COMPROMISED_PASSWORDS;
         }
diff --git a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsModuleMediator.java b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsModuleMediator.java
index 09d6f8a..8e5bacf 100644
--- a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsModuleMediator.java
+++ b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsModuleMediator.java
@@ -76,8 +76,11 @@
                 return new SafetyHubAccountPasswordsUnavailableAllPasswordsModuleHelper(
                         context, mModuleDelegate);
             case ModuleType.NO_SAVED_PASSWORDS:
-                return new SafetyHubAccountPasswordsNoPasswordsModuleHelper(
-                        context, mModuleDelegate);
+                return new SafetyHubNoSavedPasswordsModuleHelper(
+                        context,
+                        mModuleDelegate,
+                        /* noAccountPasswords= */ true,
+                        /* noLocalPasswords= */ false);
             case ModuleType.HAS_COMPROMISED_PASSWORDS:
                 return new SafetyHubCompromisedPasswordsModuleHelper(
                         context,
diff --git a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsNoPasswordsModuleHelper.java b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsNoPasswordsModuleHelper.java
deleted file mode 100644
index ec4485c..0000000
--- a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubAccountPasswordsNoPasswordsModuleHelper.java
+++ /dev/null
@@ -1,66 +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.
-
-package org.chromium.chrome.browser.safety_hub;
-
-import static org.chromium.chrome.browser.safety_hub.SafetyHubMetricUtils.recordDashboardInteractions;
-
-import android.content.Context;
-import android.view.View;
-
-import org.chromium.build.annotations.NullMarked;
-import org.chromium.build.annotations.Nullable;
-import org.chromium.chrome.browser.safety_hub.SafetyHubMetricUtils.DashboardInteractions;
-import org.chromium.chrome.browser.safety_hub.SafetyHubModuleMediator.ModuleState;
-
-/** Helper for the {@link SafetyHubAccountPasswordsModule} for the has no passwords state. */
-@NullMarked
-public class SafetyHubAccountPasswordsNoPasswordsModuleHelper implements SafetyHubModuleHelper {
-    private final Context mContext;
-    private final SafetyHubModuleDelegate mModuleDelegate;
-
-    SafetyHubAccountPasswordsNoPasswordsModuleHelper(
-            Context context, SafetyHubModuleDelegate moduleDelegate) {
-        mContext = context;
-        mModuleDelegate = moduleDelegate;
-    }
-
-    @Override
-    public String getTitle() {
-        return mContext.getString(R.string.safety_hub_no_account_passwords_title);
-    }
-
-    @Override
-    public String getSummary() {
-        return mContext.getString(R.string.safety_hub_no_passwords_summary);
-    }
-
-    @Override
-    public @Nullable String getPrimaryButtonText() {
-        return null;
-    }
-
-    @Override
-    public View.@Nullable OnClickListener getPrimaryButtonListener() {
-        return null;
-    }
-
-    @Override
-    public String getSecondaryButtonText() {
-        return mContext.getString(R.string.safety_hub_passwords_navigation_button);
-    }
-
-    @Override
-    public View.OnClickListener getSecondaryButtonListener() {
-        return v -> {
-            mModuleDelegate.showPasswordCheckUi(mContext);
-            recordDashboardInteractions(DashboardInteractions.OPEN_PASSWORD_MANAGER);
-        };
-    }
-
-    @Override
-    public @ModuleState int getModuleState() {
-        return ModuleState.INFO;
-    }
-}
diff --git a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsModuleMediator.java b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsModuleMediator.java
index 1143217..c25ec39 100644
--- a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsModuleMediator.java
+++ b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsModuleMediator.java
@@ -176,7 +176,11 @@
                 return new SafetyHubLocalPasswordsUnavailableAllPasswordsModuleHelper(
                         context, mModuleDelegate);
             case ModuleType.NO_SAVED_PASSWORDS:
-                return new SafetyHubLocalPasswordsNoPasswordsModuleHelper(context, mModuleDelegate);
+                return new SafetyHubNoSavedPasswordsModuleHelper(
+                        context,
+                        mModuleDelegate,
+                        /* noAccountPasswords= */ false,
+                        /* noLocalPasswords= */ true);
             case ModuleType.HAS_COMPROMISED_PASSWORDS:
                 return new SafetyHubCompromisedPasswordsModuleHelper(
                         context,
diff --git a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsNoPasswordsModuleHelper.java b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsNoPasswordsModuleHelper.java
deleted file mode 100644
index 8c7cb0f..0000000
--- a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubLocalPasswordsNoPasswordsModuleHelper.java
+++ /dev/null
@@ -1,66 +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.
-
-package org.chromium.chrome.browser.safety_hub;
-
-import static org.chromium.chrome.browser.safety_hub.SafetyHubMetricUtils.recordDashboardInteractions;
-
-import android.content.Context;
-import android.view.View;
-
-import org.chromium.build.annotations.NullMarked;
-import org.chromium.build.annotations.Nullable;
-import org.chromium.chrome.browser.safety_hub.SafetyHubMetricUtils.DashboardInteractions;
-import org.chromium.chrome.browser.safety_hub.SafetyHubModuleMediator.ModuleState;
-
-/** Helper for the {@link SafetyHubLocalPasswordsModule} for the no passwords state. */
-@NullMarked
-public class SafetyHubLocalPasswordsNoPasswordsModuleHelper implements SafetyHubModuleHelper {
-    private final Context mContext;
-    private final SafetyHubModuleDelegate mModuleDelegate;
-
-    SafetyHubLocalPasswordsNoPasswordsModuleHelper(
-            Context context, SafetyHubModuleDelegate moduleDelegate) {
-        mContext = context;
-        mModuleDelegate = moduleDelegate;
-    }
-
-    @Override
-    public String getTitle() {
-        return mContext.getString(R.string.safety_hub_no_local_passwords_title);
-    }
-
-    @Override
-    public String getSummary() {
-        return mContext.getString(R.string.safety_hub_no_passwords_summary);
-    }
-
-    @Override
-    public @Nullable String getPrimaryButtonText() {
-        return null;
-    }
-
-    @Override
-    public View.@Nullable OnClickListener getPrimaryButtonListener() {
-        return null;
-    }
-
-    @Override
-    public String getSecondaryButtonText() {
-        return mContext.getString(R.string.safety_hub_passwords_navigation_button);
-    }
-
-    @Override
-    public View.OnClickListener getSecondaryButtonListener() {
-        return v -> {
-            mModuleDelegate.showLocalPasswordCheckUi(mContext);
-            recordDashboardInteractions(DashboardInteractions.OPEN_PASSWORD_MANAGER);
-        };
-    }
-
-    @Override
-    public @ModuleState int getModuleState() {
-        return ModuleState.INFO;
-    }
-}
diff --git a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubNoSavedPasswordsModuleHelper.java b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubNoSavedPasswordsModuleHelper.java
new file mode 100644
index 0000000..04ac6ec
--- /dev/null
+++ b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubNoSavedPasswordsModuleHelper.java
@@ -0,0 +1,99 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.safety_hub;
+
+import static org.chromium.chrome.browser.safety_hub.SafetyHubMetricUtils.recordDashboardInteractions;
+
+import android.content.Context;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.chrome.browser.safety_hub.SafetyHubMetricUtils.DashboardInteractions;
+import org.chromium.chrome.browser.safety_hub.SafetyHubModuleMediator.ModuleState;
+
+/** Helper for the {@link SafetyHubAccountPasswordsModule} for the has no passwords state. */
+@NullMarked
+public class SafetyHubNoSavedPasswordsModuleHelper implements SafetyHubModuleHelper {
+    private final Context mContext;
+    private final SafetyHubModuleDelegate mModuleDelegate;
+    private final boolean mNoAccountPasswords;
+    private final boolean mNoLocalPasswords;
+
+    SafetyHubNoSavedPasswordsModuleHelper(
+            Context context,
+            SafetyHubModuleDelegate moduleDelegate,
+            boolean noAccountPasswords,
+            boolean noLocalPasswords) {
+        mContext = context;
+        mModuleDelegate = moduleDelegate;
+        mNoAccountPasswords = noAccountPasswords;
+        mNoLocalPasswords = noLocalPasswords;
+
+        assert noAccountPasswords || noLocalPasswords
+                : "Some storage should be empty in NoSavedPasswordsModuleHelper";
+    }
+
+    @Override
+    public String getTitle() {
+        if (mNoAccountPasswords && mNoLocalPasswords) {
+            return mContext.getString(R.string.safety_hub_no_passwords_title);
+        }
+        if (mNoAccountPasswords) {
+            return mContext.getString(R.string.safety_hub_no_account_passwords_title);
+        }
+        return mContext.getString(R.string.safety_hub_no_local_passwords_title);
+    }
+
+    @Override
+    public String getSummary() {
+        return mContext.getString(R.string.safety_hub_no_passwords_summary);
+    }
+
+    @Override
+    public @Nullable String getPrimaryButtonText() {
+        return null;
+    }
+
+    @Override
+    public @Nullable View.OnClickListener getPrimaryButtonListener() {
+        return null;
+    }
+
+    @Override
+    public String getSecondaryButtonText() {
+        if (mNoAccountPasswords && mNoLocalPasswords) {
+            return mContext.getString(R.string.safety_hub_password_subpage_navigation_button);
+        }
+        return mContext.getString(R.string.safety_hub_passwords_navigation_button);
+    }
+
+    @Override
+    public View.OnClickListener getSecondaryButtonListener() {
+        if (mNoAccountPasswords && mNoLocalPasswords) {
+            return v -> {
+                // TODO(crbug.com/407931779): Change to open the SH passwords page.
+                mModuleDelegate.showLocalPasswordCheckUi(mContext);
+            };
+        }
+        if (mNoAccountPasswords) {
+            return v -> {
+                mModuleDelegate.showPasswordCheckUi(mContext);
+                recordDashboardInteractions(DashboardInteractions.OPEN_PASSWORD_MANAGER);
+            };
+        }
+
+        return v -> {
+            mModuleDelegate.showLocalPasswordCheckUi(mContext);
+            recordDashboardInteractions(DashboardInteractions.OPEN_PASSWORD_MANAGER);
+        };
+    }
+
+    @Override
+    public @ModuleState int getModuleState() {
+        return ModuleState.INFO;
+    }
+}
diff --git a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediator.java b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediator.java
index 90db06e6..2e2dbaec 100644
--- a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediator.java
+++ b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediator.java
@@ -133,7 +133,7 @@
 
         if (accountModuleType
                         == SafetyHubAccountPasswordsDataSource.ModuleType.NO_COMPROMISED_PASSWORDS
-                && localModuleType
+                || localModuleType
                         == SafetyHubLocalPasswordsDataSource.ModuleType.NO_COMPROMISED_PASSWORDS) {
             return new SafetyHubNoCompromisedPasswordsModuleHelper(
                     context,
@@ -142,9 +142,19 @@
                     /* unifiedModule= */ true);
         }
 
-        // TODO(crbug.com/407930886): Add no password state for account and local passwords.
-        return new SafetyHubAccountPasswordsUnavailableAllPasswordsModuleHelper(
-                context, mModuleDelegate);
+        // By reaching this point, all other states have been exhausted.
+        assert (accountModuleType
+                                == SafetyHubAccountPasswordsDataSource.ModuleType.NO_SAVED_PASSWORDS
+                        || accountModuleType
+                                == SafetyHubAccountPasswordsDataSource.ModuleType.SIGNED_OUT)
+                && localModuleType
+                        == SafetyHubLocalPasswordsDataSource.ModuleType.NO_SAVED_PASSWORDS;
+
+        return new SafetyHubNoSavedPasswordsModuleHelper(
+                context,
+                mModuleDelegate,
+                /* noAccountPasswords= */ true,
+                /* noLocalPasswords= */ true);
     }
 
     private void updateModule(
diff --git a/chrome/browser/safety_hub/android/javatests/src/org/chromium/chrome/browser/safety_hub/SafetyHubTest.java b/chrome/browser/safety_hub/android/javatests/src/org/chromium/chrome/browser/safety_hub/SafetyHubTest.java
index b11bbf0..42f764c9 100644
--- a/chrome/browser/safety_hub/android/javatests/src/org/chromium/chrome/browser/safety_hub/SafetyHubTest.java
+++ b/chrome/browser/safety_hub/android/javatests/src/org/chromium/chrome/browser/safety_hub/SafetyHubTest.java
@@ -1648,6 +1648,79 @@
         ChromeFeatureList.SAFETY_HUB_LOCAL_PASSWORDS_MODULE,
         ChromeFeatureList.SAFETY_HUB_UNIFIED_PASSWORDS_MODULE
     })
+    public void testUnifiedPasswordsModule_NoAccountCompromisedPasswords_NoLocalPasswords() {
+        clearAllPasswords();
+        setAccountCompromisedPasswordsCount(0);
+        setAccountReusedPasswordsCount(0);
+        setAccountWeakPasswordsCount(0);
+        addCredentialToAccountStore();
+
+        mSafetyHubFragmentTestRule.startSettingsActivity();
+        SafetyHubFragment safetyHubFragment = mSafetyHubFragmentTestRule.getFragment();
+
+        // Verify that unified passwords module which is in the safe state is collapsed by default.
+        String noCompromisedPasswordsTitle =
+                safetyHubFragment.getString(R.string.safety_hub_no_compromised_passwords_title);
+        scrollToExpandedPreference(noCompromisedPasswordsTitle);
+        verifyButtonsNextToTextVisibility(noCompromisedPasswordsTitle, false);
+
+        // Verify the information module is expanded.
+        String safeBrowsingTitle =
+                safetyHubFragment.getString(R.string.prefs_safe_browsing_no_protection_summary);
+        scrollToPreference(withText(safeBrowsingTitle));
+        verifyButtonsNextToTextVisibility(safeBrowsingTitle, true);
+
+        clearAccountCompromisedPasswordsCount();
+        setLocalPasswordCheckTimestamp(0);
+    }
+
+    @Test
+    @MediumTest
+    @Policies.Add({@Policies.Item(key = "SafeBrowsingEnabled", string = "false")})
+    @Restriction(GmsCoreVersionRestriction.RESTRICTION_TYPE_VERSION_GE_24W15)
+    @Features.EnableFeatures({
+        ChromeFeatureList.SAFETY_HUB_WEAK_AND_REUSED_PASSWORDS,
+        ChromeFeatureList.SAFETY_HUB_LOCAL_PASSWORDS_MODULE,
+        ChromeFeatureList.SAFETY_HUB_UNIFIED_PASSWORDS_MODULE
+    })
+    public void testUnifiedPasswordsModule_NoLocalCompromisedPasswords_NoAccountPasswords() {
+        signIn();
+        clearAllPasswords();
+        setLocalCompromisedPasswordsCount(0);
+        setLocalReusedPasswordsCount(0);
+        setLocalWeakPasswordsCount(0);
+        setLocalPasswordCheckTimestamp(TimeUtils.currentTimeMillis());
+        addCredentialToProfileStore();
+
+        mSafetyHubFragmentTestRule.startSettingsActivity();
+        SafetyHubFragment safetyHubFragment = mSafetyHubFragmentTestRule.getFragment();
+
+        // Verify that unified passwords module which is in the safe state is collapsed by default.
+        String noCompromisedPasswordsTitle =
+                safetyHubFragment.getString(R.string.safety_hub_no_compromised_passwords_title);
+        scrollToExpandedPreference(noCompromisedPasswordsTitle);
+        verifyButtonsNextToTextVisibility(noCompromisedPasswordsTitle, false);
+
+        // Verify the information module is expanded.
+        String safeBrowsingTitle =
+                safetyHubFragment.getString(R.string.prefs_safe_browsing_no_protection_summary);
+        scrollToPreference(withText(safeBrowsingTitle));
+        verifyButtonsNextToTextVisibility(safeBrowsingTitle, true);
+
+        clearLocalCompromisedPasswordsCount();
+        clearAccountCompromisedPasswordsCount();
+        setLocalPasswordCheckTimestamp(0);
+    }
+
+    @Test
+    @MediumTest
+    @Policies.Add({@Policies.Item(key = "SafeBrowsingEnabled", string = "false")})
+    @Restriction(GmsCoreVersionRestriction.RESTRICTION_TYPE_VERSION_GE_24W15)
+    @Features.EnableFeatures({
+        ChromeFeatureList.SAFETY_HUB_WEAK_AND_REUSED_PASSWORDS,
+        ChromeFeatureList.SAFETY_HUB_LOCAL_PASSWORDS_MODULE,
+        ChromeFeatureList.SAFETY_HUB_UNIFIED_PASSWORDS_MODULE
+    })
     public void testUnifiedPasswordsModule_AccountAndLocalPasswordsUnavailable() {
         clearLocalCompromisedPasswordsCount();
         clearAccountCompromisedPasswordsCount();
@@ -1676,6 +1749,36 @@
 
     @Test
     @MediumTest
+    @Policies.Add({@Policies.Item(key = "SafeBrowsingEnabled", string = "false")})
+    @Restriction(GmsCoreVersionRestriction.RESTRICTION_TYPE_VERSION_GE_24W15)
+    @Features.EnableFeatures({
+        ChromeFeatureList.SAFETY_HUB_WEAK_AND_REUSED_PASSWORDS,
+        ChromeFeatureList.SAFETY_HUB_LOCAL_PASSWORDS_MODULE,
+        ChromeFeatureList.SAFETY_HUB_UNIFIED_PASSWORDS_MODULE
+    })
+    public void testUnifiedPasswordsModule_NoAccountAndLocalPasswords() {
+        signIn();
+        clearAllPasswords();
+
+        mSafetyHubFragmentTestRule.startSettingsActivity();
+        SafetyHubFragment safetyHubFragment = mSafetyHubFragmentTestRule.getFragment();
+
+        // Verify that unified passwords module which is in the info state is expanded by
+        // default.
+        String noAccountAndLocalPasswords =
+                safetyHubFragment.getString(R.string.safety_hub_no_passwords_title);
+        scrollToExpandedPreference(noAccountAndLocalPasswords);
+        verifyButtonsNextToTextVisibility(noAccountAndLocalPasswords, true);
+
+        // Verify the information module is expanded.
+        String safeBrowsingTitle =
+                safetyHubFragment.getString(R.string.prefs_safe_browsing_no_protection_summary);
+        scrollToPreference(withText(safeBrowsingTitle));
+        verifyButtonsNextToTextVisibility(safeBrowsingTitle, true);
+    }
+
+    @Test
+    @MediumTest
     @Feature({"SafetyHubPermissions"})
     public void testPermissionsModule_ClearList() {
         mUnusedPermissionsBridge.setPermissionsDataForReview(
@@ -2252,10 +2355,14 @@
                 });
     }
 
-    private void addCredentialToAccountStore() {
-        // Set up an account with at least one password in the account store.
+    private void signIn() {
         mSigninTestRule.addAccountThenSignin(TestAccounts.ACCOUNT1);
         PasswordManagerTestHelper.setAccountForPasswordStore(SigninTestRule.TEST_ACCOUNT_EMAIL);
+    }
+
+    private void addCredentialToAccountStore() {
+        // Set up an account with at least one password in the account store.
+        signIn();
 
         PasswordStoreBridge passwordStoreBridge =
                 ThreadUtils.runOnUiThreadBlocking(() -> new PasswordStoreBridge(mProfile));
@@ -2292,6 +2399,22 @@
                 });
     }
 
+    private void clearAllPasswords() {
+        PasswordStoreBridge passwordStoreBridge =
+                ThreadUtils.runOnUiThreadBlocking(() -> new PasswordStoreBridge(mProfile));
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    passwordStoreBridge.clearAllPasswords();
+                });
+        CriteriaHelper.pollUiThread(
+                () -> {
+                    Criteria.checkThat(
+                            "The account store should've had any passwords",
+                            passwordStoreBridge.getPasswordStoreCredentialsCountForAllStores(),
+                            is(0));
+                });
+    }
+
     private void setSafeBrowsingState(@SafeBrowsingState int state) {
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediatorTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediatorTest.java
index fbb3a3a..2913080 100644
--- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediatorTest.java
+++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/SafetyHubPasswordsModuleMediatorTest.java
@@ -563,6 +563,52 @@
     }
 
     @Test
+    public void noAccountOrLocalPasswordsExist() {
+        mockAccountPasswordState(SafetyHubAccountPasswordsDataSource.ModuleType.NO_SAVED_PASSWORDS);
+        mockLocalPasswordState(SafetyHubLocalPasswordsDataSource.ModuleType.NO_SAVED_PASSWORDS);
+        mModuleMediator.accountPasswordsStateChanged(
+                SafetyHubAccountPasswordsDataSource.ModuleType.NO_SAVED_PASSWORDS);
+        mModuleMediator.localPasswordsStateChanged(
+                SafetyHubLocalPasswordsDataSource.ModuleType.NO_SAVED_PASSWORDS);
+
+        verify(mMediatorDelegateMock, times(1)).onUpdateNeeded();
+
+        String expectedTitle = mActivity.getString(R.string.safety_hub_no_passwords_title);
+        String expectedSummary = mActivity.getString(R.string.safety_hub_no_passwords_summary);
+        String expectedSecondaryButtonText =
+                mActivity.getString(R.string.safety_hub_password_subpage_navigation_button);
+
+        assertEquals(expectedTitle, mPreference.getTitle().toString());
+        assertEquals(expectedSummary, mPreference.getSummary().toString());
+        assertEquals(INFO_ICON, shadowOf(mPreference.getIcon()).getCreatedFromResId());
+        assertNull(mPreference.getPrimaryButtonText());
+        assertEquals(expectedSecondaryButtonText, mPreference.getSecondaryButtonText());
+    }
+
+    @Test
+    public void signOutAndNoLocalPasswordsExist() {
+        mockAccountPasswordState(SafetyHubAccountPasswordsDataSource.ModuleType.SIGNED_OUT);
+        mockLocalPasswordState(SafetyHubLocalPasswordsDataSource.ModuleType.NO_SAVED_PASSWORDS);
+        mModuleMediator.accountPasswordsStateChanged(
+                SafetyHubAccountPasswordsDataSource.ModuleType.SIGNED_OUT);
+        mModuleMediator.localPasswordsStateChanged(
+                SafetyHubLocalPasswordsDataSource.ModuleType.NO_SAVED_PASSWORDS);
+
+        verify(mMediatorDelegateMock, times(1)).onUpdateNeeded();
+
+        String expectedTitle = mActivity.getString(R.string.safety_hub_no_passwords_title);
+        String expectedSummary = mActivity.getString(R.string.safety_hub_no_passwords_summary);
+        String expectedSecondaryButtonText =
+                mActivity.getString(R.string.safety_hub_password_subpage_navigation_button);
+
+        assertEquals(expectedTitle, mPreference.getTitle().toString());
+        assertEquals(expectedSummary, mPreference.getSummary().toString());
+        assertEquals(INFO_ICON, shadowOf(mPreference.getIcon()).getCreatedFromResId());
+        assertNull(mPreference.getPrimaryButtonText());
+        assertEquals(expectedSecondaryButtonText, mPreference.getSecondaryButtonText());
+    }
+
+    @Test
     public void unavailableLocalPasswords_reusedAccountPasswordsExists() {
         mockAccountPasswordState(
                 SafetyHubAccountPasswordsDataSource.ModuleType.HAS_REUSED_PASSWORDS);
diff --git a/chrome/browser/sessions/session_restore_stats_collector.cc b/chrome/browser/sessions/session_restore_stats_collector.cc
index 25dcfd1a..263da194 100644
--- a/chrome/browser/sessions/session_restore_stats_collector.cc
+++ b/chrome/browser/sessions/session_restore_stats_collector.cc
@@ -13,8 +13,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/stringprintf.h"
+#include "base/strings/strcat.h"
 #include "base/task/bind_post_task.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/resource_coordinator/session_restore_policy.h"
@@ -120,6 +119,13 @@
              .status == blink::mojom::PermissionStatus::GRANTED;
 }
 
+void LogFirstPaintHistogram(base::TimeDelta paint_time,
+                            std::string_view suffix = "") {
+  base::UmaHistogramCustomTimes(
+      base::StrCat({"SessionRestore.ForegroundTabFirstPaint4", suffix}),
+      paint_time, base::Milliseconds(100), base::Minutes(16), 50);
+}
+
 }  // namespace
 
 SessionRestoreStatsCollector::TabLoaderStats::TabLoaderStats()
@@ -311,19 +317,26 @@
 void SessionRestoreStatsCollector::UmaStatsReportingDelegate::
     ReportTabLoaderStats(const TabLoaderStats& tab_loader_stats) {
   if (!tab_loader_stats.foreground_tab_first_paint.is_zero()) {
-    UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstPaint4",
-                               tab_loader_stats.foreground_tab_first_paint,
-                               base::Milliseconds(100), base::Minutes(16), 50);
-
-    std::string time_for_count = base::StringPrintf(
-        "SessionRestore.ForegroundTabFirstPaint4_%u",
-        static_cast<unsigned int>(tab_loader_stats.tab_count));
-    base::HistogramBase* counter_for_count = base::Histogram::FactoryTimeGet(
-        time_for_count, base::Milliseconds(100), base::Minutes(16), 50,
-        base::Histogram::kUmaTargetedHistogramFlag);
-    counter_for_count->AddTime(tab_loader_stats.foreground_tab_first_paint);
+    LogFirstPaintHistogram(tab_loader_stats.foreground_tab_first_paint);
+    std::string_view count_suffix;
+    CHECK_GT(tab_loader_stats.tab_count, 0u);
+    if (tab_loader_stats.tab_count < 2) {
+      count_suffix = ".1Tab";
+    } else if (tab_loader_stats.tab_count < 4) {
+      count_suffix = ".2to3Tabs";
+    } else if (tab_loader_stats.tab_count < 8) {
+      count_suffix = ".4to7Tabs";
+    } else if (tab_loader_stats.tab_count < 16) {
+      count_suffix = ".8to15Tabs";
+    } else if (tab_loader_stats.tab_count < 32) {
+      count_suffix = ".16to31Tabs";
+    } else {
+      count_suffix = ".32PlusTabs";
+    }
+    LogFirstPaintHistogram(tab_loader_stats.foreground_tab_first_paint,
+                           count_suffix);
   }
-  UMA_HISTOGRAM_ENUMERATION(
+  base::UmaHistogramEnumeration(
       "SessionRestore.ForegroundTabFirstPaint4.FinishReason",
       tab_loader_stats.tab_first_paint_reason, PAINT_FINISHED_UMA_MAX);
 
diff --git a/chrome/browser/sessions/session_restore_stats_collector.h b/chrome/browser/sessions/session_restore_stats_collector.h
index e23843d..efac1cd 100644
--- a/chrome/browser/sessions/session_restore_stats_collector.h
+++ b/chrome/browser/sessions/session_restore_stats_collector.h
@@ -36,7 +36,7 @@
   // Values other than PAINT_FINISHED_UMA_DONE indicate why FirstPaint time
   // was not recorded.
   enum SessionRestorePaintFinishReasonUma {
-    // SessionRestore.ForegroundTabFirstPaint4_XX successfully recorded.
+    // SessionRestore.ForegroundTabFirstPaint4 successfully recorded.
     PAINT_FINISHED_UMA_DONE = 0,
     // No tabs were visible the whole time before first paint.
     PAINT_FINISHED_UMA_NO_COMPLETELY_VISIBLE_TABS = 1,
@@ -65,7 +65,7 @@
     // any of the tabs involved in the session restore. If this is zero it is
     // because it has not been recorded (all restored tabs were closed or
     // hidden before they were painted, or were never painted). Corresponds to
-    // "SessionRestore.ForegroundTabFirstPaint4" and its _XX variants.
+    // "SessionRestore.ForegroundTabFirstPaint4" and its ".NTabs" variants.
     base::TimeDelta foreground_tab_first_paint;
 
     // Whether we recorded |foreground_tab_first_paint| and if not, why.
diff --git a/chrome/browser/smart_card/chromeos_smart_card_delegate.cc b/chrome/browser/smart_card/chromeos_smart_card_delegate.cc
index 7f4a192..abb2965 100644
--- a/chrome/browser/smart_card/chromeos_smart_card_delegate.cc
+++ b/chrome/browser/smart_card/chromeos_smart_card_delegate.cc
@@ -32,9 +32,19 @@
 // The check whether the window has focus is enough here, as this is for IWAs
 // only, which appear in standalone windows dedicated to only them.
 bool WindowHasFocus(content::RenderFrameHost& render_frame_host) {
-  return tabs::TabInterface::GetFromContents(
-             content::WebContents::FromRenderFrameHost(&render_frame_host)
-                 ->GetOutermostWebContents())
+  content::WebContents* tab_web_contents =
+      content::WebContents::FromRenderFrameHost(&render_frame_host)
+          ->GetOutermostWebContents();
+  CHECK(tab_web_contents);
+  // For the case when this is called in permission revoke handler called after
+  // closing the last windows of the origin (and thus expiring the one-time
+  // permission). Calling `tabs::TabInterface::GetFromContents` when the tab is
+  // closing will crash.
+  if (tab_web_contents->IsBeingDestroyed()) {
+    return false;
+  }
+
+  return tabs::TabInterface::GetFromContents(tab_web_contents)
       ->GetBrowserWindowInterface()
       ->IsActive();
 }
@@ -97,6 +107,12 @@
     content::RenderFrameHost& render_frame_host,
     const std::string& reader_name,
     RequestReaderPermissionCallback callback) {
+  // Never allow requesting permissions from the background.
+  if (!WindowHasFocus(render_frame_host)) {
+    std::move(callback).Run(false);
+    return;
+  }
+
   auto& profile = CHECK_DEREF(
       Profile::FromBrowserContext(render_frame_host.GetBrowserContext()));
 
diff --git a/chrome/browser/smart_card/chromeos_smart_card_delegate_browsertest.cc b/chrome/browser/smart_card/chromeos_smart_card_delegate_browsertest.cc
index 9ddf659..3cc0d66 100644
--- a/chrome/browser/smart_card/chromeos_smart_card_delegate_browsertest.cc
+++ b/chrome/browser/smart_card/chromeos_smart_card_delegate_browsertest.cc
@@ -8,8 +8,10 @@
 
 #include "base/check_deref.h"
 #include "base/memory/raw_ref.h"
+#include "base/test/bind.h"
 #include "base/test/gmock_expected_support.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/test_future.h"
 #include "base/time/time.h"
 #include "chrome/browser/smart_card/smart_card_permission_context.h"
 #include "chrome/browser/smart_card/smart_card_permission_context_factory.h"
@@ -19,6 +21,7 @@
 #include "components/content_settings/browser/page_specific_content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/content_settings/core/common/content_settings_types.mojom-shared.h"
+#include "components/tabs/public/tab_interface.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/smart_card_delegate.h"
@@ -74,6 +77,18 @@
                                          kDummyReader);
   }
 
+  void GrantEphemeralReaderPermission() {
+    SmartCardPermissionContextFactory::GetForProfile(*profile())
+        .GrantEphemeralReaderPermission(app_frame_->GetLastCommittedOrigin(),
+                                        kDummyReader);
+  }
+
+  bool HasReaderPermission(const url::Origin& origin,
+                           const std::string& reader) {
+    return SmartCardPermissionContextFactory::GetForProfile(*profile())
+        .HasReaderPermission(origin, reader);
+  }
+
  protected:
   std::unique_ptr<web_app::ScopedBundledIsolatedWebApp> app_;
   raw_ptr<content::RenderFrameHost> app_frame_ = nullptr;
@@ -177,3 +192,35 @@
   EXPECT_TRUE(
       GetSmartCardDelegate()->HasReaderPermission(*app_frame_, kDummyReader));
 }
+
+IN_PROC_BROWSER_TEST_F(ChromeOsSmartCardDelegateBrowserTest,
+                       PermissionRequestInTheBackgroundImpossible) {
+  GrantReaderPermission();
+
+  // Hiding takes focus from the window.
+  content::WebContents::FromRenderFrameHost(app_frame_)
+      ->GetTopLevelNativeWindow()
+      ->Hide();
+
+  base::test::TestFuture<bool> got_permission;
+  // This should immediately return false, not even trying to display any
+  // prompts.
+  GetSmartCardDelegate()->RequestReaderPermission(*app_frame_, kDummyReader,
+                                                  got_permission.GetCallback());
+  EXPECT_FALSE(got_permission.Get());
+}
+
+IN_PROC_BROWSER_TEST_F(ChromeOsSmartCardDelegateBrowserTest,
+                       ClosingWindowClearsEphemeralPermissions) {
+  GrantEphemeralReaderPermission();
+  const auto origin = app_frame_->GetLastCommittedOrigin();
+  EXPECT_TRUE(HasReaderPermission(origin, kDummyReader));
+
+  auto* tab_interface = tabs::TabInterface::GetFromContents(
+      content::WebContents::FromRenderFrameHost(app_frame_));
+  // Not resetting this causes dangling raw pointer error when closing tab.
+  app_frame_ = nullptr;
+  tab_interface->Close();
+
+  EXPECT_FALSE(HasReaderPermission(origin, kDummyReader));
+}
diff --git a/chrome/browser/storage_access_api/api_browsertest.cc b/chrome/browser/storage_access_api/api_browsertest.cc
index aed05abd..44ea616 100644
--- a/chrome/browser/storage_access_api/api_browsertest.cc
+++ b/chrome/browser/storage_access_api/api_browsertest.cc
@@ -2959,18 +2959,8 @@
 }
 
 class StorageAccessAPIAutograntsWithFedCMBrowserTest
-    : public StorageAccessAPIBaseBrowserTest,
-      public testing::WithParamInterface<bool> {
+    : public StorageAccessAPIBaseBrowserTest {
  public:
-  std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override {
-    if (override_feature_state()) {
-      return {{blink::features::kFedCmWithStorageAccessAPI, {}}};
-    }
-    return {};
-  }
-
-  bool override_feature_state() const { return GetParam(); }
-
   void GrantFedCMPermission() {
     const url::Origin rp_embedder =
         url::Origin::Create(GetURL(kHostASubdomain));
@@ -3012,7 +3002,7 @@
   }
 };
 
-IN_PROC_BROWSER_TEST_P(StorageAccessAPIAutograntsWithFedCMBrowserTest,
+IN_PROC_BROWSER_TEST_F(StorageAccessAPIAutograntsWithFedCMBrowserTest,
                        FedCMGrants_RequiresPermissionPolicy) {
   SetBlockThirdPartyCookies(true);
   GrantFedCMPermission();
@@ -3030,7 +3020,7 @@
   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame()));
 }
 
-IN_PROC_BROWSER_TEST_P(StorageAccessAPIAutograntsWithFedCMBrowserTest,
+IN_PROC_BROWSER_TEST_F(StorageAccessAPIAutograntsWithFedCMBrowserTest,
                        FedCMGrants_PreventSilentAccess) {
   SetBlockThirdPartyCookies(true);
   GrantFedCMPermission();
@@ -3051,7 +3041,7 @@
   EXPECT_EQ(ReadCookies(GetFrame(), kHostB), kNoCookies);
 }
 
-IN_PROC_BROWSER_TEST_P(StorageAccessAPIAutograntsWithFedCMBrowserTest,
+IN_PROC_BROWSER_TEST_F(StorageAccessAPIAutograntsWithFedCMBrowserTest,
                        FedCMGrants_PreventSilentAccess_AfterAutogrant) {
   SetBlockThirdPartyCookies(true);
   GrantFedCMPermission();
@@ -3072,7 +3062,7 @@
   EXPECT_EQ(ReadCookies(GetFrame(), kHostB), kNoCookies);
 }
 
-IN_PROC_BROWSER_TEST_P(StorageAccessAPIAutograntsWithFedCMBrowserTest,
+IN_PROC_BROWSER_TEST_F(StorageAccessAPIAutograntsWithFedCMBrowserTest,
                        FedCMGrants_PermissionPolicyHeaderIgnored) {
   SetBlockThirdPartyCookies(true);
   GrantFedCMPermission();
@@ -3110,7 +3100,7 @@
   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame()));
 }
 
-IN_PROC_BROWSER_TEST_P(StorageAccessAPIAutograntsWithFedCMBrowserTest,
+IN_PROC_BROWSER_TEST_F(StorageAccessAPIAutograntsWithFedCMBrowserTest,
                        FedCMGrantsAllowCookieAccessViaSAA) {
   SetBlockThirdPartyCookies(true);
   GrantFedCMPermission();
@@ -3132,7 +3122,7 @@
   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetFrame()));
 }
 
-IN_PROC_BROWSER_TEST_P(StorageAccessAPIAutograntsWithFedCMBrowserTest,
+IN_PROC_BROWSER_TEST_F(StorageAccessAPIAutograntsWithFedCMBrowserTest,
                        FedCMGrantsAllowCookieAccess_NestedFrame) {
   SetBlockThirdPartyCookies(true);
   GrantFedCMPermission();
@@ -3159,10 +3149,6 @@
   EXPECT_FALSE(storage::test::HasStorageAccessForFrame(GetNestedFrame()));
 }
 
-INSTANTIATE_TEST_SUITE_P(,
-                         StorageAccessAPIAutograntsWithFedCMBrowserTest,
-                         testing::Bool());
-
 class StorageAccessHeadersBrowserTest : public StorageAccessAPIBrowserTest {
  public:
   std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override {
@@ -3659,7 +3645,7 @@
   EXPECT_EQ(retry_path_fetch_count_, 1);
 }
 
-IN_PROC_BROWSER_TEST_P(StorageAccessAPIAutograntsWithFedCMBrowserTest,
+IN_PROC_BROWSER_TEST_F(StorageAccessAPIAutograntsWithFedCMBrowserTest,
                        RetryHeader) {
   SetBlockThirdPartyCookies(true);
   SetRetryAllowedOriginFromHost(kHostA);
diff --git a/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc b/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc
index 6e4d03a..84627b5 100644
--- a/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc
+++ b/chrome/browser/storage_access_api/storage_access_grant_permission_context.cc
@@ -197,31 +197,6 @@
   return permission_allowed;
 }
 
-// Returns true if the user/field trials have enabled FedCM/SAA autogrants
-// globally via the flag/Feature, or "locally" via the origin trial.
-//
-// Feature state overrides take precedence over origin trial state.
-bool AreFedCmAutograntsEnabled(content::RenderFrameHost* rfh) {
-  if (std::optional<bool> state = base::FeatureList::GetStateIfOverridden(
-          blink::features::kFedCmWithStorageAccessAPI);
-      state.has_value()) {
-    return state.value();
-  }
-
-  // RuntimeFeatureStateDocumentData doesn't know what the default state of a
-  // feature is, so we check the underlying feature explicitly.
-  if (base::FeatureList::IsEnabled(
-          blink::features::kFedCmWithStorageAccessAPI)) {
-    return true;
-  }
-  content::RuntimeFeatureStateDocumentData* document_data =
-      content::RuntimeFeatureStateDocumentData::GetForCurrentDocument(rfh);
-  CHECK(document_data);
-
-  return document_data->runtime_feature_state_read_context()
-      .IsFedCmWithStorageAccessAPIEnabled();
-}
-
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
 enum class AutograntViaFedCmOutcome {
@@ -244,8 +219,6 @@
     const net::SchemefulSite& embedding_site,
     const net::SchemefulSite& requesting_site) {
   CHECK(browser_context);
-  CHECK(base::FeatureList::IsEnabled(
-      blink::features::kFedCmWithStorageAccessAPI));
   if (!rfh->IsFeatureEnabled(
           network::mojom::PermissionsPolicyFeature::kIdentityCredentialsGet)) {
     RecordAutograntViaFedCmOutcomeSample(
@@ -419,17 +392,15 @@
 
   // FedCM grants (and the appropriate permissions policy) may allow the call to
   // auto-resolve (without granting a new permission).
-  if (AreFedCmAutograntsEnabled(rfh)) {
-    if (FederatedIdentityPermissionContext* fedcm_context =
-            IsAutograntViaFedCmAllowed(browser_context(), rfh, embedding_origin,
-                                       embedding_site, requesting_site);
-        fedcm_context) {
-      fedcm_context->MarkStorageAccessEligible(
-          /*relying_party_embedder=*/embedding_site,
-          /*identity_provider=*/requesting_site,
-          base::BindOnce(std::move(callback), CONTENT_SETTING_ALLOW));
-      return;
-    }
+  if (FederatedIdentityPermissionContext* fedcm_context =
+          IsAutograntViaFedCmAllowed(browser_context(), rfh, embedding_origin,
+                                     embedding_site, requesting_site);
+      fedcm_context) {
+    fedcm_context->MarkStorageAccessEligible(
+        /*relying_party_embedder=*/embedding_site,
+        /*identity_provider=*/requesting_site,
+        base::BindOnce(std::move(callback), CONTENT_SETTING_ALLOW));
+    return;
   }
 
   if (!request_data->user_gesture) {
diff --git a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
index bc4b212..156a6953 100644
--- a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
+++ b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
@@ -865,16 +865,6 @@
   void SetUp() override {
     StorageAccessGrantPermissionContextTest::SetUp();
 
-    // This feature is already enabled by default, but the SUT behavior does
-    // different things if it's overridden or not. So we also try initializing
-    // as-is, without overriding.
-    if (override_feature_state()) {
-      feature_list_.InitAndEnableFeature(
-          blink::features::kFedCmWithStorageAccessAPI);
-    } else {
-      feature_list_.Init();
-    }
-
     FederatedIdentityPermissionContextFactory::GetForProfile(profile())
         ->GrantSharingPermission(
             /*relying_party_requester=*/url::Origin::Create(
@@ -885,10 +875,7 @@
             "my_account");
   }
 
-  bool override_feature_state() const { return GetParam(); }
-
  private:
-  base::test::ScopedFeatureList feature_list_;
 };
 
 TEST_P(StorageAccessGrantPermissionContextAPIWithFedCMConnectionTest,
diff --git a/chrome/browser/supervised_user/linux_mac_windows/parent_access_view.cc b/chrome/browser/supervised_user/linux_mac_windows/parent_access_view.cc
index 56b4b748..2748571 100644
--- a/chrome/browser/supervised_user/linux_mac_windows/parent_access_view.cc
+++ b/chrome/browser/supervised_user/linux_mac_windows/parent_access_view.cc
@@ -462,7 +462,7 @@
     SetBackground(nullptr);
   }
 
-  border->SetCornerRadius(corner_radius_);
+  border->set_rounded_corners(gfx::RoundedCornersF(corner_radius_));
   widget->widget_delegate()
       ->AsDialogDelegate()
       ->GetBubbleFrameView()
diff --git a/chrome/browser/tab_ui/android/BUILD.gn b/chrome/browser/tab_ui/android/BUILD.gn
index a7c57f1a..6042bf5 100644
--- a/chrome/browser/tab_ui/android/BUILD.gn
+++ b/chrome/browser/tab_ui/android/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/config/android/rules.gni")
-import("//build/config/locales.gni")
 import("//chrome/android/features/android_library_factory_tmpl.gni")
 import("//chrome/common/features.gni")
 import("//third_party/jni_zero/jni_zero.gni")
@@ -12,10 +11,6 @@
 java_strings_grd("java_strings_grd") {
   defines = chrome_grit_defines
   grd_file = "java/strings/android_tab_ui_strings.grd"
-  outputs = [ "values/android_tab_ui_strings.xml" ] +
-            process_file_template(
-                android_bundle_locales_as_resources,
-                [ "values-{{source_name_part}}/android_tab_ui_strings.xml" ])
 }
 
 android_library("java") {
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java
index 236b39e..a885169 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java
@@ -746,11 +746,13 @@
         return Collections.unmodifiableList(tabs);
     }
 
+    /** Whether {@param tab}'s {@code TabLaunchType} should be part of a group with its parent. */
     private boolean shouldUseParentIds(Tab tab) {
         @TabLaunchType int tabLaunchType = tab.getLaunchType();
         return isTabModelRestored()
                 && !mIsResetting
                 && (tabLaunchType == TabLaunchType.FROM_TAB_GROUP_UI
+                        || tabLaunchType == TabLaunchType.FROM_LONGPRESS_FOREGROUND_IN_GROUP
                         || tabLaunchType == TabLaunchType.FROM_LONGPRESS_BACKGROUND_IN_GROUP
                         || tabLaunchType == TabLaunchType.FROM_COLLABORATION_BACKGROUND_IN_GROUP);
     }
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java
index aaf82baa..cd36b7a6 100644
--- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderControllerImpl.java
@@ -33,7 +33,7 @@
         if (type == TabLaunchType.FROM_BROWSER_ACTIONS || type == TabLaunchType.FROM_RECENT_TABS) {
             return -1;
         }
-        if (linkClicked(type)) {
+        if (mightBeAdjacent(type)) {
             position = determineInsertionIndex(type, newTab);
         }
 
@@ -138,13 +138,16 @@
         }
     }
 
-    /** Determine if a launch type is the result of linked being clicked. */
-    static boolean linkClicked(@TabLaunchType int type) {
+    /** Determine if a launch type requires calculation to determine the position of the new tab. */
+    static boolean mightBeAdjacent(@TabLaunchType int type) {
         return type == TabLaunchType.FROM_LINK
                 || type == TabLaunchType.FROM_LONGPRESS_FOREGROUND
+                || type == TabLaunchType.FROM_LONGPRESS_FOREGROUND_IN_GROUP
                 || type == TabLaunchType.FROM_LONGPRESS_BACKGROUND
                 || type == TabLaunchType.FROM_LONGPRESS_BACKGROUND_IN_GROUP
-                || type == TabLaunchType.FROM_LONGPRESS_INCOGNITO;
+                || type == TabLaunchType.FROM_LONGPRESS_INCOGNITO
+                || type == TabLaunchType.FROM_HISTORY_NAVIGATION_BACKGROUND
+                || type == TabLaunchType.FROM_HISTORY_NAVIGATION_FOREGROUND;
     }
 
     @Override
diff --git a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/FlatBufferTabStateSerializer.java b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/FlatBufferTabStateSerializer.java
index adab1cb..8eeea822 100644
--- a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/FlatBufferTabStateSerializer.java
+++ b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/FlatBufferTabStateSerializer.java
@@ -226,6 +226,8 @@
                 return TabLaunchType.FROM_HISTORY_NAVIGATION_BACKGROUND;
             case TabLaunchTypeAtCreation.FROM_HISTORY_NAVIGATION_FOREGROUND:
                 return TabLaunchType.FROM_HISTORY_NAVIGATION_FOREGROUND;
+            case TabLaunchTypeAtCreation.FROM_LONGPRESS_FOREGROUND_IN_GROUP:
+                return TabLaunchType.FROM_LONGPRESS_FOREGROUND_IN_GROUP;
             case TabLaunchTypeAtCreation.SIZE:
                 return TabLaunchType.SIZE;
             case TabLaunchTypeAtCreation.UNKNOWN:
@@ -302,6 +304,8 @@
                 return TabLaunchTypeAtCreation.FROM_HISTORY_NAVIGATION_BACKGROUND;
             case TabLaunchType.FROM_HISTORY_NAVIGATION_FOREGROUND:
                 return TabLaunchTypeAtCreation.FROM_HISTORY_NAVIGATION_FOREGROUND;
+            case TabLaunchType.FROM_LONGPRESS_FOREGROUND_IN_GROUP:
+                return TabLaunchTypeAtCreation.FROM_LONGPRESS_FOREGROUND_IN_GROUP;
             case TabLaunchType.SIZE:
                 return TabLaunchTypeAtCreation.SIZE;
             default:
diff --git a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManagerUnitTest.java b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManagerUnitTest.java
index 78ede47..04009945 100644
--- a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManagerUnitTest.java
+++ b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/TabStateFileManagerUnitTest.java
@@ -167,12 +167,13 @@
         Assert.assertEquals(27, TabLaunchTypeAtCreation.FROM_REPARENTING_BACKGROUND);
         Assert.assertEquals(28, TabLaunchTypeAtCreation.FROM_HISTORY_NAVIGATION_BACKGROUND);
         Assert.assertEquals(29, TabLaunchTypeAtCreation.FROM_HISTORY_NAVIGATION_FOREGROUND);
+        Assert.assertEquals(30, TabLaunchTypeAtCreation.FROM_LONGPRESS_FOREGROUND_IN_GROUP);
         // Note this should be the total number of TabLaunchTypeAtCreation values including
         // SIZE and UNKNOWN so it should be equal to the last value +3.
         Assert.assertEquals(
                 "Need to increment 1 to expected value each time a LaunchTypeAtCreation "
                         + "is added. Also need to add any new LaunchTypeAtCreation to this test.",
-                32,
+                33,
                 TabLaunchTypeAtCreation.names.length);
     }
 
@@ -183,7 +184,7 @@
                         + " FlatBufferTabStateSerializer#getLaunchTypeFromFlatBuffer,"
                         + " FlatBufferTabStateSerializer#getLaunchTypeToFlatBuffer"
                         + " and this test file.",
-                30,
+                31,
                 TabLaunchType.SIZE);
     }
 
@@ -330,6 +331,10 @@
                 FlatBufferTabStateSerializer.getLaunchTypeFromFlatBuffer(
                         TabLaunchTypeAtCreation.FROM_HISTORY_NAVIGATION_FOREGROUND));
         Assert.assertEquals(
+                TabLaunchType.FROM_LONGPRESS_FOREGROUND_IN_GROUP,
+                FlatBufferTabStateSerializer.getLaunchTypeFromFlatBuffer(
+                        TabLaunchTypeAtCreation.FROM_LONGPRESS_FOREGROUND_IN_GROUP));
+        Assert.assertEquals(
                 TabLaunchType.UNSET,
                 FlatBufferTabStateSerializer.getLaunchTypeFromFlatBuffer(
                         TabLaunchTypeAtCreation.UNKNOWN));
@@ -454,6 +459,10 @@
                 FlatBufferTabStateSerializer.getLaunchTypeToFlatBuffer(
                         TabLaunchType.FROM_HISTORY_NAVIGATION_FOREGROUND));
         Assert.assertEquals(
+                TabLaunchTypeAtCreation.FROM_LONGPRESS_FOREGROUND_IN_GROUP,
+                FlatBufferTabStateSerializer.getLaunchTypeToFlatBuffer(
+                        TabLaunchType.FROM_LONGPRESS_FOREGROUND_IN_GROUP));
+        Assert.assertEquals(
                 TabLaunchTypeAtCreation.UNSET,
                 FlatBufferTabStateSerializer.getLaunchTypeToFlatBuffer(TabLaunchType.UNSET));
         Assert.assertEquals(
diff --git a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/flatbuffer/tab_state_common.fbs b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/flatbuffer/tab_state_common.fbs
index a8a02c7..5f24ea57 100644
--- a/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/flatbuffer/tab_state_common.fbs
+++ b/chrome/browser/tabpersistence/android/java/src/org/chromium/chrome/browser/tabpersistence/flatbuffer/tab_state_common.fbs
@@ -49,6 +49,7 @@
     FROM_REPARENTING_BACKGROUND = 27,
     FROM_HISTORY_NAVIGATION_BACKGROUND = 28,
     FROM_HISTORY_NAVIGATION_FOREGROUND = 29,
+    FROM_LONGPRESS_FOREGROUND_IN_GROUP = 30,
     // Add new values here and don't change existing values
     // as they are persisted across restarts. Changing existing
     // values will lead to backwards compatibility issues crbug.com/1286984.
diff --git a/chrome/browser/themes/theme_syncable_service_unittest.cc b/chrome/browser/themes/theme_syncable_service_unittest.cc
index 935a85d..1ce12ad 100644
--- a/chrome/browser/themes/theme_syncable_service_unittest.cc
+++ b/chrome/browser/themes/theme_syncable_service_unittest.cc
@@ -4014,7 +4014,12 @@
       std::make_unique<syncer::SyncChangeProcessorWrapperForTest>(
           fake_change_processor_.get())));
 
-  ASSERT_EQ(fake_change_processor_->changes().size(), 1u);
+  ASSERT_EQ(fake_change_processor_->changes().size(),
+            base::FeatureList::IsEnabled(syncer::kSeparateLocalAndAccountThemes)
+                ? 0u
+                : 1u);
+  fake_change_processor_->changes().clear();
+
   ASSERT_EQ(prefs()->GetInteger(prefs::kNonSyncingUserColorDoNotUse),
             static_cast<int>(SK_ColorBLUE));
   ASSERT_EQ(prefs()->GetInteger(prefs::kNonSyncingBrowserColorVariantDoNotUse),
@@ -4029,7 +4034,7 @@
       std::make_unique<syncer::SyncChangeProcessorWrapperForTest>(
           fake_change_processor_.get())));
 
-  EXPECT_EQ(2, std::ranges::count_if(
+  EXPECT_EQ(1, std::ranges::count_if(
                    fake_change_processor_->changes(), [](const auto& e) {
                      return e.sync_data().GetSpecifics().has_theme();
                    }));
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 43a0f9d..8daa3b3 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
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/config/android/rules.gni")
-import("//build/config/locales.gni")
 import("//chrome/common/features.gni")
 import("//third_party/jni_zero/jni_zero.gni")
 import("//tools/grit/grit_rule.gni")
@@ -89,9 +88,4 @@
 java_strings_grd("java_strings_grd") {
   defines = chrome_grit_defines
   grd_file = "java/strings/android_touch_to_fill_strings.grd"
-  outputs =
-      [ "values/android_touch_to_fill_strings.xml" ] +
-      process_file_template(
-          android_bundle_locales_as_resources,
-          [ "values-{{source_name_part}}/android_touch_to_fill_strings.xml" ])
 }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 1c0780e..e1f99b8b4 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1842,7 +1842,6 @@
       "//components/saved_tab_groups/internal",
       "//components/saved_tab_groups/internal:tab_group_sync_bridge",
       "//components/saved_tab_groups/public",
-      "//components/search_engines:generate_search_engine_icons",
       "//components/search_provider_logos",
       "//components/services/app_service",
       "//components/supervised_user/core/common",
@@ -1970,7 +1969,7 @@
       allow_circular_includes_from += [ "//chrome/browser/ui/find_bar:impl" ]
     }
 
-    if (is_win) {
+    if (is_win && is_chrome_branded) {
       sources += [
         "startup/installer_downloader/installer_downloader_infobar_delegate.cc",
         "startup/installer_downloader/installer_downloader_infobar_delegate.h",
@@ -5842,6 +5841,7 @@
     "//extensions/buildflags",
   ]
   deps = [
+    "//components/search",
     "//components/webui/flags",
     "//ui/base:features",
   ]
diff --git a/chrome/browser/ui/android/strings/BUILD.gn b/chrome/browser/ui/android/strings/BUILD.gn
index e878520..21d12301 100644
--- a/chrome/browser/ui/android/strings/BUILD.gn
+++ b/chrome/browser/ui/android/strings/BUILD.gn
@@ -2,15 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/locales.gni")
 import("//chrome/common/features.gni")
 import("//tools/grit/grit_rule.gni")
 
 java_strings_grd("ui_strings_grd") {
   defines = chrome_grit_defines
   grd_file = "android_chrome_strings.grd"
-  outputs = [ "values/android_chrome_strings.xml" ] +
-            process_file_template(
-                android_bundle_locales_as_resources,
-                [ "values-{{source_name_part}}/android_chrome_strings.xml" ])
 }
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 619df4ef..d206b26 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -1871,6 +1871,9 @@
       <message name="IDS_SAFETY_HUB_NO_LOCAL_PASSWORDS_TITLE" desc="Title for the local password check row in the Safety Check page indicating that the user does not have any local passwords saved.">
         No saved passwords on this device
       </message>
+      <message name="IDS_SAFETY_HUB_NO_PASSWORDS_TITLE" desc="Title for the password check row in the Safety Check page indicating that the user does not have any local or account passwords saved.">
+        No saved passwords
+      </message>
       <message name="IDS_SAFETY_HUB_NO_PASSWORDS_SUMMARY" desc="Summary for the password check row in the Safety Check page indicating that the user has no passwords saved and that Safety check will start checking for breached passwords when they save them.">
         Chrome can check your passwords when you save them
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SAFETY_HUB_NO_PASSWORDS_TITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SAFETY_HUB_NO_PASSWORDS_TITLE.png.sha1
new file mode 100644
index 0000000..fc3a2475
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SAFETY_HUB_NO_PASSWORDS_TITLE.png.sha1
@@ -0,0 +1 @@
+d40606995630cf47cd4c23a97fb7dc1966abc474
\ No newline at end of file
diff --git a/chrome/browser/ui/android/tab_model/tab_model.h b/chrome/browser/ui/android/tab_model/tab_model.h
index 773b8433..e03dd9fb 100644
--- a/chrome/browser/ui/android/tab_model/tab_model.h
+++ b/chrome/browser/ui/android/tab_model/tab_model.h
@@ -142,6 +142,9 @@
     // From history navigation (back / forward) when opening a new tab/window in
     // the foreground.
     FROM_HISTORY_NAVIGATION_FOREGROUND,
+    // Like FROM_LONGPRESS_FOREGROUND, but used when the parent tab is part of a
+    // group.
+    FROM_LONGPRESS_FOREGROUND_IN_GROUP,
     // Must be last.
     SIZE
   };
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
index 0d6913a..10c1627 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
@@ -516,19 +516,29 @@
     void updateBookmarkButton(boolean isBookmarked, boolean editingAllowed) {}
 
     /**
-     * Gives inheriting classes the chance to update home button UI if home button preference is
-     * changed.
+     * Gives inheriting classes the chance to update home button UI if home button's enable
+     * preference is changed.
      *
      * @param homeButtonEnabled Whether or not home button is enabled in preference.
      */
-    void onHomeButtonUpdate(boolean homeButtonEnabled) {}
+    void onHomeButtonIsEnabledUpdate(boolean homeButtonEnabled) {}
+
+    /**
+     * Gives inheriting classes the chance to update home button UI if the current homepage is set
+     * to something other than the NTP.
+     *
+     * @param isHomepageNonNtp Whether the current homepage is something other than the NTP.
+     */
+    // TODO(crbug.com/407554279): Usage will be added in follow-up CLs related to the NTP
+    // customization toolbar button.
+    void onHomepageIsNonNtpUpdate(boolean isHomepageNonNtp) {}
 
     /**
      * Triggered when the current tab or model has changed.
-     * <p>
-     * As there are cases where you can select a model with no tabs (i.e. having incognito
-     * tabs but no normal tabs will still allow you to select the normal model), this should
-     * not guarantee that the model's current tab is non-null.
+     *
+     * <p>As there are cases where you can select a model with no tabs (i.e. having incognito tabs
+     * but no normal tabs will still allow you to select the normal model), this should not
+     * guarantee that the model's current tab is non-null.
      */
     void onTabOrModelChanged() {}
 
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index e167483..9df1d2b9 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -1679,7 +1679,7 @@
     }
 
     @Override
-    public void onHomeButtonUpdate(boolean homeButtonEnabled) {
+    public void onHomeButtonIsEnabledUpdate(boolean homeButtonEnabled) {
         mIsHomeButtonEnabled = homeButtonEnabled;
         updateButtonVisibility();
     }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
index ecc084b..3680c42 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
@@ -444,7 +444,7 @@
     }
 
     @Override
-    void onHomeButtonUpdate(boolean homeButtonEnabled) {
+    void onHomeButtonIsEnabledUpdate(boolean homeButtonEnabled) {
         mHomeButton.setVisibility(homeButtonEnabled ? VISIBLE : GONE);
     }
 
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
index fb6dd468..9f912d6 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
@@ -138,6 +138,8 @@
      * @param tabCountSupplier Supplier of {@link
      *     org.chromium.chrome.browser.toolbar.CustomTabCount}.
      * @param homepageEnabledSupplier Supplier of whether Home button is enabled.
+     * @param homepageNonNtpSupplier Supplier of whether homepage is set to something other than the
+     *     NTP.
      * @param resourceManagerSupplier A supplier of a resource manager for native textures.
      * @param historyDelegate Delegate used to display navigation history.
      * @param initializeWithIncognitoColors Whether the toolbar should be initialized with incognito
@@ -167,6 +169,7 @@
             ToggleTabStackButtonCoordinator tabSwitcherButtonCoordinator,
             ObservableSupplier<Integer> tabCountSupplier,
             ObservableSupplier<Boolean> homepageEnabledSupplier,
+            ObservableSupplier<Boolean> homepageNonNtpSupplier,
             Supplier<ResourceManager> resourceManagerSupplier,
             HistoryDelegate historyDelegate,
             boolean initializeWithIncognitoColors,
@@ -249,7 +252,10 @@
         mToolbarLayout.setThemeColorProvider(normalThemeColorProvider);
         mAppMenuButtonHelperSupplier = appMenuButtonHelperSupplier;
         new OneShotCallback<>(mAppMenuButtonHelperSupplier, this::setAppMenuButtonHelper);
-        homepageEnabledSupplier.addObserver((show) -> mToolbarLayout.onHomeButtonUpdate(show));
+        homepageEnabledSupplier.addObserver(
+                (show) -> mToolbarLayout.onHomeButtonIsEnabledUpdate(show));
+        homepageNonNtpSupplier.addObserver(
+                (isNonNtp) -> mToolbarLayout.onHomepageIsNonNtpUpdate(isNonNtp));
     }
 
     /**
diff --git a/chrome/browser/ui/ash/input_method/border_factory.cc b/chrome/browser/ui/ash/input_method/border_factory.cc
index e91f12dd..bdf44611 100644
--- a/chrome/browser/ui/ash/input_method/border_factory.cc
+++ b/chrome/browser/ui/ash/input_method/border_factory.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/ash/input_method/border_factory.h"
 
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/views/layout/layout_provider.h"
 
 namespace ui {
@@ -23,8 +24,10 @@
           views::LayoutProvider::Get()->GetShadowElevationMetric(
               views::Emphasis::kMedium));
   }
-  border->SetCornerRadius(views::LayoutProvider::Get()->GetCornerRadiusMetric(
-      views::Emphasis::kMedium));
+
+  const int corner_radius = views::LayoutProvider::Get()->GetCornerRadiusMetric(
+      views::Emphasis::kMedium);
+  border->set_rounded_corners(gfx::RoundedCornersF(corner_radius));
   return border;
 }
 
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
index f5c1a28..d04fd6cd 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
@@ -51,6 +51,7 @@
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/font_list.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/transform_util.h"
 #include "ui/gfx/image/image_skia.h"
@@ -531,7 +532,7 @@
   auto bubble_border =
       std::make_unique<views::BubbleBorder>(arrow(), GetShadow());
   bubble_border->SetColor(background_color());
-  bubble_border->SetCornerRadius(kCornerRadius);
+  bubble_border->set_rounded_corners(gfx::RoundedCornersF(kCornerRadius));
   auto frame =
       views::BubbleDialogDelegateView::CreateNonClientFrameView(widget);
   static_cast<views::BubbleFrameView*>(frame.get())
diff --git a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc
index dac0dd4..d6a53e8 100644
--- a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl.cc
@@ -562,6 +562,17 @@
             pdm->address_data_manager().GetProfileByGUID(
                 std::get<Suggestion::AutofillProfilePayload>(payload)
                     .guid.value())) {
+      // Home & Work addresses can't be deleted through the chrome UI.
+      switch (profile->record_type()) {
+        case AutofillProfile::RecordType::kAccountHome:
+        case AutofillProfile::RecordType::kAccountWork:
+          return false;
+        default:
+        case AutofillProfile::RecordType::kLocalOrSyncable:
+        case AutofillProfile::RecordType::kAccount:
+          break;
+      }
+
       if (title) {
         std::u16string street_address = profile->GetRawInfo(ADDRESS_HOME_CITY);
         if (!street_address.empty()) {
diff --git a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc
index 1061eb4..2d2f256f 100644
--- a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/ui/autofill/autofill_suggestion_controller_test_base.h"
 #include "chrome/browser/ui/autofill/test_autofill_keyboard_accessory_controller_autofill_client.h"
 #include "components/autofill/core/browser/data_manager/addresses/address_data_manager.h"
+#include "components/autofill/core/browser/data_model/addresses/autofill_profile_test_api.h"
 #include "components/autofill/core/browser/suggestions/suggestion_hiding_reason.h"
 #include "components/strings/grit/components_strings.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -251,6 +252,44 @@
                 IDS_AUTOFILL_DELETE_PROFILE_SUGGESTION_CONFIRMATION_BODY));
 }
 
+TEST_F(AutofillKeyboardAccessoryControllerImplTest,
+       GetRemovalConfirmationText_AutofillProfile_Home) {
+  AutofillProfile profile = test::GetFullProfile();
+  test_api(profile).set_record_type(AutofillProfile::RecordType::kAccountHome);
+
+  personal_data().address_data_manager().AddProfile(profile);
+
+  ShowSuggestions(manager(),
+                  {test::CreateAutofillSuggestion(
+                      SuggestionType::kAddressEntry, u"Autofill home profile",
+                      Suggestion::AutofillProfilePayload(
+                          Suggestion::Guid(profile.guid())))});
+
+  EXPECT_CALL(*client().popup_view(), ConfirmDeletion).Times(0);
+  EXPECT_FALSE(client().popup_controller(manager()).RemoveSuggestion(
+      /*index=*/0,
+      AutofillMetrics::SingleEntryRemovalMethod::kKeyboardAccessory));
+}
+
+TEST_F(AutofillKeyboardAccessoryControllerImplTest,
+       GetRemovalConfirmationText_AutofillProfile_Work) {
+  AutofillProfile profile = test::GetFullProfile();
+  test_api(profile).set_record_type(AutofillProfile::RecordType::kAccountWork);
+
+  personal_data().address_data_manager().AddProfile(profile);
+
+  ShowSuggestions(manager(),
+                  {test::CreateAutofillSuggestion(
+                      SuggestionType::kAddressEntry, u"Autofill work profile",
+                      Suggestion::AutofillProfilePayload(
+                          Suggestion::Guid(profile.guid())))});
+
+  EXPECT_CALL(*client().popup_view(), ConfirmDeletion).Times(0);
+  EXPECT_FALSE(client().popup_controller(manager()).RemoveSuggestion(
+      /*index=*/0,
+      AutofillMetrics::SingleEntryRemovalMethod::kKeyboardAccessory));
+}
+
 // Tests that a call to `RemoveSuggestion()` leads to a deletion confirmation
 // dialog and, on accepting that dialog, to the deletion of the suggestion and
 // the a11y announcement that it was deleted.
diff --git a/chrome/browser/ui/bookmarks/bookmark_browsertest.cc b/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
index e5b5ad4f..2d9aef8e1 100644
--- a/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
@@ -243,7 +243,7 @@
   const BookmarkNode* const folder = bookmark_model->AddFolder(
       bookmark_model->bookmark_bar_node(), 0, u"Folder");
   const BookmarkNode* const page1 = bookmark_model->AddURL(
-      folder, 0, u"BookmarkManager", GURL(chrome::kChromeUIBookmarksURL));
+      folder, 0, u"Extensions", GURL(chrome::kChromeUIExtensionsURL));
   const BookmarkNode* const page2 = bookmark_model->AddURL(
       folder, 1, u"Settings", GURL(chrome::kChromeUISettingsURL));
 
diff --git a/chrome/browser/ui/bookmarks/bookmark_tab_helper.cc b/chrome/browser/ui/bookmarks/bookmark_tab_helper.cc
index c74b14f..633a0fb 100644
--- a/chrome/browser/ui/bookmarks/bookmark_tab_helper.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_tab_helper.cc
@@ -27,70 +27,12 @@
 using bookmarks::BookmarkModel;
 using bookmarks::BookmarkNode;
 
-namespace {
-
-// TODO(crbug.com/382494946): Similar bespoke checks are used throughout the
-// codebase. This should be factored out as a common util and other callsites
-// converted to use this.
-bool IsNTP(content::WebContents* web_contents) {
-  // Use the committed entry (or the visible entry, if the committed entry is
-  // the initial NavigationEntry) so the bookmarks bar disappears at the same
-  // time the page does.
-  content::NavigationEntry* entry =
-      web_contents->GetController().GetLastCommittedEntry();
-  if (entry->IsInitialEntry()) {
-    entry = web_contents->GetController().GetVisibleEntry();
-  }
-  const GURL& url = entry->GetURL();
-  return NewTabUI::IsNewTab(url) || NewTabPageUI::IsNewTabPageOrigin(url) ||
-         NewTabPageThirdPartyUI::IsNewTabPageOrigin(url) ||
-         search::NavEntryIsInstantNTP(web_contents, entry);
-}
-
-}  // namespace
-
 BookmarkTabHelper::~BookmarkTabHelper() {
   if (bookmark_model_) {
     bookmark_model_->RemoveObserver(this);
   }
 }
 
-bool BookmarkTabHelper::ShouldShowBookmarkBar() const {
-  if (SadTab::ShouldShow(web_contents()->GetCrashedStatus())) {
-    return false;
-  }
-
-  if (!browser_defaults::bookmarks_enabled) {
-    return false;
-  }
-
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
-
-#if !BUILDFLAG(IS_CHROMEOS)
-  if (profile->IsGuestSession()) {
-    return false;
-  }
-#endif
-
-  PrefService* prefs = profile->GetPrefs();
-  if (prefs->IsManagedPreference(bookmarks::prefs::kShowBookmarkBar) &&
-      !prefs->GetBoolean(bookmarks::prefs::kShowBookmarkBar)) {
-    return false;
-  }
-
-  const bool has_bookmarks = bookmark_model_ && bookmark_model_->HasBookmarks();
-
-  tab_groups::TabGroupSyncService* tab_group_service =
-      tab_groups::SavedTabGroupUtils::GetServiceForProfile(profile);
-  const bool has_saved_tab_groups =
-      tab_group_service && !tab_group_service->GetAllGroups().empty();
-
-  // The bookmark bar is only shown on the NTP if the user
-  // has added something to it.
-  return IsNTP(web_contents()) && (has_bookmarks || has_saved_tab_groups);
-}
-
 void BookmarkTabHelper::AddObserver(BookmarkTabHelperObserver* observer) {
   observers_.AddObserver(observer);
 }
diff --git a/chrome/browser/ui/bookmarks/bookmark_tab_helper.h b/chrome/browser/ui/bookmarks/bookmark_tab_helper.h
index d680502..6e290cc 100644
--- a/chrome/browser/ui/bookmarks/bookmark_tab_helper.h
+++ b/chrome/browser/ui/bookmarks/bookmark_tab_helper.h
@@ -56,8 +56,6 @@
 
   bool is_starred() const { return is_starred_; }
 
-  bool ShouldShowBookmarkBar() const;
-
   void AddObserver(BookmarkTabHelperObserver* observer);
   void RemoveObserver(BookmarkTabHelperObserver* observer);
   bool HasObserver(BookmarkTabHelperObserver* observer) const;
diff --git a/chrome/browser/ui/bookmarks/bookmark_ui_utils_desktop_unittest.cc b/chrome/browser/ui/bookmarks/bookmark_ui_utils_desktop_unittest.cc
index e8e5c3b..100c7d2 100644
--- a/chrome/browser/ui/bookmarks/bookmark_ui_utils_desktop_unittest.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_ui_utils_desktop_unittest.cc
@@ -67,11 +67,11 @@
   std::unique_ptr<BookmarkModel> model(
       bookmarks::TestBookmarkClient::CreateModel());
   std::vector<raw_ptr<const BookmarkNode, VectorExperimental>> nodes;
+  const GURL history_page = GURL(chrome::kChromeUIHistoryURL);
 
   // This tests that |nodes| contains an disabled-in-incognito URL.
   const BookmarkNode* page1 =
-      model->AddURL(model->bookmark_bar_node(), 0, u"BookmarkManager",
-                    GURL(chrome::kChromeUIBookmarksURL));
+      model->AddURL(model->bookmark_bar_node(), 0, u"History", history_page);
   nodes.push_back(page1);
   EXPECT_FALSE(chrome::HasBookmarkURLsAllowedInIncognitoMode(nodes));
   nodes.clear();
@@ -94,7 +94,7 @@
   // This verifies if HasBookmarkURLsAllowedInIncognitoMode iterates through
   // immediate children.
   // Add disabled-in-incognito url.
-  model->AddURL(folder1, 0, u"Foo", GURL(chrome::kChromeUIBookmarksURL));
+  model->AddURL(folder1, 0, u"Foo", history_page);
   EXPECT_FALSE(chrome::HasBookmarkURLsAllowedInIncognitoMode(nodes));
   // Add normal url.
   model->AddURL(folder1, 0, u"Foo", GURL("http://randomsite.com"));
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 25216a4..30f168f3 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -38,6 +38,7 @@
 #include "chrome/browser/background/background_contents.h"
 #include "chrome/browser/background/background_contents_service.h"
 #include "chrome/browser/background/background_contents_service_factory.h"
+#include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/buildflags.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
@@ -123,6 +124,7 @@
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/overscroll_pref_manager.h"
 #include "chrome/browser/ui/page_action/page_action_icon_type.h"
+#include "chrome/browser/ui/sad_tab.h"
 #include "chrome/browser/ui/search/search_tab_helper.h"
 #include "chrome/browser/ui/signin/cookie_clear_on_exit_migration_notice.h"
 #include "chrome/browser/ui/singleton_tabs.h"
@@ -132,6 +134,7 @@
 #include "chrome/browser/ui/tab_dialogs.h"
 #include "chrome/browser/ui/tab_helpers.h"
 #include "chrome/browser/ui/tab_modal_confirm_dialog.h"
+#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
 #include "chrome/browser/ui/tabs/tab_enums.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_deletion_dialog_controller.h"
@@ -146,6 +149,9 @@
 #include "chrome/browser/ui/views/status_bubble_views.h"
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
+#include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h"
+#include "chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.h"
+#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
 #include "chrome/browser/ui/window_sizer/window_sizer.h"
@@ -478,6 +484,24 @@
   return false;
 }
 
+// TODO(crbug.com/382494946): Similar bespoke checks are used throughout the
+// codebase. This should be factored out as a common util and other callsites
+// converted to use this.
+bool IsNTP(content::WebContents* web_contents) {
+  // Use the committed entry (or the visible entry, if the committed entry is
+  // the initial NavigationEntry) so the bookmarks bar disappears at the same
+  // time the page does.
+  content::NavigationEntry* entry =
+      web_contents->GetController().GetLastCommittedEntry();
+  if (entry->IsInitialEntry()) {
+    entry = web_contents->GetController().GetVisibleEntry();
+  }
+  const GURL& url = entry->GetURL();
+  return NewTabUI::IsNewTab(url) || NewTabPageUI::IsNewTabPageOrigin(url) ||
+         NewTabPageThirdPartyUI::IsNewTabPageOrigin(url) ||
+         search::NavEntryIsInstantNTP(web_contents, entry);
+}
+
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1252,6 +1276,10 @@
   return window_->GetLensOverlayView();
 }
 
+new_tab_footer::NewTabFooterWebView* Browser::NewTabFooterWebView() {
+  return GetBrowserView().new_tab_footer_web_view();
+}
+
 base::CallbackListSubscription Browser::RegisterActiveTabDidChange(
     ActiveTabChangeCallback callback) {
   return did_active_tab_change_callback_list_.Add(std::move(callback));
@@ -3800,14 +3828,38 @@
     return true;
   }
 
-  WebContents* web_contents = tab_strip_model_->GetActiveWebContents();
+  content::WebContents* web_contents = tab_strip_model_->GetActiveWebContents();
   if (!web_contents) {
     return false;
   }
 
-  BookmarkTabHelper* bookmark_tab_helper =
-      BookmarkTabHelper::FromWebContents(web_contents);
-  return bookmark_tab_helper && bookmark_tab_helper->ShouldShowBookmarkBar();
+  if (SadTab::ShouldShow(web_contents->GetCrashedStatus())) {
+    return false;
+  }
+
+  if (!browser_defaults::bookmarks_enabled) {
+    return false;
+  }
+
+  PrefService* prefs = profile_->GetPrefs();
+  if (prefs->IsManagedPreference(bookmarks::prefs::kShowBookmarkBar) &&
+      !prefs->GetBoolean(bookmarks::prefs::kShowBookmarkBar)) {
+    return false;
+  }
+
+  bookmarks::BookmarkModel* bookmark_model =
+      BookmarkModelFactory::GetForBrowserContext(
+          web_contents->GetBrowserContext());
+  const bool has_bookmarks = bookmark_model && bookmark_model->HasBookmarks();
+
+  tab_groups::TabGroupSyncService* tab_group_service =
+      tab_groups::SavedTabGroupUtils::GetServiceForProfile(profile_);
+  const bool has_saved_tab_groups =
+      tab_group_service && !tab_group_service->GetAllGroups().empty();
+
+  // The bookmark bar is only shown on the NTP if the user
+  // has added something to it.
+  return IsNTP(web_contents) && (has_bookmarks || has_saved_tab_groups);
 }
 
 bool Browser::IsBrowserClosing() const {
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index ab7fb40..9841afe 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -130,6 +130,10 @@
 class View;
 }
 
+namespace new_tab_footer {
+class NewTabFooterWebView;
+}  // namespace new_tab_footer
+
 // This enum is not a member of `Browser` so that it can be forward
 // declared in `unload_controller.h` to avoid circular includes.
 enum class BrowserClosingStatus {
@@ -876,6 +880,7 @@
   bool IsVisible() const override;
   base::WeakPtr<BrowserWindowInterface> GetWeakPtr() override;
   views::View* LensOverlayView() override;
+  new_tab_footer::NewTabFooterWebView* NewTabFooterWebView() override;
   base::CallbackListSubscription RegisterActiveTabDidChange(
       ActiveTabChangeCallback callback) override;
   tabs::TabInterface* GetActiveTabInterface() override;
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index c5bb924a..5f848bb0 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -577,7 +577,6 @@
          host != chrome::kChromeUIHelpHost &&
          host != chrome::kChromeUIHistoryHost &&
          host != chrome::kChromeUIExtensionsHost &&
-         host != chrome::kChromeUIBookmarksHost &&
          host != password_manager::kChromeUIPasswordManagerHost;
 }
 
diff --git a/chrome/browser/ui/browser_navigator_browsertest.cc b/chrome/browser/ui/browser_navigator_browsertest.cc
index 8c32041..5198d05 100644
--- a/chrome/browser/ui/browser_navigator_browsertest.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest.cc
@@ -1432,22 +1432,32 @@
   RunDoNothingIfIncognitoIsForcedTest(GetSettingsURL());
 }
 
-// This test verifies that the bookmarks page isn't opened in the incognito
-// window.
+// This test verifies that the bookmarks page can open in incognito windows.
 IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
-                       Disposition_Bookmarks_UseNonIncognitoWindow) {
-  RunUseNonIncognitoWindowTest(
-      GURL(chrome::kChromeUIBookmarksURL),
-      ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK);
-}
+                       Disposition_Bookmarks_UseIncognitoWindow) {
+  Browser* const incognito_browser = CreateIncognitoBrowser();
+  TabStripModel* const incognito_tab_strip_model =
+      incognito_browser->tab_strip_model();
 
-// Bookmark manager is expected to always open in normal mode regardless
-// of whether the user is trying to open it in incognito mode or not.
-// This test verifies that if incognito mode is forced (by policy), bookmark
-// manager doesn't open at all.
-IN_PROC_BROWSER_TEST_F(BrowserNavigatorTest,
-                       Disposition_Bookmarks_DoNothingIfIncognitoIsForced) {
-  RunDoNothingIfIncognitoIsForcedTest(GURL(chrome::kChromeUIBookmarksURL));
+  EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
+  EXPECT_EQ(1, browser()->tab_strip_model()->count());
+  EXPECT_EQ(1, incognito_tab_strip_model->count());
+
+  // Navigate to the page.
+  const GURL bookmarks_page = GURL(chrome::kChromeUIBookmarksURL);
+  NavigateParams params(MakeNavigateParams(incognito_browser));
+  params.disposition = WindowOpenDisposition::SINGLETON_TAB;
+  params.url = bookmarks_page;
+  params.window_action = NavigateParams::SHOW_WINDOW;
+  params.transition = ui::PageTransition::PAGE_TRANSITION_AUTO_BOOKMARK;
+  Navigate(&params);
+
+  // This page should be opened in browser() window.
+  EXPECT_EQ(incognito_browser, params.browser);
+  EXPECT_NE(browser(), params.browser);
+  EXPECT_EQ(2, incognito_tab_strip_model->count());
+  EXPECT_EQ(bookmarks_page,
+            incognito_tab_strip_model->GetActiveWebContents()->GetURL());
 }
 
 // This test makes sure a crashed singleton tab reloads from a new navigation.
diff --git a/chrome/browser/ui/browser_window/public/browser_window_interface.h b/chrome/browser/ui/browser_window/public/browser_window_interface.h
index c45b360..457cde1 100644
--- a/chrome/browser/ui/browser_window/public/browser_window_interface.h
+++ b/chrome/browser/ui/browser_window/public/browser_window_interface.h
@@ -18,6 +18,10 @@
 // your feature needs. This comment will be deleted after there are 10+ features
 // in BrowserWindowFeatures.
 
+namespace new_tab_footer {
+class NewTabFooterWebView;
+}  // namespace new_tab_footer
+
 namespace tabs {
 class TabInterface;
 }  // namespace tabs
@@ -113,6 +117,9 @@
   // Returns the view that houses the Lens overlay.
   virtual views::View* LensOverlayView() = 0;
 
+  // Returns the view that houses the New Tab Footer.
+  virtual new_tab_footer::NewTabFooterWebView* NewTabFooterWebView() = 0;
+
   using ActiveTabChangeCallback =
       base::RepeatingCallback<void(BrowserWindowInterface*)>;
   virtual base::CallbackListSubscription RegisterActiveTabDidChange(
diff --git a/chrome/browser/ui/browser_window/test/mock_browser_window_interface.h b/chrome/browser/ui/browser_window/test/mock_browser_window_interface.h
index 1d222ff..ddb2088 100644
--- a/chrome/browser/ui/browser_window/test/mock_browser_window_interface.h
+++ b/chrome/browser/ui/browser_window/test/mock_browser_window_interface.h
@@ -37,6 +37,10 @@
               (),
               (override));
   MOCK_METHOD(views::View*, LensOverlayView, (), (override));
+  MOCK_METHOD(new_tab_footer::NewTabFooterWebView*,
+              NewTabFooterWebView,
+              (),
+              (override));
   MOCK_METHOD(base::CallbackListSubscription,
               RegisterActiveTabDidChange,
               (ActiveTabChangeCallback callback),
diff --git a/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.h b/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.h
index f4f9e78..67d86b8 100644
--- a/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.h
+++ b/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.h
@@ -27,7 +27,7 @@
 // True if the forward button is enabled.
 @property(nonatomic, assign) BOOL canGoForward;
 
-@property(nonatomic, assign) BrowserWindowTouchBarController* controller;
+@property(nonatomic, weak) BrowserWindowTouchBarController* controller;
 
 @property(nonatomic) Browser* browser;
 
diff --git a/chrome/browser/ui/color/new_tab_page_color_mixer.cc b/chrome/browser/ui/color/new_tab_page_color_mixer.cc
index 36f4f193..b3db7b6 100644
--- a/chrome/browser/ui/color/new_tab_page_color_mixer.cc
+++ b/chrome/browser/ui/color/new_tab_page_color_mixer.cc
@@ -382,6 +382,10 @@
     mixer[kColorNewTabPageText] = {dark_mode ? gfx::kGoogleGrey200
                                              : SK_ColorBLACK};
   }
+
+  /* NewTabFooter */
+  mixer[kColorNewTabFooterBackground] = {kColorToolbar};
+  mixer[kColorNewTabFooterText] = {kColorToolbarText};
 }
 
 void AddWebThemeNewTabPageColors(ui::ColorMixer& mixer, bool dark_mode) {
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 f08b785..14bacd2 100644
--- a/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
+++ b/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
@@ -74,7 +74,7 @@
   MOCK_METHOD(void,
               OnCookieControlsIconStatusChanged,
               (/*icon_visible*/ bool,
-               /*protections_on*/ bool,
+               CookieControlsState,
                CookieBlocking3pcdStatus,
                /*should_highlight*/ bool));
   MOCK_METHOD(void, OnFinishedPageReloadWithChangedSettings, ());
@@ -286,18 +286,20 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Accessing cookies should be notified.
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   page_specific_content_settings()->OnBrowsingDataAccessed(
       CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
       BrowsingDataModel::StorageType::kQuotaStorage,
@@ -313,10 +315,11 @@
                            CookieControlsState::k3pcsAllowed,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
   t.ExpectUniqueSample(kCookieControlsActivatedSaaHistogram, false, 1);
   t.ExpectUniqueSample(kCookieControlsActivatedRefreshCountHistogram, 0, 1);
@@ -333,7 +336,7 @@
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/false,
+                           /*icon_visible=*/false, CookieControlsState::kHidden,
                            CookieBlocking3pcdStatus::kNotIn3pcd,
                            /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -346,10 +349,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -360,7 +364,7 @@
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/false,
+                           /*icon_visible=*/false, CookieControlsState::kHidden,
                            CookieBlocking3pcdStatus::kNotIn3pcd,
                            /*should_highlight=*/false));
   profile()->GetPrefs()->SetInteger(
@@ -376,10 +380,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -389,10 +394,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_settings()->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -401,10 +407,11 @@
                            CookieControlsState::k3pcsAllowed,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
   t.ExpectUniqueSample(kCookieControlsActivatedSaaHistogram, false, 1);
   t.ExpectUniqueSample(kCookieControlsActivatedRefreshCountHistogram, 0, 1);
@@ -422,10 +429,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -434,10 +442,11 @@
                            CookieControlsState::k3pcsAllowed,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -448,10 +457,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -461,10 +471,11 @@
                            CookieControlsState::k3pcsAllowed,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -474,10 +485,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(true);
   testing::Mock::VerifyAndClearExpectations(mock());
 }
@@ -490,10 +502,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -526,7 +539,7 @@
   EXPECT_CALL(
       incognito_mock,
       OnCookieControlsIconStatusChanged(
-          /*icon_visible=*/false, /*protections_on=*/true,
+          /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
           CookieBlocking3pcdStatus::kNotIn3pcd, /*should_highlight=*/false));
   incognito_cookie_controls.Update(incognito_web_contents.get());
   testing::Mock::VerifyAndClearExpectations(mock());
@@ -538,10 +551,11 @@
                            CookieControlsState::k3pcsAllowed,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
 
   EXPECT_CALL(
       incognito_mock,
@@ -551,7 +565,7 @@
   EXPECT_CALL(
       incognito_mock,
       OnCookieControlsIconStatusChanged(
-          /*icon_visible=*/true, /*protections_on=*/false,
+          /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
           CookieBlocking3pcdStatus::kNotIn3pcd, /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
   testing::Mock::VerifyAndClearExpectations(mock());
@@ -568,7 +582,7 @@
                               // expires, zero_expiration is correct.
                               zero_expiration()));
   EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/false,
+                           /*icon_visible=*/false, CookieControlsState::kHidden,
                            CookieBlocking3pcdStatus::kNotIn3pcd,
                            /*should_highlight=*/false));
 
@@ -580,7 +594,7 @@
   EXPECT_CALL(
       incognito_mock,
       OnCookieControlsIconStatusChanged(
-          /*icon_visible=*/true, /*protections_on=*/false,
+          /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
           CookieBlocking3pcdStatus::kNotIn3pcd, /*should_highlight=*/false));
   profile()->GetPrefs()->SetInteger(
       prefs::kCookieControlsMode,
@@ -603,10 +617,11 @@
       OnStatusChanged(CookieControlsState::k3pcsAllowed,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -616,10 +631,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(true);
   testing::Mock::VerifyAndClearExpectations(mock());
 }
@@ -634,19 +650,21 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Accessing cookies should be notified.
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   page_specific_content_settings()->OnBrowsingDataAccessed(
 
       CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
@@ -655,10 +673,11 @@
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Reload the page and simulate accessing storage on page load.
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
   page_specific_content_settings()->OnBrowsingDataAccessed(
       CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
@@ -667,10 +686,11 @@
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // After the second reload and accessing storage, UB should highlight.
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/true));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/true));
 
   NavigateAndCommit(GURL(kUrl));
   page_specific_content_settings()->OnBrowsingDataAccessed(
@@ -700,10 +720,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
@@ -711,10 +732,11 @@
   // Don't call observer when reload count = 1.
   EXPECT_CALL(*mock(), OnReloadThresholdExceeded()).Times(0);
 
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
@@ -722,10 +744,11 @@
   // Expect observer call when reload count hits threshold of 2.
   EXPECT_CALL(*mock(), OnReloadThresholdExceeded());
   // Expect that we attempt to highlight the user bypass icon.
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/true));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/true));
   NavigateAndCommit(GURL(kUrl));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
@@ -747,7 +770,7 @@
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/false,
+                           /*icon_visible=*/false, CookieControlsState::kHidden,
                            CookieBlocking3pcdStatus::kNotIn3pcd,
                            /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
@@ -755,7 +778,7 @@
   testing::Mock::VerifyAndClearExpectations(mock());
   // Trigger reload heuristic.
   EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/false,
+                           /*icon_visible=*/false, CookieControlsState::kHidden,
                            CookieBlocking3pcdStatus::kNotIn3pcd,
                            /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
@@ -764,7 +787,7 @@
 
   // Verify we do not attempt to highlight user bypass as 3PCs are allowed.
   EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/false,
+                           /*icon_visible=*/false, CookieControlsState::kHidden,
                            CookieBlocking3pcdStatus::kNotIn3pcd,
                            /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
@@ -782,18 +805,20 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Accessing cookies should be notified.
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   page_specific_content_settings()->OnBrowsingDataAccessed(
       CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
       BrowsingDataModel::StorageType::kQuotaStorage,
@@ -801,10 +826,11 @@
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Reload the page and simulate accessing storage on page load.
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
   page_specific_content_settings()->OnBrowsingDataAccessed(
       CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
@@ -813,10 +839,11 @@
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // After the second reload and accessing storage, UB should be highlighted.
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/true));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/true));
   NavigateAndCommit(GURL(kUrl));
   page_specific_content_settings()->OnBrowsingDataAccessed(
       CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
@@ -829,10 +856,11 @@
                            CookieControlsState::k3pcsAllowed,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
   t.ExpectUniqueSample(kCookieControlsActivatedSaaHistogram, false, 1);
   t.ExpectUniqueSample(kCookieControlsActivatedRefreshCountHistogram, 2, 1);
@@ -857,18 +885,20 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Accessing cookies should be notified.
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   page_specific_content_settings()->OnBrowsingDataAccessed(
 
       CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
@@ -877,10 +907,11 @@
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Reload the page and simulate accessing storage on page load.
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
   page_specific_content_settings()->OnBrowsingDataAccessed(
       CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
@@ -892,10 +923,11 @@
   FastForwardBy(base::Seconds(30));
 
   // The second reload happens with a delay and doesn't highlight.
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
   page_specific_content_settings()->OnBrowsingDataAccessed(
       CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
@@ -908,10 +940,11 @@
                            CookieControlsState::k3pcsAllowed,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
   t.ExpectUniqueSample(kCookieControlsActivatedSaaHistogram, false, 1);
   t.ExpectUniqueSample(kCookieControlsActivatedRefreshCountHistogram, 1, 1);
@@ -947,18 +980,20 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/true));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/true));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Site data access should highlight.
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/true));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/true));
   page_specific_content_settings()->OnBrowsingDataAccessed(
 
       CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
@@ -981,19 +1016,21 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Site with medium or low engagement index that has accessed site does not
   // highlight UB and only shows the icon.
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   page_specific_content_settings()->OnBrowsingDataAccessed(
 
       CreateUnpartitionedStorageKey(GURL("https://anotherthirdparty.com")),
@@ -1003,10 +1040,11 @@
 
   // Revisiting high site engagement site doesn't highlight UB
   // because the entry point was already highlighted for that site.
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   NavigateAndCommit(GURL("https://highengagement.com"));
   page_specific_content_settings()->OnBrowsingDataAccessed(
       CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
@@ -1037,19 +1075,21 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
   // Even though the site has high engagement level, UB does not highlight
   // because SAA was requested in the site context.
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   page_specific_content_settings()->OnBrowsingDataAccessed(
 
       CreateUnpartitionedStorageKey(GURL("https://thirdparty.com")),
@@ -1062,10 +1102,11 @@
                            CookieControlsState::k3pcsAllowed,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
   t.ExpectUniqueSample(kCookieControlsActivatedSaaHistogram, true, 1);
   t.ExpectUniqueSample(kCookieControlsActivatedRefreshCountHistogram, 0, 1);
@@ -1092,10 +1133,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -1106,10 +1148,11 @@
       OnStatusChanged(CookieControlsState::k3pcsAllowed,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   hcsm->SetContentSettingCustomScope(
       ContentSettingsPattern::Wildcard(),
       ContentSettingsPattern::FromString("cool.things.com"),
@@ -1126,10 +1169,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -1140,10 +1184,11 @@
       OnStatusChanged(CookieControlsState::k3pcsAllowed,
                       CookieControlsEnforcement::kEnforcedByCookieSetting,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   hcsm->SetContentSettingCustomScope(
       ContentSettingsPattern::Wildcard(),
       ContentSettingsPattern::FromString("[*.]cool.things.com"),
@@ -1161,10 +1206,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -1177,10 +1223,11 @@
                       CookieControlsEnforcement::kEnforcedByCookieSetting,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
 
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   hcsm->SetContentSettingCustomScope(
       ContentSettingsPattern::Wildcard(),
       ContentSettingsPattern::FromString("[*.]things.com"),
@@ -1197,10 +1244,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/false, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -1212,10 +1260,11 @@
       OnStatusChanged(CookieControlsState::k3pcsAllowed,
                       CookieControlsEnforcement::kEnforcedByCookieSetting,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   hcsm->SetContentSettingCustomScope(
       ContentSettingsPattern::Wildcard(),
       ContentSettingsPattern::FromString("[*.]com"),
@@ -1318,10 +1367,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -1330,10 +1380,11 @@
                            CookieControlsState::k3pcsAllowed,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
   ValidateCookieControlsActivatedUKM(
       /*fed_cm_initiated=*/false,
@@ -1359,10 +1410,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/true));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/true));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
@@ -1378,10 +1430,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 }
@@ -1395,10 +1448,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
 }
 
@@ -1422,10 +1476,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
 }
 
@@ -1449,10 +1504,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
 }
 
@@ -1475,10 +1531,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
 }
 
@@ -1491,10 +1548,11 @@
   navigation->SetResponseHeaders(headers);
   navigation->Start();
   navigation->Commit();
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 }
@@ -1596,10 +1654,11 @@
       OnStatusChanged(CookieControlsState::k3pcsAllowed,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
 
   incognito_cookie_controls()->OnCookieBlockingEnabledForSite(false);
   testing::Mock::VerifyAndClearExpectations(mock());
@@ -1624,10 +1683,11 @@
       OnStatusChanged(CookieControlsState::k3pcsBlocked,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true, /*protections_on=*/true,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
 
   incognito_cookie_controls()->Update(web_contents());
 }
@@ -1789,11 +1849,11 @@
                            CookieControlsState::k3pcsAllowed,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
-  EXPECT_CALL(*mock(), OnCookieControlsIconStatusChanged(
-                           /*icon_visible=*/true,
-                           /*protections_on=*/false,
-                           CookieBlocking3pcdStatus::kNotIn3pcd,
-                           /*should_highlight=*/false));
+  EXPECT_CALL(*mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
 
   EXPECT_CALL(
       *incognito_mock(),
@@ -1806,7 +1866,7 @@
                   /*icon_visible=*/
                   !protections_on,  // Icon shown when
                                     // protections are off
-                  protections_on, CookieBlocking3pcdStatus::kNotIn3pcd,
+                  std::get<0>(GetParam()), CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
   testing::Mock::VerifyAndClearExpectations(mock());
@@ -1851,11 +1911,11 @@
       OnStatusChanged(std::get<0>(GetParam()),
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
-  EXPECT_CALL(*incognito_mock(), OnCookieControlsIconStatusChanged(
-                                     /*icon_visible=*/true,
-                                     /*protections_on=*/protections_on,
-                                     CookieBlocking3pcdStatus::kNotIn3pcd,
-                                     /*should_highlight=*/false));
+  EXPECT_CALL(*incognito_mock(),
+              OnCookieControlsIconStatusChanged(
+                  /*icon_visible=*/true, std::get<0>(GetParam()),
+                  CookieBlocking3pcdStatus::kNotIn3pcd,
+                  /*should_highlight=*/false));
 
   incognito_cookie_controls()->OnCookieBlockingEnabledForSite(protections_on);
 
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 4384aae..96d7d60 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -331,7 +331,7 @@
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
 
   // --- Section 1: Common tab helpers ---
-  if (page_info::IsAboutThisSiteAsyncFetchingEnabled()) {
+  if (page_info::IsAboutThisSiteFeatureEnabled()) {
     if (auto* optimization_guide_decider =
             OptimizationGuideKeyedServiceFactory::GetForProfile(profile)) {
       AboutThisSiteTabHelper::CreateForWebContents(web_contents,
diff --git a/chrome/browser/ui/tabs/BUILD.gn b/chrome/browser/ui/tabs/BUILD.gn
index b901805..74e3e358 100644
--- a/chrome/browser/ui/tabs/BUILD.gn
+++ b/chrome/browser/ui/tabs/BUILD.gn
@@ -253,7 +253,6 @@
       "features.h",
       "hover_tab_selector.h",
       "pinned_tab_codec.h",
-      "pinned_tab_collection.h",
       "pinned_tab_service.h",
       "pinned_tab_service_factory.h",
       "split_tab_collection.h",
@@ -307,7 +306,6 @@
       "features.cc",
       "hover_tab_selector.cc",
       "pinned_tab_codec.cc",
-      "pinned_tab_collection.cc",
       "pinned_tab_service.cc",
       "pinned_tab_service_factory.cc",
       "recent_tabs_sub_menu_model.cc",
@@ -356,6 +354,7 @@
       "//chrome/browser/ui/commerce",
       "//chrome/browser/ui/lens",
       "//chrome/browser/ui/views/intent_picker:intent_picker_page_action",
+      "//chrome/browser/ui/views/new_tab_footer",
       "//chrome/browser/ui/views/page_action",
       "//chrome/browser/ui/views/side_panel",
       "//chrome/browser/ui/web_applications:pwa_install_page_action",
@@ -375,6 +374,7 @@
       "//components/performance_manager",
       "//components/permissions",
       "//components/pref_registry",
+      "//components/search",
       "//components/security_interstitials/content:security_interstitial_page",
       "//components/sessions",
       "//components/sync_sessions",
diff --git a/chrome/browser/ui/tabs/features.cc b/chrome/browser/ui/tabs/features.cc
index 4ac467fa..1ea0ada 100644
--- a/chrome/browser/ui/tabs/features.cc
+++ b/chrome/browser/ui/tabs/features.cc
@@ -36,6 +36,8 @@
              base::FEATURE_DISABLED_BY_DEFAULT);
 const char kScrollableTabStripOverflowModeName[] = "tabScrollOverflow";
 
+BASE_FEATURE(kTabGroupHome, "TabGroupHome", base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kTabSearchPositionSetting,
              "TabSearchPositionSetting",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/ui/tabs/features.h b/chrome/browser/ui/tabs/features.h
index fbc2126..475a46a 100644
--- a/chrome/browser/ui/tabs/features.h
+++ b/chrome/browser/ui/tabs/features.h
@@ -19,6 +19,8 @@
 BASE_DECLARE_FEATURE(kScrollableTabStripWithDragging);
 extern const char kTabScrollingWithDraggingModeName[];
 
+BASE_DECLARE_FEATURE(kTabGroupHome);
+
 BASE_DECLARE_FEATURE(kScrollableTabStripOverflow);
 extern const char kScrollableTabStripOverflowModeName[];
 
diff --git a/chrome/browser/ui/tabs/public/tab_features.h b/chrome/browser/ui/tabs/public/tab_features.h
index 601f3e1..313a7903 100644
--- a/chrome/browser/ui/tabs/public/tab_features.h
+++ b/chrome/browser/ui/tabs/public/tab_features.h
@@ -98,6 +98,10 @@
 class CollaborationMessagingTabData;
 }  // namespace tab_groups
 
+namespace new_tab_footer {
+class NewTabFooterController;
+}  // namespace new_tab_footer
+
 namespace tabs {
 
 class TabInterface;
@@ -217,6 +221,10 @@
     return inactive_window_mouse_event_controller_.get();
   }
 
+  new_tab_footer::NewTabFooterController* new_tab_footer_controller() {
+    return new_tab_footer_controller_.get();
+  }
+
 #if BUILDFLAG(ENABLE_GLIC)
   glic::GlicPageContextEligibilityObserver*
   glic_page_context_eligibility_observer() {
@@ -352,6 +360,9 @@
   std::unique_ptr<FromGWSNavigationAndKeepAliveRequestObserver>
       from_gws_navigation_and_keep_alive_request_observer_;
 
+  std::unique_ptr<new_tab_footer::NewTabFooterController>
+      new_tab_footer_controller_;
+
   // Must be the last member.
   base::WeakPtrFactory<TabFeatures> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/tabs/tab_collection_storage_unittest.cc b/chrome/browser/ui/tabs/tab_collection_storage_unittest.cc
index 7e8af38..abf6337 100644
--- a/chrome/browser/ui/tabs/tab_collection_storage_unittest.cc
+++ b/chrome/browser/ui/tabs/tab_collection_storage_unittest.cc
@@ -8,7 +8,6 @@
 
 #include "base/test/gtest_util.h"
 #include "chrome/browser/ui/tabs/features.h"
-#include "chrome/browser/ui/tabs/pinned_tab_collection.h"
 #include "chrome/browser/ui/tabs/tab_group_tab_collection.h"
 #include "chrome/browser/ui/tabs/tab_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -18,6 +17,7 @@
 #include "chrome/browser/ui/tabs/unpinned_tab_collection.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/tabs/public/pinned_tab_collection.h"
 #include "components/tabs/public/tab_collection.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_renderer_host.h"
diff --git a/chrome/browser/ui/tabs/tab_collection_unittest.cc b/chrome/browser/ui/tabs/tab_collection_unittest.cc
index a5550943..4704755 100644
--- a/chrome/browser/ui/tabs/tab_collection_unittest.cc
+++ b/chrome/browser/ui/tabs/tab_collection_unittest.cc
@@ -9,7 +9,6 @@
 #include <optional>
 
 #include "chrome/browser/ui/tabs/features.h"
-#include "chrome/browser/ui/tabs/pinned_tab_collection.h"
 #include "chrome/browser/ui/tabs/split_tab_collection.h"
 #include "chrome/browser/ui/tabs/split_tab_data.h"
 #include "chrome/browser/ui/tabs/split_tab_visual_data.h"
@@ -23,6 +22,7 @@
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/tab_groups/tab_group_visual_data.h"
+#include "components/tabs/public/pinned_tab_collection.h"
 #include "components/tabs/public/split_tab_id.h"
 #include "components/tabs/public/tab_collection_storage.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/ui/tabs/tab_features.cc b/chrome/browser/ui/tabs/tab_features.cc
index 96991351..8fc18dd0 100644
--- a/chrome/browser/ui/tabs/tab_features.cc
+++ b/chrome/browser/ui/tabs/tab_features.cc
@@ -51,6 +51,7 @@
 #include "chrome/browser/ui/views/commerce/price_insights_page_action_view_controller.h"
 #include "chrome/browser/ui/views/file_system_access/file_system_access_page_action_controller.h"
 #include "chrome/browser/ui/views/intent_picker/intent_picker_view_page_action_controller.h"
+#include "chrome/browser/ui/views/new_tab_footer/footer_controller.h"
 #include "chrome/browser/ui/views/page_action/action_ids.h"
 #include "chrome/browser/ui/views/page_action/page_action_controller.h"
 #include "chrome/browser/ui/views/page_action/page_action_properties_provider.h"
@@ -73,6 +74,7 @@
 #include "components/metrics/content/dwa_web_contents_observer.h"
 #include "components/passage_embeddings/passage_embeddings_features.h"
 #include "components/permissions/permission_indicators_tab_data.h"
+#include "components/search/ntp_features.h"
 #include "components/tabs/public/tab_interface.h"
 #include "net/base/features.h"
 
@@ -202,6 +204,11 @@
               tab.GetContents());
     }
 #endif  // BUILDFLAG(ENABLE_GLIC)
+
+    if (base::FeatureList::IsEnabled(ntp_features::kNtpFooter)) {
+      new_tab_footer_controller_ =
+          std::make_unique<new_tab_footer::NewTabFooterController>(&tab);
+    }
   }     // IsInNormalWindow() end.
 
   if (base::FeatureList::IsEnabled(features::kPageActionsMigration)) {
diff --git a/chrome/browser/ui/tabs/tab_group_home/BUILD.gn b/chrome/browser/ui/tabs/tab_group_home/BUILD.gn
new file mode 100644
index 0000000..8152873
--- /dev/null
+++ b/chrome/browser/ui/tabs/tab_group_home/BUILD.gn
@@ -0,0 +1,9 @@
+# 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.
+
+source_set("constants") {
+  public = [ "constants.h" ]
+  sources = [ "constants.cc" ]
+  deps = [ "//base" ]
+}
diff --git a/chrome/browser/ui/tabs/tab_group_home/OWNERS b/chrome/browser/ui/tabs/tab_group_home/OWNERS
new file mode 100644
index 0000000..0268142
--- /dev/null
+++ b/chrome/browser/ui/tabs/tab_group_home/OWNERS
@@ -0,0 +1,5 @@
+aymana@chromium.org
+chazzy@chromium.org
+mdjones@chromium.org
+meiliang@chromium.org
+file://chrome/browser/ui/tabs/OWNERS
diff --git a/chrome/browser/ui/tabs/tab_group_home/constants.cc b/chrome/browser/ui/tabs/tab_group_home/constants.cc
new file mode 100644
index 0000000..671172c
--- /dev/null
+++ b/chrome/browser/ui/tabs/tab_group_home/constants.cc
@@ -0,0 +1,14 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/tabs/tab_group_home/constants.h"
+
+namespace tabs {
+
+namespace flag_descriptions {
+const char kTabGroupHomeName[] = "Tab Group Home";
+const char kTabGroupHomeDescription[] = "Home surface for a tab group.";
+}  // namespace flag_descriptions
+
+}  // namespace tabs
diff --git a/chrome/browser/ui/tabs/tab_group_home/constants.h b/chrome/browser/ui/tabs/tab_group_home/constants.h
new file mode 100644
index 0000000..e39f7732
--- /dev/null
+++ b/chrome/browser/ui/tabs/tab_group_home/constants.h
@@ -0,0 +1,19 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_TABS_TAB_GROUP_HOME_CONSTANTS_H_
+#define CHROME_BROWSER_UI_TABS_TAB_GROUP_HOME_CONSTANTS_H_
+
+#include "base/feature_list.h"
+
+namespace tabs {
+
+namespace flag_descriptions {
+extern const char kTabGroupHomeName[];
+extern const char kTabGroupHomeDescription[];
+}  // namespace flag_descriptions
+
+}  // namespace tabs
+
+#endif  // CHROME_BROWSER_UI_TABS_TAB_GROUP_HOME_CONSTANTS_H_
diff --git a/chrome/browser/ui/tabs/tab_strip_collection.cc b/chrome/browser/ui/tabs/tab_strip_collection.cc
index 4faa2356..bad3e448 100644
--- a/chrome/browser/ui/tabs/tab_strip_collection.cc
+++ b/chrome/browser/ui/tabs/tab_strip_collection.cc
@@ -11,11 +11,11 @@
 
 #include "base/containers/adapters.h"
 #include "base/memory/ptr_util.h"
-#include "chrome/browser/ui/tabs/pinned_tab_collection.h"
 #include "chrome/browser/ui/tabs/split_tab_collection.h"
 #include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_group_tab_collection.h"
 #include "chrome/browser/ui/tabs/unpinned_tab_collection.h"
+#include "components/tabs/public/pinned_tab_collection.h"
 #include "components/tabs/public/split_tab_id.h"
 #include "components/tabs/public/tab_collection.h"
 #include "components/tabs/public/tab_collection_storage.h"
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 8ff53e7e..baa62c6 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -8,6 +8,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "components/search/ntp_features.h"
 #include "components/webui/flags/feature_entry.h"
 #include "ui/base/ui_base_features.h"
 
@@ -132,6 +133,10 @@
              base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kSideBySide, "SideBySide", base::FEATURE_DISABLED_BY_DEFAULT);
+bool IsNtpFooterEnabledWithoutSideBySide() {
+  return (base::FeatureList::IsEnabled(ntp_features::kNtpFooter) &&
+          !base::FeatureList::IsEnabled(features::kSideBySide));
+}
 
 BASE_FEATURE(kSidePanelResizing,
              "SidePanelResizing",
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index f3ed5ac..b94f5cb 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -102,6 +102,7 @@
 BASE_DECLARE_FEATURE(KScrimForTabModal);
 
 BASE_DECLARE_FEATURE(kSideBySide);
+bool IsNtpFooterEnabledWithoutSideBySide();
 
 BASE_DECLARE_FEATURE(kTabDuplicateMetrics);
 
diff --git a/chrome/browser/ui/views/autofill/popup/popup_base_view.cc b/chrome/browser/ui/views/autofill/popup/popup_base_view.cc
index 7f40c18..c253e24 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_base_view.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_base_view.cc
@@ -78,7 +78,8 @@
   auto border = std::make_unique<views::BubbleBorder>(
       views::BubbleBorder::NONE, views::BubbleBorder::STANDARD_SHADOW);
   border->SetColor(ui::kColorDropdownBackground);
-  border->SetCornerRadius(PopupBaseView::GetCornerRadius());
+  border->set_rounded_corners(
+      gfx::RoundedCornersF(PopupBaseView::GetCornerRadius()));
   border->set_md_shadow_elevation(
       ChromeLayoutProvider::Get()->GetShadowElevationMetric(
           base::FeatureList::IsEnabled(features::kAutofillMoreProminentPopup)
diff --git a/chrome/browser/ui/views/extensions/extension_popup.cc b/chrome/browser/ui/views/extensions/extension_popup.cc
index 864d4a3..ac706b84 100644
--- a/chrome/browser/ui/views/extensions/extension_popup.cc
+++ b/chrome/browser/ui/views/extensions/extension_popup.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/extensions/extension_popup.h"
 
+#include "base/check.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/devtools/devtools_window.h"
@@ -127,11 +128,16 @@
 
 void ExtensionPopup::AddedToWidget() {
   BubbleDialogDelegateView::AddedToWidget();
-  const int radius = GetBubbleFrameView()->GetCornerRadius();
+
+  const gfx::RoundedCornersF& radii = GetBubbleFrameView()->GetRoundedCorners();
+  CHECK_EQ(radii.upper_left(), radii.upper_right());
+  CHECK_EQ(radii.lower_left(), radii.lower_right());
+
   const bool contents_has_rounded_corners =
-      extension_view_->holder()->SetCornerRadii(gfx::RoundedCornersF(radius));
-  SetBorder(views::CreateEmptyBorder(
-      gfx::Insets::VH(contents_has_rounded_corners ? 0 : radius, 0)));
+      extension_view_->holder()->SetCornerRadii(radii);
+  SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(
+      contents_has_rounded_corners ? 0 : radii.upper_left(), 0,
+      contents_has_rounded_corners ? 0 : radii.lower_left(), 0)));
 }
 
 void ExtensionPopup::OnWidgetDestroying(views::Widget* widget) {
diff --git a/chrome/browser/ui/views/find_bar_view.cc b/chrome/browser/ui/views/find_bar_view.cc
index 2f5a4af..5ac5c142 100644
--- a/chrome/browser/ui/views/find_bar_view.cc
+++ b/chrome/browser/ui/views/find_bar_view.cc
@@ -294,7 +294,7 @@
     border->SetColor(kColorFindBarBackground);
     border->set_md_shadow_elevation(
         layout_provider->GetCornerRadiusMetric(views::Emphasis::kHigh));
-    border->SetCornerRadius(corner_radius);
+    border->set_rounded_corners(gfx::RoundedCornersF(corner_radius));
 
     SetBackground(std::make_unique<views::BubbleBackground>(border.get()));
     SetBorder(std::move(border));
diff --git a/chrome/browser/ui/views/flying_indicator.cc b/chrome/browser/ui/views/flying_indicator.cc
index f48844fb4..a56e929 100644
--- a/chrome/browser/ui/views/flying_indicator.cc
+++ b/chrome/browser/ui/views/flying_indicator.cc
@@ -12,6 +12,7 @@
 #include "ui/base/mojom/dialog_button.mojom.h"
 #include "ui/color/color_provider.h"
 #include "ui/gfx/geometry/cubic_bezier.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/bubble/bubble_frame_view.h"
@@ -92,7 +93,7 @@
   views::BubbleFrameView* const frame_view =
       bubble_view_ptr->GetBubbleFrameView();
   frame_view->set_hit_test_transparent(true);
-  frame_view->SetCornerRadius(kBubbleCornerRadius);
+  frame_view->SetRoundedCorners(gfx::RoundedCornersF(kBubbleCornerRadius));
   widget_->SetZOrderLevel(ui::ZOrderLevel::kFloatingUIElement);
 
   // Set up the initial position and opacity, store the desired size, and start
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 301c35f..535891e 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -1021,14 +1021,11 @@
   devtools_web_view->SetVisible(false);
 
   std::unique_ptr<new_tab_footer::NewTabFooterWebView> new_tab_footer_web_view;
-  if (base::FeatureList::IsEnabled(ntp_features::kNtpFooter) &&
-      !base::FeatureList::IsEnabled(features::kSideBySide)) {
+  if (features::IsNtpFooterEnabledWithoutSideBySide()) {
     new_tab_footer_web_view =
         std::make_unique<new_tab_footer::NewTabFooterWebView>(
             browser_->profile());
-    // TODO(crbug.com/409056427): Set this elsewhere so the footer is only
-    // visible when the new tab page is shown.
-    new_tab_footer_web_view->SetVisible(true);
+    new_tab_footer_web_view->SetVisible(false);
   }
 
   auto contents_container = std::make_unique<views::View>();
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index bb41513e..fa2306f 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -309,6 +309,10 @@
 
   ScrimView* window_scrim_view() { return window_scrim_view_; }
 
+  new_tab_footer::NewTabFooterWebView* new_tab_footer_web_view() const {
+    return new_tab_footer_web_view_;
+  }
+
   base::WeakPtr<BrowserView> GetAsWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
   }
diff --git a/chrome/browser/ui/views/frame/desktop_browser_frame_aura_linux_browsertest.cc b/chrome/browser/ui/views/frame/desktop_browser_frame_aura_linux_browsertest.cc
index f560e7c9..0e6248c5 100644
--- a/chrome/browser/ui/views/frame/desktop_browser_frame_aura_linux_browsertest.cc
+++ b/chrome/browser/ui/views/frame/desktop_browser_frame_aura_linux_browsertest.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/interactive_test_utils.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_test.h"
 #include "ui/base/mojom/window_show_state.mojom.h"
@@ -115,6 +116,8 @@
 // be the same as the already existing window has.
 // The regression was found in https://crbug.com/1287212.
 IN_PROC_BROWSER_TEST_F(DesktopBrowserFrameAuraLinuxTest, NewWindowSize) {
+  // Ensure the first window is active before creating the second one.
+  ui_test_utils::BrowserActivationWaiter(browser()).WaitForActivation();
   Profile* profile = browser()->profile();
   Browser::CreateParams params(profile, true /* user_gesture */);
   Browser* browser2 = Browser::Create(params);
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc b/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
index a05269c..50f25677 100644
--- a/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
@@ -257,7 +257,7 @@
       views::Emphasis::kHigh);
   views::BubbleFrameView* frame = GetBubbleFrameView();
   if (frame) {
-    frame->SetCornerRadius(corner_radius);
+    frame->SetRoundedCorners(gfx::RoundedCornersF(corner_radius));
   }
   if (entry_point_ ==
       global_media_controls::GlobalMediaControlsEntryPoint::kPresentation) {
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 ba8f145..f69e48e1 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
@@ -23,6 +23,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/content_settings/browser/ui/cookie_controls_controller.h"
 #include "components/content_settings/core/common/cookie_blocking_3pcd_status.h"
+#include "components/content_settings/core/common/cookie_controls_state.h"
 #include "components/feature_engagement/public/event_constants.h"
 #include "components/omnibox/browser/vector_icons.h"
 #include "components/privacy_sandbox/privacy_sandbox_features.h"
@@ -39,11 +40,11 @@
 
 namespace {
 
-void RecordOpenedAction(bool icon_visible, bool protections_on) {
+void RecordOpenedAction(bool icon_visible, CookieControlsState controls_state) {
   if (!icon_visible) {
     base::RecordAction(
         base::UserMetricsAction("CookieControls.Bubble.UnknownState.Opened"));
-  } else if (protections_on) {
+  } else if (controls_state == CookieControlsState::k3pcsBlocked) {
     base::RecordAction(
         base::UserMetricsAction("CookieControls.Bubble.CookiesBlocked.Opened"));
   } else {
@@ -183,7 +184,7 @@
 }
 
 int CookieControlsIconView::GetLabelForStatus() const {
-  if (!protections_on_) {
+  if (controls_state_ == CookieControlsState::k3pcsAllowed) {
     return IDS_COOKIE_CONTROLS_PAGE_ACTION_COOKIES_ALLOWED_LABEL;
   } else if (blocking_status_ == CookieBlocking3pcdStatus::kLimited) {
     return IDS_COOKIE_CONTROLS_PAGE_ACTION_COOKIES_LIMITED_LABEL;
@@ -194,14 +195,14 @@
 
 void CookieControlsIconView::OnCookieControlsIconStatusChanged(
     bool icon_visible,
-    bool protections_on,
+    CookieControlsState controls_state,
     CookieBlocking3pcdStatus blocking_status,
     bool should_highlight) {
-  if (icon_visible != icon_visible_ || protections_on != protections_on_ ||
+  if (icon_visible != icon_visible_ || controls_state != controls_state_ ||
       blocking_status != blocking_status_ || should_highlight_) {
     icon_visible_ = icon_visible;
-    protections_changed_ = protections_on != protections_on_;
-    protections_on_ = protections_on;
+    protections_changed_ = controls_state != controls_state_;
+    controls_state_ = controls_state;
     blocking_status_ = blocking_status;
     should_highlight_ = should_highlight;
     if (!bubble_coordinator_->IsReloadingState()) {
@@ -249,7 +250,8 @@
   custom_tooltip_text_ = l10n_util::GetStringUTF16(GetLabelForStatus());
   SetTooltipText(custom_tooltip_text_);
 
-  if (protections_on_ && should_highlight_) {
+  if (controls_state_ == CookieControlsState::k3pcsBlocked &&
+      should_highlight_) {
     if (blocking_status_ == CookieBlocking3pcdStatus::kNotIn3pcd) {
       MaybeShowIPH();
     } else {
@@ -311,7 +313,7 @@
       browser_->GetBrowserView().toolbar_button_provider(),
       delegate()->GetWebContentsForPageActionIconView(), controller_.get());
   CHECK(ShouldBeVisible());
-  RecordOpenedAction(icon_visible_, protections_on_);
+  RecordOpenedAction(icon_visible_, controls_state_);
   if (did_animate_) {
     base::RecordAction(base::UserMetricsAction(
         "TrackingProtection.UserBypass.Animated.Opened"));
@@ -331,8 +333,9 @@
 }
 
 const gfx::VectorIcon& CookieControlsIconView::GetVectorIcon() const {
-  return protections_on_ ? views::kEyeCrossedRefreshIcon
-                         : views::kEyeRefreshIcon;
+  return controls_state_ == CookieControlsState::k3pcsBlocked
+             ? views::kEyeCrossedRefreshIcon
+             : views::kEyeRefreshIcon;
 }
 
 void CookieControlsIconView::UpdateTooltipForFocus() {}
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view.h b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view.h
index 3ce200e..b52f428 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view.h
+++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view.h
@@ -35,7 +35,7 @@
   // CookieControlsObserver:
   void OnCookieControlsIconStatusChanged(
       bool icon_visible,
-      bool protections_on,
+      CookieControlsState controls_state,
       CookieBlocking3pcdStatus blocking_status,
       bool should_highlight) override;
   void OnFinishedPageReloadWithChangedSettings() override;
@@ -81,7 +81,7 @@
   void SetLabelForStatus();
 
   bool icon_visible_ = false;
-  bool protections_on_ = false;
+  CookieControlsState controls_state_ = CookieControlsState::kHidden;
   bool protections_changed_ = true;
   bool did_animate_ = false;
   // Whether we should have a visual indicator highlighting the icon.
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 cc2a0d3..9be861c 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
@@ -157,9 +157,9 @@
 
 TEST_P(CookieControlsIconViewUnitTest,
        IconAnimatesWhenShouldHighlightIsTrueAndProtectionsAreOn) {
-  view_->OnCookieControlsIconStatusChanged(/*icon_visible=*/true,
-                                           /*protections_on=*/true, GetParam(),
-                                           /*should_highlight=*/true);
+  view_->OnCookieControlsIconStatusChanged(
+      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*should_highlight=*/true);
   FlushEvents();
   EXPECT_TRUE(Visible());
   EXPECT_TRUE(LabelShown());
@@ -180,9 +180,9 @@
 
 TEST_P(CookieControlsIconViewUnitTest,
        IconAnimatesOnPageReloadWithChangedSettings) {
-  view_->OnCookieControlsIconStatusChanged(/*icon_visible=*/true,
-                                           /*protections_on=*/true, GetParam(),
-                                           /*should_highlight=*/false);
+  view_->OnCookieControlsIconStatusChanged(
+      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*should_highlight=*/false);
   FlushEvents();
   ExecuteIcon();
   // Force the icon to animate and set the label again
@@ -199,42 +199,42 @@
 
 TEST_P(CookieControlsIconViewUnitTest,
        IconAnimationTextDoesNotResetWhenProtectionsDoNotChange) {
-  view_->OnCookieControlsIconStatusChanged(/*icon_visible=*/true,
-                                           /*protections_on=*/true, GetParam(),
-                                           /*should_highlight=*/true);
+  view_->OnCookieControlsIconStatusChanged(
+      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*should_highlight=*/true);
   FlushEvents();
   EXPECT_TRUE(Visible());
   EXPECT_TRUE(LabelShown());
   EXPECT_EQ(LabelText(), In3pcd() ? SiteNotWorkingLabel() : BlockedLabel());
 
-  view_->OnCookieControlsIconStatusChanged(/*icon_visible=*/true,
-                                           /*protections_on=*/true, GetParam(),
-                                           /*should_highlight=*/true);
+  view_->OnCookieControlsIconStatusChanged(
+      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*should_highlight=*/true);
   FlushEvents();
   EXPECT_EQ(LabelText(), In3pcd() ? SiteNotWorkingLabel() : BlockedLabel());
 }
 
 TEST_P(CookieControlsIconViewUnitTest,
        IconAnimationTextUpdatesWhenProtectionsChange) {
-  view_->OnCookieControlsIconStatusChanged(/*icon_visible=*/true,
-                                           /*protections_on=*/true, GetParam(),
-                                           /*should_highlight=*/true);
+  view_->OnCookieControlsIconStatusChanged(
+      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*should_highlight=*/true);
   FlushEvents();
   EXPECT_TRUE(Visible());
   EXPECT_TRUE(LabelShown());
   EXPECT_EQ(LabelText(), In3pcd() ? SiteNotWorkingLabel() : BlockedLabel());
 
-  view_->OnCookieControlsIconStatusChanged(/*icon_visible=*/true,
-                                           /*protections_on=*/false, GetParam(),
-                                           /*should_highlight=*/true);
+  view_->OnCookieControlsIconStatusChanged(
+      /*icon_visible=*/true, CookieControlsState::k3pcsAllowed, GetParam(),
+      /*should_highlight=*/true);
   FlushEvents();
   EXPECT_EQ(LabelText(), AllowedLabel());
 }
 
 TEST_P(CookieControlsIconViewUnitTest, IconAnimationIsResetOnWebContentChange) {
-  view_->OnCookieControlsIconStatusChanged(/*icon_visible=*/true,
-                                           /*protections_on=*/true, GetParam(),
-                                           /*should_highlight=*/true);
+  view_->OnCookieControlsIconStatusChanged(
+      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*should_highlight=*/true);
   FlushEvents();
   EXPECT_TRUE(Visible());
   EXPECT_TRUE(LabelShown());
@@ -245,9 +245,9 @@
   EXPECT_EQ(user_actions_.GetActionCount(kUMAIconOpened), 0);
   // Simulate a change in web content.
   view_->UpdateImpl();
-  view_->OnCookieControlsIconStatusChanged(/*icon_visible=*/true,
-                                           /*protections_on=*/true, GetParam(),
-                                           /*should_highlight=*/false);
+  view_->OnCookieControlsIconStatusChanged(
+      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*should_highlight=*/false);
   FlushEvents();
   ExecuteIcon();
   EXPECT_TRUE(Visible());
@@ -260,9 +260,9 @@
 }
 
 TEST_P(CookieControlsIconViewUnitTest, HidingIconDoesNotRetriggerA11yReadOut) {
-  view_->OnCookieControlsIconStatusChanged(/*icon_visible=*/true,
-                                           /*protections_on=*/true, GetParam(),
-                                           /*should_highlight=*/true);
+  view_->OnCookieControlsIconStatusChanged(
+      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*should_highlight=*/true);
   FlushEvents();
   EXPECT_TRUE(Visible());
   EXPECT_TRUE(LabelShown());
@@ -276,9 +276,9 @@
 
   EXPECT_EQ(user_actions_.GetActionCount(kUMAIconAnimated), 1);
   EXPECT_EQ(user_actions_.GetActionCount(kUMAIconShown), 0);
-  view_->OnCookieControlsIconStatusChanged(/*icon_visible=*/false,
-                                           /*protections_on=*/true, GetParam(),
-                                           /*should_highlight=*/false);
+  view_->OnCookieControlsIconStatusChanged(
+      /*icon_visible=*/false, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*should_highlight=*/false);
   FlushEvents();
   EXPECT_FALSE(Visible());
   EXPECT_FALSE(LabelShown());
@@ -296,9 +296,9 @@
 
 TEST_P(CookieControlsIconViewUnitTest,
        IconDoesNotAnimateWhenShouldHighlightIsFalse) {
-  view_->OnCookieControlsIconStatusChanged(/*icon_visible=*/true,
-                                           /*protections_on=*/true, GetParam(),
-                                           /*should_highlight=*/false);
+  view_->OnCookieControlsIconStatusChanged(
+      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*should_highlight=*/false);
   FlushEvents();
   EXPECT_TRUE(Visible());
   EXPECT_FALSE(LabelShown());
@@ -308,9 +308,9 @@
 }
 
 TEST_P(CookieControlsIconViewUnitTest, IconHiddenWhenIconVisibleIsFalse) {
-  view_->OnCookieControlsIconStatusChanged(/*icon_visible=*/false,
-                                           /*protections_on=*/false, GetParam(),
-                                           /*should_highlight=*/false);
+  view_->OnCookieControlsIconStatusChanged(
+      /*icon_visible=*/false, CookieControlsState::k3pcsAllowed, GetParam(),
+      /*should_highlight=*/false);
   FlushEvents();
   EXPECT_FALSE(Visible());
   EXPECT_FALSE(LabelShown());
@@ -325,9 +325,9 @@
 
 TEST_P(CookieControlsIconViewUnitTest,
        RecordsIconOpenMetricWhenProtectionsAreOff) {
-  view_->OnCookieControlsIconStatusChanged(/*icon_visible=*/true,
-                                           /*protections_on=*/false, GetParam(),
-                                           /*should_highlight=*/false);
+  view_->OnCookieControlsIconStatusChanged(
+      /*icon_visible=*/true, CookieControlsState::k3pcsAllowed, GetParam(),
+      /*should_highlight=*/false);
   FlushEvents();
   EXPECT_TRUE(Visible());
   EXPECT_EQ(TooltipText(), AllowedLabel());
@@ -340,9 +340,9 @@
 
 TEST_P(CookieControlsIconViewUnitTest,
        RecordsIconOpenMetricWhenProtectionsAreOn) {
-  view_->OnCookieControlsIconStatusChanged(/*icon_visible=*/true,
-                                           /*protections_on=*/true, GetParam(),
-                                           /*should_highlight=*/false);
+  view_->OnCookieControlsIconStatusChanged(
+      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*should_highlight=*/false);
   FlushEvents();
   EXPECT_EQ(TooltipText(),
             In3pcd() ? TrackingProtectionLabel() : BlockedLabel());
diff --git a/chrome/browser/ui/views/new_tab_footer/BUILD.gn b/chrome/browser/ui/views/new_tab_footer/BUILD.gn
index 864c3de1..a1e6345 100644
--- a/chrome/browser/ui/views/new_tab_footer/BUILD.gn
+++ b/chrome/browser/ui/views/new_tab_footer/BUILD.gn
@@ -6,6 +6,8 @@
 
 source_set("new_tab_footer") {
   sources = [
+    "footer_controller.cc",
+    "footer_controller.h",
     "footer_web_view.cc",
     "footer_web_view.h",
   ]
@@ -14,9 +16,12 @@
     "//base:base",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/resources/new_tab_footer:resources_grit",
+    "//chrome/browser/ui:ui_features",
+    "//chrome/browser/ui/browser_window",
     "//chrome/browser/ui/webui/new_tab_footer",
     "//chrome/browser/ui/webui/top_chrome",
     "//chrome/common",
+    "//components/tabs:public",
     "//content/public/browser",
     "//ui/base",
     "//ui/views",
diff --git a/chrome/browser/ui/views/new_tab_footer/footer_controller.cc b/chrome/browser/ui/views/new_tab_footer/footer_controller.cc
new file mode 100644
index 0000000..4f7213a
--- /dev/null
+++ b/chrome/browser/ui/views/new_tab_footer/footer_controller.cc
@@ -0,0 +1,28 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/new_tab_footer/footer_controller.h"
+
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/new_tab_footer/footer_web_view.h"
+
+namespace new_tab_footer {
+
+NewTabFooterController::NewTabFooterController(tabs::TabInterface* tab)
+    : tab_(tab) {
+  // TODO(crbug.com/4438803): Support SideBySide.
+  if (!features::IsNtpFooterEnabledWithoutSideBySide()) {
+    return;
+  }
+
+  auto* footer_web_view =
+      tab_->GetBrowserWindowInterface()->NewTabFooterWebView();
+  CHECK(footer_web_view);
+  // TODO(crbug.com/409056427): Show/hide the footer based on what tab is being
+  // used.
+  footer_web_view->ShowUI();
+}
+
+}  // namespace new_tab_footer
diff --git a/chrome/browser/ui/views/new_tab_footer/footer_controller.h b/chrome/browser/ui/views/new_tab_footer/footer_controller.h
new file mode 100644
index 0000000..c1195c6a
--- /dev/null
+++ b/chrome/browser/ui/views/new_tab_footer/footer_controller.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 CHROME_BROWSER_UI_VIEWS_NEW_TAB_FOOTER_FOOTER_CONTROLLER_H_
+#define CHROME_BROWSER_UI_VIEWS_NEW_TAB_FOOTER_FOOTER_CONTROLLER_H_
+
+#include "components/tabs/public/tab_interface.h"
+
+namespace new_tab_footer {
+
+// Class used to manage the state of the new tab footer.
+class NewTabFooterController {
+ public:
+  explicit NewTabFooterController(tabs::TabInterface* tab);
+  NewTabFooterController(const NewTabFooterController&) = delete;
+  NewTabFooterController& operator=(const NewTabFooterController&) = delete;
+
+ private:
+  const raw_ptr<tabs::TabInterface> tab_;
+};
+
+}  // namespace new_tab_footer
+
+#endif  // CHROME_BROWSER_UI_VIEWS_NEW_TAB_FOOTER_FOOTER_CONTROLLER_H_
diff --git a/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc b/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc
index 8e1bb62..8e8d856e 100644
--- a/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc
+++ b/chrome/browser/ui/views/omnibox/rounded_omnibox_results_frame.cc
@@ -246,7 +246,7 @@
   auto border = std::make_unique<views::BubbleBorder>(
       views::BubbleBorder::Arrow::NONE,
       views::BubbleBorder::Shadow::STANDARD_SHADOW);
-  border->SetCornerRadius(corner_radius);
+  border->set_rounded_corners(gfx::RoundedCornersF(corner_radius));
   border->set_md_shadow_elevation(kElevation);
   SetBorder(std::move(border));
 
diff --git a/chrome/browser/ui/views/page_action/page_action_controller.cc b/chrome/browser/ui/views/page_action/page_action_controller.cc
index 6b28b62..81e391c 100644
--- a/chrome/browser/ui/views/page_action/page_action_controller.cc
+++ b/chrome/browser/ui/views/page_action/page_action_controller.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/ui/views/page_action/page_action_controller.h"
 
+#include <memory>
+#include <utility>
+
 #include "chrome/browser/ui/toolbar/pinned_toolbar/pinned_toolbar_actions_model.h"
 #include "chrome/browser/ui/views/page_action/page_action_metrics_recorder.h"
 #include "chrome/browser/ui/views/page_action/page_action_metrics_recorder_interface.h"
diff --git a/chrome/browser/ui/views/page_action/page_action_metrics_recorder.cc b/chrome/browser/ui/views/page_action/page_action_metrics_recorder.cc
index 0dea9af..8c553f1 100644
--- a/chrome/browser/ui/views/page_action/page_action_metrics_recorder.cc
+++ b/chrome/browser/ui/views/page_action/page_action_metrics_recorder.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/views/page_action/page_action_metrics_recorder.h"
 
+#include <utility>
+
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/strcat.h"
 #include "chrome/browser/ui/views/page_action/page_action_enums.h"
diff --git a/chrome/browser/ui/views/page_action/page_action_page_metrics_recorder.cc b/chrome/browser/ui/views/page_action/page_action_page_metrics_recorder.cc
index 653e178..307e51a 100644
--- a/chrome/browser/ui/views/page_action/page_action_page_metrics_recorder.cc
+++ b/chrome/browser/ui/views/page_action/page_action_page_metrics_recorder.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/views/page_action/page_action_page_metrics_recorder.h"
 
+#include <utility>
+
 #include "base/metrics/histogram_functions.h"
 #include "chrome/browser/ui/views/page_action/page_action_enums.h"
 #include "chrome/browser/ui/views/page_action/page_action_model.h"
diff --git a/chrome/browser/ui/views/page_action/page_action_view.cc b/chrome/browser/ui/views/page_action/page_action_view.cc
index d2a0d7f..e93e9de 100644
--- a/chrome/browser/ui/views/page_action/page_action_view.cc
+++ b/chrome/browser/ui/views/page_action/page_action_view.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/views/page_action/page_action_view.h"
 
+#include <utility>
+
 #include "base/callback_list.h"
 #include "base/functional/bind.h"
 #include "chrome/browser/ui/layout_constants.h"
diff --git a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
index dfa40e0..1e8fb34a 100644
--- a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
@@ -124,6 +124,13 @@
     views::SetImageFromVectorIconWithColor(this, vector_icons::kCloseIcon,
                                            enabled_color, disabled_color);
   }
+
+  base::WeakPtr<PaymentHandlerCloseButton> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+ private:
+  base::WeakPtrFactory<PaymentHandlerCloseButton> weak_ptr_factory_{this};
 };
 
 BEGIN_METADATA(PaymentHandlerCloseButton)
@@ -392,10 +399,18 @@
       color_utils::kMinimumVisibleContrastRatio);
   const SkColor close_icon_disabled_color = color_utils::AlphaBlend(
       close_icon_color, background_color, gfx::kDisabledControlAlpha);
-  container->AddChildView(std::make_unique<PaymentHandlerCloseButton>(
+  auto close_button = std::make_unique<PaymentHandlerCloseButton>(
       base::BindRepeating(&PaymentRequestSheetController::CloseButtonPressed,
                           GetWeakPtr()),
-      close_icon_color, close_icon_disabled_color));
+      close_icon_color, close_icon_disabled_color);
+  close_button_ = close_button->GetWeakPtr();
+  container->AddChildView(std::move(close_button));
+}
+
+views::View* PaymentHandlerWebFlowViewController::GetFirstFocusedView() {
+  // Prevent focusing the hidden "Cancel" button (https://crbug.com/415275892).
+  return close_button_ ? close_button_.get()
+                       : PaymentRequestSheetController::GetFirstFocusedView();
 }
 
 bool PaymentHandlerWebFlowViewController::GetSheetId(DialogViewID* sheet_id) {
diff --git a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.h b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.h
index 9013038..47cf625 100644
--- a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.h
+++ b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_PAYMENTS_PAYMENT_HANDLER_WEB_FLOW_VIEW_CONTROLLER_H_
 #define CHROME_BROWSER_UI_VIEWS_PAYMENTS_PAYMENT_HANDLER_WEB_FLOW_VIEW_CONTROLLER_H_
 
+#include "base/memory/weak_ptr.h"
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ui/views/payments/payment_handler_modal_dialog_manager_delegate.h"
 #include "chrome/browser/ui/views/payments/payment_request_sheet_controller.h"
@@ -15,6 +16,7 @@
 #include "ui/base/window_open_disposition.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/views/controls/webview/unhandled_keyboard_event_handler.h"
+#include "ui/views/view.h"
 #include "url/gurl.h"
 
 class Profile;
@@ -67,6 +69,7 @@
   bool ShouldShowPrimaryButton() override;
   bool ShouldShowSecondaryButton() override;
   void PopulateSheetHeaderView(views::View* view) override;
+  views::View* GetFirstFocusedView() override;
   bool GetSheetId(DialogViewID* sheet_id) override;
   bool DisplayDynamicBorderForHiddenContents() override;
   bool CanContentViewBeScrollable() override;
@@ -103,6 +106,7 @@
   GURL target_;
   raw_ptr<views::ProgressBar, DanglingUntriaged> progress_bar_ = nullptr;
   raw_ptr<views::View, DanglingUntriaged> separator_ = nullptr;
+  base::WeakPtr<views::View> close_button_;
   PaymentHandlerOpenWindowCallback first_navigation_complete_callback_;
   // Used to present modal dialog triggered from the payment handler web view,
   // e.g. an authenticator dialog.
diff --git a/chrome/browser/ui/views/permissions/chip/chip_controller.cc b/chrome/browser/ui/views/permissions/chip/chip_controller.cc
index 233bb6d9..6663a33 100644
--- a/chrome/browser/ui/views/permissions/chip/chip_controller.cc
+++ b/chrome/browser/ui/views/permissions/chip/chip_controller.cc
@@ -335,8 +335,12 @@
 
   request_chip_shown_time_ = base::TimeTicks::Now();
 
-  AnnouncePermissionRequestForAccessibility(
-      permission_prompt_model_->GetAccessibilityChipText());
+  // The permission prompt bubble has its own accessibility announcement. We
+  // should not announce the chip.
+  if (!permission_prompt_model_->ShouldBubbleStartOpen()) {
+    AnnouncePermissionRequestForAccessibility(
+        permission_prompt_model_->GetAccessibilityChipText());
+  }
 
   chip_->SetVisible(true);
 
diff --git a/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view_browsertest.cc b/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view_browsertest.cc
index 40f9bf1..243a67f 100644
--- a/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view_browsertest.cc
+++ b/chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view_browsertest.cc
@@ -235,15 +235,9 @@
   EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kAlert));
   ShowUi("geolocation");
 
-  ChipController* chip_controller = GetChipController();
-
-  // If chip UI is used, two notifications will be announced: one that
-  // permission was requested and second when bubble is opened.
-  if (chip_controller->IsPermissionPromptChipVisible()) {
-    EXPECT_EQ(2, counter.GetCount(ax::mojom::Event::kAlert));
-  } else {
-    EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kAlert));
-  }
+  // Even though chip UI is used, only the event "permission prompt bubble is
+  // opened" will be announced.
+  EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kAlert));
 }
 
 // Test switching between PermissionChip and PermissionPromptBubbleBaseView and
diff --git a/chrome/browser/ui/views/tabs/tab_group_header.cc b/chrome/browser/ui/views/tabs/tab_group_header.cc
index 9c23455..7f01dd11 100644
--- a/chrome/browser/ui/views/tabs/tab_group_header.cc
+++ b/chrome/browser/ui/views/tabs/tab_group_header.cc
@@ -44,6 +44,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/mojom/menu_source_type.mojom-forward.h"
+#include "ui/base/mojom/menu_source_type.mojom-shared.h"
 #include "ui/color/color_id.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
@@ -226,13 +227,21 @@
 
 void TabGroupHeader::OnMouseReleased(const ui::MouseEvent& event) {
   if (!dragging()) {
-    if (event.IsLeftMouseButton()) {
-      tab_slot_controller_->ToggleTabGroupCollapsedState(
-          group().value(), ToggleTabGroupCollapsedStateOrigin::kMouse);
-    } else if (event.IsRightMouseButton() &&
-               !editor_bubble_tracker_.is_open()) {
+    bool open_editor_bubble =
+        base::FeatureList::IsEnabled(tab_groups::kLeftClickOpensTabGroupBubble)
+            ? (event.IsLeftMouseButton() && !editor_bubble_tracker_.is_open())
+            : (event.IsRightMouseButton() && !editor_bubble_tracker_.is_open());
+    bool toggle_collapse =
+        base::FeatureList::IsEnabled(tab_groups::kLeftClickOpensTabGroupBubble)
+            ? event.IsRightMouseButton()
+            : event.IsLeftMouseButton();
+
+    if (open_editor_bubble) {
       editor_bubble_tracker_.Opened(TabGroupEditorBubbleView::Show(
           tab_slot_controller_->GetBrowser(), group().value(), this));
+    } else if (toggle_collapse) {
+      tab_slot_controller_->ToggleTabGroupCollapsedState(
+          group().value(), ToggleTabGroupCollapsedStateOrigin::kMouse);
     }
   }
 
@@ -337,7 +346,12 @@
     views::View* source,
     const gfx::Point& point,
     ui::mojom::MenuSourceType source_type) {
-  if (editor_bubble_tracker_.is_open()) {
+  // Right click toggles ShowContextMenuForViewImpl, which we dont want to occur
+  // if the left click should toggle the context menu.
+  if ((source_type == ui::mojom::MenuSourceType::kMouse &&
+       base::FeatureList::IsEnabled(
+           tab_groups::kLeftClickOpensTabGroupBubble)) ||
+      editor_bubble_tracker_.is_open()) {
     return;
   }
 
diff --git a/chrome/browser/ui/views/tabs/tab_group_header_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_group_header_interactive_uitest.cc
new file mode 100644
index 0000000..e488225
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/tab_group_header_interactive_uitest.cc
@@ -0,0 +1,112 @@
+// 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/feature_list.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/data_sharing/data_sharing_service_factory.h"
+#include "chrome/browser/tab_group_sync/tab_group_sync_service_factory.h"
+#include "chrome/browser/ui/browser_element_identifiers.h"
+#include "chrome/browser/ui/tabs/tab_group.h"
+#include "chrome/browser/ui/tabs/tab_group_model.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/tabs/test/tab_strip_interactive_test_mixin.h"
+#include "chrome/browser/ui/views/tabs/recent_activity_bubble_dialog_view.h"
+#include "chrome/browser/ui/views/tabs/tab_group_header.h"
+#include "chrome/browser/ui/views/tabs/tab_strip.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/interaction/interactive_browser_test.h"
+#include "components/collaboration/public/features.h"
+#include "components/data_sharing/public/features.h"
+#include "components/saved_tab_groups/public/features.h"
+#include "components/saved_tab_groups/public/tab_group_sync_service.h"
+#include "components/signin/public/base/avatar_icon_util.h"
+#include "components/tab_groups/tab_group_id.h"
+#include "content/public/test/browser_test.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/http_connection.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/image/image_unittest_util.h"
+
+// The Param value sets tab_groups::kLeftClickOpensTabGroupBubble.
+class TabGroupHeaderInteractiveUiTest
+    : public TabStripInteractiveTestMixin<InteractiveBrowserTest>,
+      public testing::WithParamInterface<bool> {
+ public:
+  TabGroupHeaderInteractiveUiTest() {
+    if (GetParam()) {
+      scoped_feature_list_.InitWithFeatures(
+          /* enabled_features =*/{tab_groups::kLeftClickOpensTabGroupBubble},
+          /* disabled_features =*/{});
+    } else {
+      scoped_feature_list_.InitWithFeatures(
+          /* enabled_features =*/{},
+          /* disabled_features =*/{tab_groups::kLeftClickOpensTabGroupBubble});
+    }
+  }
+
+  ~TabGroupHeaderInteractiveUiTest() override = default;
+
+  tabs::TabInterface* CreateTab() {
+    auto index = browser()->tab_strip_model()->count();
+    CHECK(AddTabAtIndex(index, GURL(chrome::kChromeUINewTabPageURL),
+                        ui::PAGE_TRANSITION_TYPED));
+    auto* tab = browser()->tab_strip_model()->GetTabAtIndex(index);
+    CHECK(tab);
+    return tab;
+  }
+
+  const tab_groups::TabGroupId CreateTabGroup(
+      std::vector<tabs::TabInterface*> tabs) {
+    std::vector<int> tab_indices = {};
+    for (auto* tab : tabs) {
+      tab_indices.emplace_back(
+          browser()->tab_strip_model()->GetIndexOfTab(tab));
+    }
+    return browser()->tab_strip_model()->AddToNewGroup(tab_indices);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Disable these tests on windows.
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_Collapse DISABLED_Collapse
+#else
+#define MAYBE_Collapse Collapse
+#endif
+IN_PROC_BROWSER_TEST_P(TabGroupHeaderInteractiveUiTest, MAYBE_Collapse) {
+  CreateTabGroup({CreateTab()});
+
+  ui_controls::MouseButton action =
+      base::FeatureList::IsEnabled(tab_groups::kLeftClickOpensTabGroupBubble)
+          ? ui_controls::MouseButton::RIGHT
+          : ui_controls::MouseButton::LEFT;
+
+  RunTestSequence(WaitForShow(kTabGroupHeaderElementId),
+                  FinishTabstripAnimations(),
+                  MoveMouseTo(kTabGroupHeaderElementId), ClickMouse(action),
+                  WaitForActivate(kTabGroupHeaderElementId));
+}
+
+IN_PROC_BROWSER_TEST_P(TabGroupHeaderInteractiveUiTest, OpenEditorBubble) {
+  CreateTabGroup({CreateTab()});
+
+  ui_controls::MouseButton action =
+      base::FeatureList::IsEnabled(tab_groups::kLeftClickOpensTabGroupBubble)
+          ? ui_controls::MouseButton::LEFT
+          : ui_controls::MouseButton::RIGHT;
+
+  RunTestSequence(WaitForShow(kTabGroupHeaderElementId),
+                  FinishTabstripAnimations(),
+                  MoveMouseTo(kTabGroupHeaderElementId), ClickMouse(action),
+                  WaitForShow(kTabGroupEditorBubbleId));
+}
+
+INSTANTIATE_TEST_SUITE_P(TabGroupFeature,
+                         TabGroupHeaderInteractiveUiTest,
+                         ::testing::Bool());
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
index d330df32..5c3401a6 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
@@ -455,7 +455,7 @@
       views::BubbleFrameView::PreferredArrowAdjustment::kOffset);
   GetBubbleFrameView()->set_hit_test_transparent(true);
 
-  GetBubbleFrameView()->SetCornerRadius(corner_radius_);
+  GetBubbleFrameView()->SetRoundedCorners(gfx::RoundedCornersF(corner_radius_));
 
   // Placeholder image should be used when there is no image data for the
   // given tab. Otherwise don't flash the placeholder while we wait for the
diff --git a/chrome/browser/ui/views/tabs/tab_style_views.cc b/chrome/browser/ui/views/tabs/tab_style_views.cc
index 61171ed..9015c37b 100644
--- a/chrome/browser/ui/views/tabs/tab_style_views.cc
+++ b/chrome/browser/ui/views/tabs/tab_style_views.cc
@@ -45,6 +45,14 @@
 
 namespace {
 
+Tab* GetLeftTab(const Tab* tab) {
+  return tab->controller()->GetAdjacentTab(tab, base::i18n::IsRTL() ? 1 : -1);
+}
+
+Tab* GetRightTab(const Tab* tab) {
+  return tab->controller()->GetAdjacentTab(tab, base::i18n::IsRTL() ? -1 : 1);
+}
+
 // Updates a target value, returning true if it changed.
 template <class T>
 bool UpdateValue(T* dest, const T& src) {
@@ -172,10 +180,14 @@
                              SkColor stroke_color) const;
   void PaintSeparators(gfx::Canvas* canvas) const;
 
-  // Returns true if the tab is the first in a set of split tabs.
-  bool IsStartSplitTab(const Tab* tab) const;
-  // Returns true if the tab is the last in a set of split tabs.
-  bool IsEndSplitTab(const Tab* tab) const;
+  // Returns true if the tab is the leftmost in a set of split tabs. In RTL,
+  // this will still return the leftmost tab which is needed when setting
+  // left/right insets and positioning.
+  bool IsLeftSplitTab(const Tab* tab) const;
+  // Returns true if the tab is the rightmost in a set of split tabs. In RTL,
+  // this will still return the rightmost tab which is needed when setting
+  // left/right insets and positioning.
+  bool IsRightSplitTab(const Tab* tab) const;
 
   const raw_ptr<const Tab> tab_;
 
@@ -266,10 +278,10 @@
     const bool limited_tab_space = (right - left) < (gfx::kFaviconSize * scale);
     const bool expand_into_previous_separator =
         limited_tab_space || path_type == TabStyle::PathType::kHitTest ||
-        IsEndSplitTab(tab());
+        IsRightSplitTab(tab());
     const bool expand_into_next_separator =
         limited_tab_space || path_type == TabStyle::PathType::kHitTest ||
-        IsStartSplitTab(tab());
+        IsLeftSplitTab(tab());
     if (expand_into_previous_separator || expand_into_next_separator) {
       // Take the entire size of the separator. in odd separator size cases, the
       // right side will take the remaining space.
@@ -279,8 +291,7 @@
           tab_style()->GetSeparatorSize().width() - left_separator_overlap;
 
       // If there is a tab before this one, then expand into its overlap.
-      const Tab* const previous_tab =
-          tab()->controller()->GetAdjacentTab(tab(), -1);
+      const Tab* const previous_tab = GetLeftTab(tab());
       if (expand_into_previous_separator && previous_tab) {
         left -= (tab_style()->GetSeparatorMargins().right() +
                  left_separator_overlap) *
@@ -288,7 +299,7 @@
       }
 
       // If there is a tab after this one, then expand into its overlap.
-      const Tab* const next_tab = tab()->controller()->GetAdjacentTab(tab(), 1);
+      const Tab* const next_tab = GetRightTab(tab());
       if (expand_into_next_separator && next_tab) {
         right += (tab_style()->GetSeparatorMargins().left() +
                   right_separator_overlap) *
@@ -297,10 +308,10 @@
     }
 
     if (tab()->split().has_value()) {
-      if (IsStartSplitTab(tab())) {
+      if (IsLeftSplitTab(tab())) {
         top_right_corner_radius = 0;
         bottom_right_corner_radius = 0;
-      } else if (IsEndSplitTab(tab())) {
+      } else if (IsRightSplitTab(tab())) {
         top_left_corner_radius = 0;
         bottom_left_corner_radius = 0;
       }
@@ -375,12 +386,12 @@
   }
 
   if (tab()->split().has_value()) {
-    if (IsStartSplitTab(tab())) {
+    if (IsLeftSplitTab(tab())) {
       top_right_corner_radius = 0;
       // Assign half of the tab overlap to each of the split tabs.
       tab_right = tab_right + extension - tab_style()->GetTabOverlap() / 2;
       extension_corner_radius = 0;
-    } else if (IsEndSplitTab(tab())) {
+    } else if (IsRightSplitTab(tab())) {
       top_left_corner_radius = 0;
       tab_left = tab_left - extension + tab_style()->GetTabOverlap() / 2;
       left_extension_corner_radius = 0;
@@ -400,7 +411,7 @@
   // stroke width.
 
   if (path_type == TabStyle::PathType::kBorder && tab()->split() &&
-      !IsStartSplitTab(tab())) {
+      !IsLeftSplitTab(tab())) {
     // Start with the top left side of the shape.
     path.moveTo(left, tab_top);
   } else {
@@ -441,7 +452,7 @@
   path.lineTo(tab_right - top_right_corner_radius, tab_top);
 
   if (path_type == TabStyle::PathType::kBorder && tab()->split() &&
-      !IsEndSplitTab(tab())) {
+      !IsRightSplitTab(tab())) {
     // Finish to the top right corner.
     path.lineTo(right, tab_top);
   } else {
@@ -501,10 +512,10 @@
       tab_style()->GetSeparatorMargins().left() +
       tab_style()->GetSeparatorSize().width() +
       tab_style()->GetSeparatorMargins().right();
-  if (IsEndSplitTab(tab())) {
+  if (IsRightSplitTab(tab())) {
     split_insets.set_left(total_separator_width / -2);
   }
-  if (IsStartSplitTab(tab())) {
+  if (IsLeftSplitTab(tab())) {
     split_insets.set_right(total_separator_width / -2);
   }
 
@@ -1065,26 +1076,30 @@
                         tab_style()->GetSeparatorCornerRadius() * scale, flags);
 }
 
-bool TabStyleViewsImpl::IsStartSplitTab(const Tab* tab) const {
+bool TabStyleViewsImpl::IsLeftSplitTab(const Tab* tab) const {
   if (!tab->split().has_value()) {
     return false;
   }
-  const Tab* tab_to_left = tab->controller()->GetAdjacentTab(tab, -1);
-  return std::ranges::none_of(tab->controller()->GetTabsInSplit(tab),
-                              [&tab_to_left](const Tab* split_tab) {
-                                return split_tab == tab_to_left;
-                              });
+  const std::vector<Tab*>& tabs_in_split =
+      tab->controller()->GetTabsInSplit(tab);
+  if (tabs_in_split.size() < 2) {
+    return true;
+  }
+  return tab ==
+         tabs_in_split[base::i18n::IsRTL() ? tabs_in_split.size() - 1 : 0];
 }
 
-bool TabStyleViewsImpl::IsEndSplitTab(const Tab* tab) const {
+bool TabStyleViewsImpl::IsRightSplitTab(const Tab* tab) const {
   if (!tab->split().has_value()) {
     return false;
   }
-  const Tab* tab_to_right = tab->controller()->GetAdjacentTab(tab, 1);
-  return std::ranges::none_of(tab->controller()->GetTabsInSplit(tab),
-                              [&tab_to_right](const Tab* split_tab) {
-                                return split_tab == tab_to_right;
-                              });
+  const std::vector<Tab*>& tabs_in_split =
+      tab->controller()->GetTabsInSplit(tab);
+  if (tabs_in_split.size() < 2) {
+    return true;
+  }
+  return tab ==
+         tabs_in_split[base::i18n::IsRTL() ? 0 : tabs_in_split.size() - 1];
 }
 
 float TabStyleViewsImpl::GetTopCornerRadiusForWidth(int width) const {
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.cc b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.cc
index 4cc048e..54efdf45e 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.cc
@@ -22,6 +22,7 @@
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/mojom/dialog_button.mojom.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/style/typography.h"
@@ -213,9 +214,10 @@
   GetBubbleFrameView()->SetPreferredArrowAdjustment(
       views::BubbleFrameView::PreferredArrowAdjustment::kOffset);
   GetBubbleFrameView()->set_hit_test_transparent(true);
-  GetBubbleFrameView()->SetCornerRadius(
-      ChromeLayoutProvider::Get()->GetCornerRadiusMetric(
-          views::Emphasis::kHigh));
+
+  const int corner_radius = ChromeLayoutProvider::Get()->GetCornerRadiusMetric(
+      views::Emphasis::kHigh);
+  GetBubbleFrameView()->SetRoundedCorners(gfx::RoundedCornersF(corner_radius));
 
   // Start in the fully "faded-in" position so that whatever text we initially
   // display is visible.
diff --git a/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_handler.cc b/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_handler.cc
index 6e6cd21..61f431b 100644
--- a/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_handler.cc
+++ b/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_handler.cc
@@ -269,8 +269,7 @@
   params.Set("hl", app_locale);
   params.Set("email", context.email);
   params.Set("gaiaId", context.gaia_id.ToString());
-  params.Set("extractSamlPasswordAttributes",
-             login::ExtractSamlPasswordAttributesEnabled());
+  params.Set("extractSamlPasswordAttributes", true);
   params.Set("clientVersion", version_info::GetVersionNumber());
   params.Set("readOnlyEmail", true);
   PrefService* local_state = g_browser_process->local_state();
diff --git a/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc
index a396bd9a..ca5404d 100644
--- a/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc
@@ -515,8 +515,7 @@
   params.Set("webviewPartitionName", partition_name);
   signin_partition_name_ = partition_name;
 
-  params.Set("extractSamlPasswordAttributes",
-             login::ExtractSamlPasswordAttributesEnabled());
+  params.Set("extractSamlPasswordAttributes", true);
 
   params.Set("recordAccountCreation",
              ash::features::IsGaiaRecordAccountCreationEnabled());
diff --git a/chrome/browser/ui/webui/ash/login/online_login_utils.cc b/chrome/browser/ui/webui/ash/login/online_login_utils.cc
index d5fa118..301dd5b 100644
--- a/chrome/browser/ui/webui/ash/login/online_login_utils.cc
+++ b/chrome/browser/ui/webui/ash/login/online_login_utils.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/ui/ash/login/login_display_host_webui.h"
 #include "chrome/browser/ui/ash/login/signin_ui.h"
-#include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/installer/util/google_update_settings.h"
 #include "chromeos/ash/components/login/auth/challenge_response/cert_utils.h"
@@ -73,10 +72,6 @@
       challenge_response_key(std::move(original.challenge_response_key)),
       cookies(original.cookies) {}
 
-bool ExtractSamlPasswordAttributesEnabled() {
-  return base::FeatureList::IsEnabled(::features::kInSessionPasswordChange);
-}
-
 base::OnceClosure GetStartSigninSession(::content::WebUI* web_ui,
                                         LoadGaiaWithPartition callback) {
   // Start a new session with SigninPartitionManager, generating a unique
@@ -199,9 +194,7 @@
                                 : UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML);
   if (using_saml) {
     user_context->SetIsUsingSamlPrincipalsApi(using_saml_api);
-    if (ExtractSamlPasswordAttributesEnabled()) {
-      user_context->SetSamlPasswordAttributes(password_attributes);
-    }
+    user_context->SetSamlPasswordAttributes(password_attributes);
   }
 
   if (sync_trusted_vault_keys.has_value()) {
diff --git a/chrome/browser/ui/webui/ash/login/online_login_utils.h b/chrome/browser/ui/webui/ash/login/online_login_utils.h
index 0a57f4a..7b43d98 100644
--- a/chrome/browser/ui/webui/ash/login/online_login_utils.h
+++ b/chrome/browser/ui/webui/ash/login/online_login_utils.h
@@ -96,9 +96,6 @@
 using ChallengeResponseKeyOrError =
     base::expected<ChallengeResponseKey, SigninError>;
 
-// Return whether the InSession Password Change feature is enabled.
-bool ExtractSamlPasswordAttributesEnabled();
-
 // Return Signin Session callback
 base::OnceClosure GetStartSigninSession(::content::WebUI* web_ui,
                                         LoadGaiaWithPartition callback);
diff --git a/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.cc b/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.cc
index b095e87e..8abf7ad7 100644
--- a/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.cc
+++ b/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.h"
 
+#include <algorithm>
+
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/time/time.h"
@@ -17,6 +19,7 @@
 #include "chrome/browser/sync/sync_service_factory.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_account_storage_move_dialog.h"
+#include "chrome/grit/generated_resources.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_node.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
@@ -31,6 +34,7 @@
 #include "components/sync/base/data_type.h"
 #include "components/sync/service/sync_service.h"
 #include "google_apis/gaia/gaia_id.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace {
 
@@ -71,17 +75,26 @@
 }
 
 base::Value::Dict GetBatchUploadPromoData(bool can_show,
-                                          int local_bookmark_count) {
+                                          int local_bookmark_count,
+                                          bool has_non_bookmark_local_data) {
   base::Value::Dict promo_data;
   promo_data.Set("canShow", can_show);
-  promo_data.Set("localBookmarksCount", local_bookmark_count);
+#if !BUILDFLAG(IS_CHROMEOS)
+  promo_data.Set("promoSubtitle",
+                 l10n_util::GetPluralStringFUTF16(
+                     has_non_bookmark_local_data
+                         ? IDS_BATCH_UPLOAD_PROMO_SUBTITLE_BOOKMARKS_COMBO
+                         : IDS_BATCH_UPLOAD_PROMO_SUBTITLE_BOOKMARKS,
+                     local_bookmark_count));
+#endif
   return promo_data;
 }
 
 // Return an empty result; should not show the promo.
 base::Value::Dict GetEmptyBatchUploadPromoData() {
   return GetBatchUploadPromoData(/*can_show=*/false,
-                                 /*local_bookmark_count=*/0);
+                                 /*local_bookmark_count=*/0,
+                                 /*has_non_bookmark_local_data=*/false);
 }
 
 base::Value::Dict GetBatchUploadDataFromProfileAndLocalData(
@@ -92,8 +105,17 @@
       local_data.contains(syncer::BOOKMARKS)
           ? local_data.at(syncer::BOOKMARKS).local_data_models.size()
           : 0;
+
+  bool has_non_bookmark_local_data = std::ranges::any_of(
+      local_data, [](const std::pair<syncer::DataType,
+                                     syncer::LocalDataDescription>& data) {
+        return data.first != syncer::BOOKMARKS &&
+               !data.second.local_data_models.empty();
+      });
+
   bool can_show = local_bookmark_count != 0 && CanShowBatchUploadPromo(profile);
-  return GetBatchUploadPromoData(can_show, local_bookmark_count);
+  return GetBatchUploadPromoData(can_show, local_bookmark_count,
+                                 has_non_bookmark_local_data);
 }
 
 }  // namespace
@@ -326,13 +348,14 @@
     return;
   }
 
-  syncer::SyncService* sync_service =
-      SyncServiceFactory::GetForProfile(profile);
-  sync_service->GetLocalDataDescriptions(
-      {syncer::BOOKMARKS},
-      base::BindOnce(
-          &BookmarksMessageHandler::OnGetLocalDataDescriptionReceived,
-          weak_ptr_factory_.GetWeakPtr(), callback_id.Clone()));
+#if !BUILDFLAG(IS_CHROMEOS)
+  BatchUploadService* batch_upload =
+      BatchUploadServiceFactory::GetForProfile(profile);
+  CHECK(batch_upload);
+  batch_upload->GetLocalDataDescriptionsForAvailableTypes(base::BindOnce(
+      &BookmarksMessageHandler::OnGetLocalDataDescriptionReceived,
+      weak_ptr_factory_.GetWeakPtr(), callback_id.Clone()));
+#endif
 }
 
 void BookmarksMessageHandler::OnGetLocalDataDescriptionReceived(
@@ -358,13 +381,14 @@
     return;
   }
 
-  syncer::SyncService* sync_service =
-      SyncServiceFactory::GetForProfile(profile);
-  sync_service->GetLocalDataDescriptions(
-      {syncer::BOOKMARKS},
-      base::BindOnce(
-          &BookmarksMessageHandler::FireOnGetLocalDataDescriptionReceived,
-          weak_ptr_factory_.GetWeakPtr()));
+#if !BUILDFLAG(IS_CHROMEOS)
+  BatchUploadService* batch_upload =
+      BatchUploadServiceFactory::GetForProfile(profile);
+  CHECK(batch_upload);
+  batch_upload->GetLocalDataDescriptionsForAvailableTypes(base::BindOnce(
+      &BookmarksMessageHandler::FireOnGetLocalDataDescriptionReceived,
+      weak_ptr_factory_.GetWeakPtr()));
+#endif
 }
 
 void BookmarksMessageHandler::HandleOnBatchUploadPromoClicked(
@@ -457,3 +481,15 @@
     RequestUpdateOrWaitForBatchUpdateEnd();
   }
 }
+
+void BookmarksMessageHandler::BookmarkNodeAdded(
+    const bookmarks::BookmarkNode* parent,
+    size_t index,
+    bool added_by_user) {
+  bookmarks::BookmarkModel* model =
+      BookmarkModelFactory::GetForBrowserContext(Profile::FromWebUI(web_ui()));
+  // Only attempt to request an update if the added node is local.
+  if (model->IsLocalOnlyNode(*parent->children()[index].get())) {
+    RequestUpdateOrWaitForBatchUpdateEnd();
+  }
+}
diff --git a/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.h b/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.h
index e8cff18..c0747a69 100644
--- a/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.h
+++ b/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.h
@@ -69,6 +69,9 @@
   void BookmarkModelLoaded(bool ids_reassigned) override;
   void ExtensiveBookmarkChangesBeginning() override;
   void ExtensiveBookmarkChangesEnded() override;
+  void BookmarkNodeAdded(const bookmarks::BookmarkNode* parent,
+                         size_t index,
+                         bool added_by_user) override;
   void BookmarkNodeMoved(const bookmarks::BookmarkNode* old_parent,
                          size_t old_index,
                          const bookmarks::BookmarkNode* new_parent,
@@ -78,9 +81,7 @@
                            const bookmarks::BookmarkNode* node,
                            const std::set<GURL>& no_longer_bookmarked,
                            const base::Location& location) override;
-  void BookmarkNodeAdded(const bookmarks::BookmarkNode* parent,
-                         size_t index,
-                         bool added_by_user) override {}
+
   void BookmarkNodeChanged(const bookmarks::BookmarkNode* node) override {}
   void BookmarkNodeFaviconChanged(
       const bookmarks::BookmarkNode* node) override {}
diff --git a/chrome/browser/ui/webui/bookmarks/bookmarks_ui.cc b/chrome/browser/ui/webui/bookmarks/bookmarks_ui.cc
index ce7a638..469efd3 100644
--- a/chrome/browser/ui/webui/bookmarks/bookmarks_ui.cc
+++ b/chrome/browser/ui/webui/bookmarks/bookmarks_ui.cc
@@ -66,8 +66,12 @@
       {"addBookmarkTitle", IDS_BOOKMARK_MANAGER_ADD_BOOKMARK_TITLE},
       {"addFolderTitle", IDS_BOOKMARK_MANAGER_ADD_FOLDER_TITLE},
       {"accountBookmarksTitle", IDS_BOOKMARKS_ACCOUNT_BOOKMARKS},
+#if !BUILDFLAG(IS_CHROMEOS)
+      {"bookmarkPromoCardTitle", IDS_BATCH_UPLOAD_PROMO_TITLE},
+#endif
       {"cancel", IDS_CANCEL},
       {"clearSearch", IDS_BOOKMARK_MANAGER_CLEAR_SEARCH},
+      {"close", IDS_CLOSE},
       {"delete", IDS_DELETE},
       {"editBookmarkTitle", IDS_BOOKMARK_EDITOR_TITLE},
       {"editDialogInvalidUrl", IDS_BOOKMARK_MANAGER_INVALID_URL},
@@ -125,6 +129,9 @@
       {"openDialogTitle", IDS_BOOKMARK_MANAGER_OPEN_DIALOG_TITLE},
       {"organizeButtonTitle", IDS_BOOKMARK_MANAGER_ORGANIZE_MENU},
       {"renameFolderTitle", IDS_BOOKMARK_MANAGER_FOLDER_RENAME_TITLE},
+#if !BUILDFLAG(IS_CHROMEOS)
+      {"saveToAccount", IDS_BATCH_UPLOAD_PROMO_TITLE_OK_BUTTON_LABEL},
+#endif
       {"searchPrompt", IDS_BOOKMARK_MANAGER_SEARCH_BUTTON},
       {"sidebarAxLabel", IDS_BOOKMARK_MANAGER_SIDEBAR_AX_LABEL},
       {"searchCleared", IDS_SEARCH_CLEARED},
@@ -140,6 +147,13 @@
     AddLocalizedString(source, str.name, str.id);
   }
 
+  source->AddResourcePath(
+      "images/batch_upload_bookmarks_promo.svg",
+      IDR_BOOKMARKS_IMAGES_BATCH_UPLOAD_BOOKMARKS_PROMO_SVG);
+  source->AddResourcePath(
+      "images/batch_upload_bookmarks_promo_dark.svg",
+      IDR_BOOKMARKS_IMAGES_BATCH_UPLOAD_BOOKMARKS_PROMO_DARK_SVG);
+
   return source;
 }
 
diff --git a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_ui.cc b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_ui.cc
index c5c79fe0..b161a2df 100644
--- a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_ui.cc
@@ -20,6 +20,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
+#include "ui/webui/color_change_listener/color_change_handler.h"
 #include "ui/webui/webui_util.h"
 
 NewTabFooterUIConfig::NewTabFooterUIConfig()
@@ -67,6 +68,13 @@
   document_factory_receiver_.Bind(std::move(pending_receiver));
 }
 
+void NewTabFooterUI::BindInterface(
+    mojo::PendingReceiver<color_change_listener::mojom::PageHandler>
+        pending_receiver) {
+  color_provider_handler_ = std::make_unique<ui::ColorChangeHandler>(
+      web_ui()->GetWebContents(), std::move(pending_receiver));
+}
+
 void NewTabFooterUI::CreateNewTabFooterHandler(
     mojo::PendingRemote<new_tab_footer::mojom::NewTabFooterDocument>
         pending_document,
diff --git a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_ui.h b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_ui.h
index 74f8a5d1..b151497 100644
--- a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_ui.h
+++ b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_ui.h
@@ -12,6 +12,11 @@
 #include "chrome/browser/ui/webui/top_chrome/top_chrome_web_ui_controller.h"
 #include "chrome/browser/ui/webui/top_chrome/top_chrome_webui_config.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom.h"
+
+namespace ui {
+class ColorChangeHandler;
+}  // namespace ui
 
 class NewTabFooterHandler;
 class NewTabFooterUI;
@@ -45,6 +50,12 @@
       mojo::PendingReceiver<new_tab_footer::mojom::NewTabFooterHandlerFactory>
           pending_receiver);
 
+  // Instantiates the implementor of the mojom::PageHandler mojo interface
+  // passing the pending receiver that will be internally bound.
+  void BindInterface(
+      mojo::PendingReceiver<color_change_listener::mojom::PageHandler>
+          pending_receiver);
+
  private:
   // new_tab_footer::mojom::NewTabFooterHandlerFactory:
   void CreateNewTabFooterHandler(
@@ -56,6 +67,7 @@
   std::unique_ptr<NewTabFooterHandler> handler_;
   mojo::Receiver<new_tab_footer::mojom::NewTabFooterHandlerFactory>
       document_factory_receiver_{this};
+  std::unique_ptr<ui::ColorChangeHandler> color_provider_handler_;
   raw_ptr<Profile> profile_;
 
  private:
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index e3a1e00..62060c76 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -1523,6 +1523,9 @@
       l10n_util::GetStringFUTF16(
           IDS_AUTOFILL_BNPL_SETTINGS_SUBLABEL, chrome::kPayOverTimeLearnMoreUrl,
           l10n_util::GetStringUTF16(IDS_SETTINGS_OPENS_IN_NEW_TAB)));
+
+  html_source->AddString("googleAccountHomeAddressUrl",
+                         chrome::kGoogleAccountHomeAddressURL);
 }
 
 void AddSignOutDialogStrings(content::WebUIDataSource* html_source,
diff --git a/chrome/browser/ui/webui/signin/batch_upload_handler.cc b/chrome/browser/ui/webui/signin/batch_upload_handler.cc
index 8a3f0f034..e7e766e8 100644
--- a/chrome/browser/ui/webui/signin/batch_upload_handler.cc
+++ b/chrome/browser/ui/webui/signin/batch_upload_handler.cc
@@ -296,6 +296,8 @@
       return IDS_BATCH_UPLOAD_SECTION_TITLE_PASSWORDS;
     case syncer::DataType::BOOKMARKS:
       return IDS_BATCH_UPLOAD_SECTION_TITLE_BOOKMARKS;
+    case syncer::DataType::READING_LIST:
+      return IDS_BATCH_UPLOAD_SECTION_TITLE_READING_LIST;
     case syncer::DataType::CONTACT_INFO:
       return IDS_BATCH_UPLOAD_SECTION_TITLE_ADDRESSES;
     case syncer::DataType::THEMES:
diff --git a/chrome/browser/visited_url_ranking/android_tab_model_url_visit_data_fetcher.cc b/chrome/browser/visited_url_ranking/android_tab_model_url_visit_data_fetcher.cc
index 5549f97..b14af90 100644
--- a/chrome/browser/visited_url_ranking/android_tab_model_url_visit_data_fetcher.cc
+++ b/chrome/browser/visited_url_ranking/android_tab_model_url_visit_data_fetcher.cc
@@ -57,6 +57,7 @@
     case TabModel::TabLaunchType::FROM_RECENT_TABS_FOREGROUND:
     case TabModel::TabLaunchType::FROM_HISTORY_NAVIGATION_BACKGROUND:
     case TabModel::TabLaunchType::FROM_HISTORY_NAVIGATION_FOREGROUND:
+    case TabModel::TabLaunchType::FROM_LONGPRESS_FOREGROUND_IN_GROUP:
       return TabMetadata::TabOrigin::kOpenedByUserAction;
 
     case TabModel::TabLaunchType::FROM_RESTORE:
diff --git a/chrome/browser/webid/federated_identity_account_keyed_permission_context.cc b/chrome/browser/webid/federated_identity_account_keyed_permission_context.cc
index b2eec7c..23abe0f 100644
--- a/chrome/browser/webid/federated_identity_account_keyed_permission_context.cc
+++ b/chrome/browser/webid/federated_identity_account_keyed_permission_context.cc
@@ -404,10 +404,6 @@
 
 ContentSettingsForOneType FederatedIdentityAccountKeyedPermissionContext::
     GetSharingPermissionGrantsAsContentSettings() {
-  if (!base::FeatureList::IsEnabled(
-          blink::features::kFedCmWithStorageAccessAPI)) {
-    return ContentSettingsForOneType();
-  }
   // ObjectPermissionContext stores its settings in the HostContentSettingsMap
   // keyed by <origin, null> with a value of `base::Value` (which is translated
   // to CONTENT_SETTING_DEFAULT). It's not possible to reconstruct the actual
diff --git a/chrome/browser/webid/federated_identity_account_keyed_permission_context_unittest.cc b/chrome/browser/webid/federated_identity_account_keyed_permission_context_unittest.cc
index b2645cf..a1a7361 100644
--- a/chrome/browser/webid/federated_identity_account_keyed_permission_context_unittest.cc
+++ b/chrome/browser/webid/federated_identity_account_keyed_permission_context_unittest.cc
@@ -381,30 +381,7 @@
 }
 
 TEST_F(FederatedIdentityAccountKeyedPermissionContextTest,
-       GetSharingPermissionGrantsAsContentSettings_FeatureDisabled) {
-  base::test::ScopedFeatureList features;
-  features.InitAndDisableFeature(blink::features::kFedCmWithStorageAccessAPI);
-  const url::Origin relying_party_requester =
-      url::Origin::Create(GURL("https://www.relying_party_requester.com"));
-  const url::Origin relying_party_embedder =
-      url::Origin::Create(GURL("https://www.relying_party_embedder.com"));
-  const url::Origin identity_provider =
-      url::Origin::Create(GURL("https://www.identity_provider.com"));
-
-  context()->GrantPermission(relying_party_requester, relying_party_embedder,
-                             identity_provider, "my_account");
-  ASSERT_TRUE(
-      context()->HasPermission(net::SchemefulSite(relying_party_embedder),
-                               net::SchemefulSite(identity_provider)));
-
-  EXPECT_THAT(context()->GetSharingPermissionGrantsAsContentSettings(),
-              IsEmpty());
-}
-
-TEST_F(FederatedIdentityAccountKeyedPermissionContextTest,
-       GetSharingPermissionGrantsAsContentSettings_FeatureEnabled) {
-  base::test::ScopedFeatureList features;
-  features.InitAndEnableFeature(blink::features::kFedCmWithStorageAccessAPI);
+       GetSharingPermissionGrantsAsContentSettings) {
   const url::Origin relying_party_requester =
       url::Origin::Create(GURL("https://www.relying_party_requester.com"));
   const url::Origin relying_party_embedder =
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 26cc3c53..c6a3ea8 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1746596669-ef8f8aa2940bd8e3489db976acee09a6c6c0bd5c-f962687515b7bc001bf4380904b6da50ba8bce93.profdata
+chrome-android32-main-1746619131-b5f119ad34729c39bb8db97e934147dbaff50605-413ccd12abe49ad79521af08e38c1208cc19f44a.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index b15487c..a257ad1 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1746610755-301f078e07fffda94a9613609e7b8e51d71e59e8-cd85033399f79ffaf7515e015ebc8e4efd6e7946.profdata
+chrome-android64-main-1746631188-eb1d0ec979dc5cad49b7f79248f80823f6ca3248-9158358aef7fc7a0f62b5f98b94c2f09b8e14b32.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index b155f474..8bb18b5 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1746596669-47b28193f25eb9fc6cde369697a0eead37c766dc-f962687515b7bc001bf4380904b6da50ba8bce93.profdata
+chrome-linux-main-1746619131-0479134592bfcf16abb87046cc181dafa4dca288-413ccd12abe49ad79521af08e38c1208cc19f44a.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index f8e42e4..09b1233 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1746611847-a37b7979e47bc6116f862351c4c6942108e07406-796016156828373f03c583de4d0cea2e8338a96e.profdata
+chrome-mac-arm-main-1746633553-c187962b7e6e4b34c494431fc424b4ae91d00bf2-27ce3c2b779fe727f5ed1e7982fcc356a9b10b7f.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 945449c..97d963af 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1746596669-a41951026aaaa090d4e91390aba9925bceb0d77b-f962687515b7bc001bf4380904b6da50ba8bce93.profdata
+chrome-mac-main-1746619131-f0689b7c453bf66428fd1cec9c04c8fc387974a9-413ccd12abe49ad79521af08e38c1208cc19f44a.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index ccc92e09..8bdecb5 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1746596669-f63224fa8420683ad9dbd4186e6b4f4157627982-f962687515b7bc001bf4380904b6da50ba8bce93.profdata
+chrome-win-arm64-main-1746619131-846a6c3eac1af9e8d77dfc02e863a0f37f6238c6-413ccd12abe49ad79521af08e38c1208cc19f44a.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 42c5814..ff835751 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1746596669-5d433969caa89462c395d3b15c51a26d637b1aff-f962687515b7bc001bf4380904b6da50ba8bce93.profdata
+chrome-win32-main-1746608342-d334b0886e62903e6e2e566b2cb4cd02de0e6fdf-deb8d871c2591efece685fe2695903c61259ee06.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 6942990..4f6f07a 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1746575735-abdf678ab1bebcfdc0d37b724b37f63e82d7e6c1-3e7a899eb30f7f372790ede75edc6f47fbedf024.profdata
+chrome-win64-main-1746586632-f8dce42004d63c7802f1074010b3c7c9b792793d-f351d8b201937d9402701c01bb3ba07eb2001ae0.profdata
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index dc3255d..e96d83c 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -317,6 +317,7 @@
         "$root_gen_dir/chrome/emoji_picker_resources.pak",
         "$root_gen_dir/chrome/enterprise_reporting_resources.pak",
         "$root_gen_dir/chrome/extended_updates_resources.pak",
+        "$root_gen_dir/chrome/floating_workspace_resources.pak",
         "$root_gen_dir/chrome/gaia_action_buttons_resources.pak",
         "$root_gen_dir/chrome/healthd_internals_resources.pak",
         "$root_gen_dir/chrome/internet_config_dialog_resources.pak",
@@ -408,6 +409,7 @@
         "//ash/wm/overview/birch/resources:coral_resources",
         "//chrome/browser/resources:office_web_app_resources",
         "//chrome/browser/resources/chromeos/dlp_internals:resources",
+        "//chrome/browser/resources/chromeos/floating_workspace:resources",
         "//chrome/browser/resources/chromeos/kerberos:resources",
         "//chrome/common/chromeos/extensions:resources",
         "//chromeos/ash/components/emoji:resources",
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 2819822..8dde0f01 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -887,20 +887,6 @@
              base::FEATURE_ENABLED_BY_DEFAULT);
 #endif  // BUILDFLAG(IS_MAC)
 
-#if BUILDFLAG(IS_CHROMEOS)
-// Enables scraping of password-expiry information during SAML login flow, which
-// can lead to an in-session flow for changing SAML password if it has expired.
-// This is safe to enable by default since it does not cause the password-expiry
-// information to be stored, or any user-visible change - in order for anything
-// to happen, the domain administrator has to intentionally send this extra
-// info in the SAML response, and enable the InSessionPasswordChange policy.
-// So, this feature is just for disabling the scraping code if it causes
-// any unforeseen issues.
-BASE_FEATURE(kInSessionPasswordChange,
-             "InSessionPasswordChange",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-#endif  // BUILDFLAG(IS_CHROMEOS)
-
 #if BUILDFLAG(IS_WIN)
 // A feature that controls whether Chrome warns about incompatible applications.
 // This feature requires Windows 10 or higher to work because it depends on
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 831c8241..fbb8f4c 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -512,11 +512,6 @@
 COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kFullscreenAnimateTabs);
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS)
-COMPONENT_EXPORT(CHROME_FEATURES)
-BASE_DECLARE_FEATURE(kInSessionPasswordChange);
-#endif
-
 #if BUILDFLAG(IS_WIN)
 // Only has an effect in branded builds.
 COMPONENT_EXPORT(CHROME_FEATURES)
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 912191f8..ab5e819 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -4259,6 +4259,11 @@
 // is set as a SharedWorker script URL.
 inline constexpr char kSharedWorkerBlobURLFixEnabled[] =
     "worker.shared_worker_blob_url_fix_enabled";
+
+// Boolean indicating whether clearing window.name when the navigation is
+// top-level, cross-site and swaps BrowsingContextGroup is allowed or not.
+inline constexpr char kClearWindowNameForNewBrowsingContextGroup[] =
+    "profile.content_settings.clear_window_name_for_new_browsing_context_group";
 }  // namespace prefs
 
 #endif  // CHROME_COMMON_PREF_NAMES_H_
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc b/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc
index c066a2ac..68bc30d 100644
--- a/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc
+++ b/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc
@@ -721,6 +721,14 @@
 }
 
 void ReadAnythingAppModel::SetActiveTreeId(ui::AXTreeID active_tree_id) {
+  // Unserialize any updates on the previous active tree;
+  // Otherwise, this can cause tree inconsistency issues if reading mode later
+  // incorrectly receives updates from the old tree.
+  if (active_tree_id_ != active_tree_id &&
+      active_tree_id_ != ui::AXTreeIDUnknown() && ContainsActiveTree()) {
+    UnserializePendingUpdates(active_tree_id_);
+  }
+
   active_tree_id_ = std::move(active_tree_id);
   // If data collection mode for screen2x is enabled, begin
   // `timer_since_page_load_for_data_collection_` from here. This is a
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index b897c99f..208c9cef 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1345,6 +1345,7 @@
     "../browser/metrics/server_urls_browsertest.cc",
     "../browser/metrics/startup_metrics_browsertest.cc",
     "../browser/metrics/tab_stats/tab_stats_tracker_browsertest.cc",
+    "../browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc",
     "../browser/net/cert_verifier_service_browsertest.cc",
     "../browser/net/cert_verify_proc_browsertest.cc",
     "../browser/policy/policy_prefs_browsertest.cc",
@@ -3124,7 +3125,6 @@
       "../browser/metrics/process_memory_metrics_emitter_browsertest.cc",
       "../browser/metrics/ukm_background_recorder_browsertest.cc",
       "../browser/metrics/ukm_browsertest.cc",
-      "../browser/metrics/usage_scenario/tab_usage_scenario_tracker_browsertest.cc",
       "../browser/metrics/variations/field_trials_on_off_browsertest.cc",
       "../browser/metrics/variations/force_field_trials_browsertest.cc",
       "../browser/metrics/variations/limited_entropy_randomization_browsertest.cc",
@@ -6127,6 +6127,10 @@
     "../browser/metrics/tab_footprint_aggregator_unittest.cc",
     "../browser/metrics/tab_stats/tab_stats_data_store_unittest.cc",
     "../browser/metrics/tab_stats/tab_stats_tracker_unittest.cc",
+    "../browser/metrics/usage_scenario/system_event_provider_unittest.cc",
+    "../browser/metrics/usage_scenario/tab_usage_scenario_tracker_unittest.cc",
+    "../browser/metrics/usage_scenario/usage_scenario_data_store_unittest.cc",
+    "../browser/metrics/usage_scenario/usage_scenario_unittest.cc",
     "../browser/metrics/variations/chrome_variations_service_client_unittest.cc",
     "../browser/navigation_predictor/navigation_predictor_unittest.cc",
     "../browser/net/cert_verifier_policy_unittest.cc",
@@ -6938,6 +6942,7 @@
     "//components/subresource_filter/core/browser:test_support",
     "//components/supervised_user/core/browser",
     "//components/supervised_user/core/browser:fetcher",
+    "//components/supervised_user/core/browser:test_support",
     "//components/supervised_user/core/browser/proto",
     "//components/supervised_user/core/common",
     "//components/supervised_user/core/common",
@@ -7157,7 +7162,6 @@
       "../browser/task_manager/sampling/shared_sampler_win_unittest.cc",
       "../browser/tracing/windows_system_tracing_client_impl_unittest_win.cc",
       "../browser/ui/startup/credential_provider_signin_info_fetcher_win_unittest.cc",
-      "../browser/ui/startup/installer_downloader/installer_downloader_infobar_delegate_unittest.cc",
       "../browser/ui/views/frame/windows_caption_button_unittest.cc",
       "../browser/ui/views/uninstall_view_unittest.cc",
       "../browser/ui/webui/version/version_handler_win_unittest.cc",
@@ -7246,6 +7250,7 @@
     if (is_chrome_branded) {
       sources += [
         "../browser/component_updater/recovery_improved_component_unittest.cc",
+        "../browser/ui/startup/installer_downloader/installer_downloader_infobar_delegate_unittest.cc",
       ]
 
       data_deps +=
@@ -7735,10 +7740,6 @@
       "../browser/metrics/power/power_metrics_unittest.cc",
       "../browser/metrics/power/process_monitor_unittest.cc",
       "../browser/metrics/usage_scenario/chrome_responsiveness_calculator_delegate_unittest.cc",
-      "../browser/metrics/usage_scenario/system_event_provider_unittest.cc",
-      "../browser/metrics/usage_scenario/tab_usage_scenario_tracker_unittest.cc",
-      "../browser/metrics/usage_scenario/usage_scenario_data_store_unittest.cc",
-      "../browser/metrics/usage_scenario/usage_scenario_unittest.cc",
       "../browser/new_tab_page/one_google_bar/one_google_bar_loader_impl_unittest.cc",
       "../browser/new_tab_page/one_google_bar/one_google_bar_service_unittest.cc",
       "../browser/new_tab_page/promos/promo_service_unittest.cc",
@@ -10869,7 +10870,6 @@
       "../browser/password_manager/password_user_education_interactive_uitest.cc",
       "../browser/permissions/one_time_permissions_tracker_helper_interactive_uitest.cc",
       "../browser/preloading/prerender/prerender_omnibox_ui_browsertest.cc",
-      "../browser/privacy_sandbox/browsing_topics_settings_interactive_uitest.cc",
       "../browser/renderer_context_menu/link_to_text_menu_observer_interactive_uitest.cc",
       "../browser/renderer_context_menu/render_view_context_menu_browsertest_util.cc",
       "../browser/renderer_context_menu/render_view_context_menu_browsertest_util.h",
@@ -11018,16 +11018,12 @@
       "//chrome/browser/policy:test_support",
       "//chrome/browser/prefs",
       "//chrome/browser/prefs:util",
+      "//chrome/browser/privacy_sandbox:interactive_ui_tests",
 
       # TODO(413315837): Remove this dependency when
       # c/b/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_interactive_ui_test.cc
       # gets modularized.
       "//chrome/browser/privacy_sandbox:test_support",
-      "//chrome/browser/ui/privacy_sandbox",
-
-      # TODO(413315837): Remove this dep when browsing_topics_settings_interactive_uitest.cc
-      # gets build off of c/b/privacy_sandbox/BUILD.gn.
-      "//chrome/browser/privacy_sandbox:headers",
       "//chrome/browser/search",
       "//chrome/browser/search_engines",
       "//chrome/browser/signin",
@@ -11054,6 +11050,7 @@
       "//chrome/browser/ui/lens:interactive_ui_tests",
       "//chrome/browser/ui/omnibox",
       "//chrome/browser/ui/page_action:icon_type",
+      "//chrome/browser/ui/privacy_sandbox",
       "//chrome/browser/ui/startup:startup_tab",
       "//chrome/browser/ui/tab_contents",
       "//chrome/browser/ui/tabs:tab_strip",
@@ -11386,6 +11383,7 @@
         "../browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc",
         "../browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.h",
         "../browser/ui/views/tabs/recent_activity_bubble_dialog_view_interactive_uitest.cc",
+        "../browser/ui/views/tabs/tab_group_header_interactive_uitest.cc",
         "../browser/ui/views/tabs/tab_hover_card_controller_interactive_uitest.cc",
         "../browser/ui/views/tabs/tab_strip_interactive_uitest.cc",
         "../browser/ui/views/test/view_event_test_base.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java
index 7ba9333..0ce4253 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/suggestions/mostvisited/FakeMostVisitedSites.java
@@ -58,6 +58,12 @@
         return false;
     }
 
+    @Override
+    public boolean reorderCustomLink(GURL keyUrl, int newPos) {
+        // TODO (crbug.com/397421764): Implement when needed by tests.
+        return false;
+    }
+
     // MostVisitedSites implementation.
     @Override
     public void destroy() {}
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index bfc9b67..b641d9b 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -5,6 +5,8 @@
 #include "chrome/test/base/testing_profile.h"
 
 #include <memory>
+#include <string>
+#include <string_view>
 #include <utility>
 #include <variant>
 
@@ -83,6 +85,8 @@
 #include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/identity_test_utils.h"
+#include "components/supervised_user/core/browser/supervised_user_pref_store.h"
+#include "components/supervised_user/core/browser/supervised_user_preferences.h"
 #include "components/supervised_user/core/browser/supervised_user_settings_service.h"
 #include "components/supervised_user/core/common/pref_names.h"
 #include "components/supervised_user/core/common/supervised_user_constants.h"
@@ -140,12 +144,49 @@
 #include "components/account_manager_core/chromeos/account_manager.h"
 #endif
 
+namespace {
 using base::Time;
 using content::BrowserThread;
 using content::DownloadManagerDelegate;
 using testing::NiceMock;
 using testing::Return;
 
+// Just like SupervisedUserPrefStore. The difference is that
+// SupervisedUserPrefStore does not offer TestingPrefStore interface, but this
+// one does (by actually wrapping SupervisedUserPrefStore).
+class SupervisedUserTestingPrefStore : public TestingPrefStore,
+                                       public PrefStore::Observer {
+ public:
+  explicit SupervisedUserTestingPrefStore(
+      supervised_user::SupervisedUserSettingsService* settings_service)
+      : pref_store_(
+            base::MakeRefCounted<SupervisedUserPrefStore>(settings_service)) {
+    observation_.Observe(pref_store_.get());
+  }
+
+ private:
+  ~SupervisedUserTestingPrefStore() override = default;
+
+  void OnPrefValueChanged(std::string_view key) override {
+    const base::Value* value = nullptr;
+    // Flags are ignored in the TestingPrefStore.
+    if (pref_store_->GetValue(key, &value)) {
+      SetValue(key, value->Clone(), /*flags=*/0);
+    } else {
+      RemoveValue(key, /*flags=*/0);
+    }
+  }
+
+  void OnInitializationCompleted(bool succeeded) override {
+    CHECK(succeeded) << "During tests initialization must succeed";
+    SetInitializationCompleted();
+  }
+
+  scoped_refptr<PrefStore> pref_store_;
+  base::ScopedObservation<PrefStore, PrefStore::Observer> observation_{this};
+};
+}  // namespace
+
 TestingProfile::TestingFactory::TestingFactory(
     BrowserContextKeyedServiceFactory* service_factory,
     BrowserContextKeyedServiceFactory::TestingFactory testing_factory)
@@ -357,14 +398,18 @@
   if (!IsOffTheRecord()) {
     supervised_user::SupervisedUserSettingsService* settings_service =
         SupervisedUserSettingsServiceFactory::GetForKey(key_.get());
-    supervised_user_pref_store_ = base::MakeRefCounted<TestingPrefStore>();
-    settings_service->Init(supervised_user_pref_store_.get());
+
+    // Note: this pref store is not a part of any pref service, but rather a
+    // convenient storage backend of the supervised user settings service.
+    scoped_refptr<TestingPrefStore> supervised_user_backing_pref_store =
+        base::MakeRefCounted<TestingPrefStore>();
+    supervised_user_backing_pref_store->SetInitializationCompleted();
+
+    settings_service->Init(supervised_user_backing_pref_store);
     settings_service->MergeDataAndStartSyncing(
         syncer::SUPERVISED_USER_SETTINGS, syncer::SyncDataList(),
         std::unique_ptr<syncer::SyncChangeProcessor>(
             new syncer::FakeSyncChangeProcessor));
-
-    supervised_user_pref_store_->SetInitializationCompleted();
   }
 
   if (prefs_.get())
@@ -696,16 +741,14 @@
 
 void TestingProfile::SetIsSupervisedProfile(bool is_supervised_profile) {
   if (is_supervised_profile) {
-    GetPrefs()->SetString(prefs::kSupervisedUserId,
-                          supervised_user::kChildAccountSUID);
+    supervised_user::EnableParentalControls(*GetPrefs());
   } else {
-    GetPrefs()->ClearPref(prefs::kSupervisedUserId);
+    supervised_user::DisableParentalControls(*GetPrefs());
   }
 }
 
 bool TestingProfile::IsChild() const {
-  return GetPrefs()->GetString(prefs::kSupervisedUserId) ==
-         supervised_user::kChildAccountSUID;
+  return supervised_user::IsSubjectToParentalControls(*GetPrefs());
 }
 
 bool TestingProfile::AllowsBrowserWindows() const {
@@ -748,7 +791,9 @@
   // Construct testing_prefs_ by hand to add the supervised user pref store.
   testing_prefs_ = new sync_preferences::TestingPrefServiceSyncable(
       /*managed_prefs=*/base::MakeRefCounted<TestingPrefStore>(),
-      supervised_user_pref_store_,
+      /*supervised_user_prefs=*/
+      base::MakeRefCounted<SupervisedUserTestingPrefStore>(
+          SupervisedUserSettingsServiceFactory::GetForKey(key_.get())),
       /*extension_prefs=*/base::MakeRefCounted<TestingPrefStore>(),
       /*user_prefs=*/base::MakeRefCounted<TestingPrefStore>(),
       /*recommended_prefs=*/base::MakeRefCounted<TestingPrefStore>(),
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index 17b8b2e1..53fd953 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -560,8 +560,6 @@
 
   std::unique_ptr<policy::PolicyService> policy_service_;
 
-  scoped_refptr<TestingPrefStore> supervised_user_pref_store_ = nullptr;
-
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 };
 
diff --git a/chrome/test/data/pdf/ink2_annotation_text_mixin_test.ts b/chrome/test/data/pdf/ink2_annotation_text_mixin_test.ts
index 14e0bbe..577afa3 100644
--- a/chrome/test/data/pdf/ink2_annotation_text_mixin_test.ts
+++ b/chrome/test/data/pdf/ink2_annotation_text_mixin_test.ts
@@ -107,8 +107,6 @@
       styles: {
         [TextStyle.BOLD]: false,
         [TextStyle.ITALIC]: false,
-        [TextStyle.UNDERLINE]: false,
-        [TextStyle.STRIKETHROUGH]: false,
       },
     });
     assertDeepEquals(newColor, testElement.currentColor);
diff --git a/chrome/test/data/pdf/ink2_manager_test.ts b/chrome/test/data/pdf/ink2_manager_test.ts
index ec95d41..11cf6a6c 100644
--- a/chrome/test/data/pdf/ink2_manager_test.ts
+++ b/chrome/test/data/pdf/ink2_manager_test.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import type {AnnotationBrush, TextAnnotation, TextAttributes, TextBoxInit} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
-import {AnnotationBrushType, DEFAULT_TEXTBOX_HEIGHT, DEFAULT_TEXTBOX_WIDTH, Ink2Manager, TextAlignment} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {AnnotationBrushType, DEFAULT_TEXTBOX_HEIGHT, DEFAULT_TEXTBOX_WIDTH, Ink2Manager, PluginController, PluginControllerEventType, TextAlignment} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
@@ -22,8 +22,6 @@
       styles: {
         bold: false,
         italic: false,
-        underline: false,
-        strikethrough: true,
       },
     },
     text: 'Hello World',
@@ -175,8 +173,6 @@
       styles: {
         bold: false,
         italic: false,
-        underline: false,
-        strikethrough: false,
       },
     };
     assertTextUpdate(0, expectedAttributes);
@@ -198,8 +194,7 @@
     assertTextUpdate(3, expectedAttributes);
 
     // Set style to bold + italic.
-    const boldItalic =
-        {bold: true, italic: true, underline: false, strikethrough: false};
+    const boldItalic = {bold: true, italic: true};
     manager.setTextStyles(boldItalic);
     expectedAttributes.styles = boldItalic;
     assertTextUpdate(4, expectedAttributes);
@@ -252,17 +247,38 @@
   },
 
   function testCommitTextAnnotation() {
-    manager.commitTextAnnotation(getTestAnnotation());
-    const finishTextAnnotationMessage =
-        mockPlugin.findMessage('finishTextAnnotation');
-    chrome.test.assertTrue(finishTextAnnotationMessage !== undefined);
-    chrome.test.assertEq(
-        'finishTextAnnotation', finishTextAnnotationMessage.type);
+    // Listen for PluginControllerEventType.FINISH_INK_STROKE events. The
+    // manager dispatches these on PluginController's eventTarget.
+    let finishInkStrokeEvents = 0;
+    PluginController.getInstance().getEventTarget().addEventListener(
+        PluginControllerEventType.FINISH_INK_STROKE, () => {
+          finishInkStrokeEvents++;
+        });
+
     const annotationPageCoords = getTestAnnotation();
     // Adjust by the x and y offsets to get to page coordinates.
     annotationPageCoords.textBoxRect.locationX = 5;
     annotationPageCoords.textBoxRect.locationY = 22;
-    assertDeepEquals(annotationPageCoords, finishTextAnnotationMessage.data);
+    function verifyFinishTextAnnotationMessage() {
+      const finishTextAnnotationMessage =
+          mockPlugin.findMessage('finishTextAnnotation');
+      chrome.test.assertTrue(finishTextAnnotationMessage !== undefined);
+      chrome.test.assertEq(
+          'finishTextAnnotation', finishTextAnnotationMessage.type);
+      assertDeepEquals(annotationPageCoords, finishTextAnnotationMessage.data);
+    }
+
+    // Committing with edited = true should fire an event.
+    manager.commitTextAnnotation(getTestAnnotation(), true);
+    chrome.test.assertEq(1, finishInkStrokeEvents);
+    verifyFinishTextAnnotationMessage();
+    mockPlugin.clearMessages();
+
+    // Committing with edited = false should not fire an event.
+    manager.commitTextAnnotation(getTestAnnotation(), false);
+    chrome.test.assertEq(1, finishInkStrokeEvents);
+    verifyFinishTextAnnotationMessage();
+
     chrome.test.succeed();
   },
 
diff --git a/chrome/test/data/pdf/ink2_save_test.ts b/chrome/test/data/pdf/ink2_save_test.ts
index 74d5e7a1..e71633d 100644
--- a/chrome/test/data/pdf/ink2_save_test.ts
+++ b/chrome/test/data/pdf/ink2_save_test.ts
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {AnnotationMode, PluginController, SaveRequestType, UserAction} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {AnnotationMode, PluginController, PluginControllerEventType, SaveRequestType, UserAction} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import {assert} from 'chrome://resources/js/assert.js';
-import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
+import {eventToPromise, isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
-import {finishInkStroke, getRequiredElement, setupMockMetricsPrivate, setupTestMockPluginForInk} from './test_util.js';
+import {createTextBox, finishInkStroke, getRequiredElement, setupMockMetricsPrivate, setupTestMockPluginForInk} from './test_util.js';
 
 const viewer = document.body.querySelector('pdf-viewer')!;
 const viewerToolbar = viewer.$.toolbar;
@@ -253,4 +254,151 @@
     mockMetricsPrivate.assertCount(UserAction.SAVE_ORIGINAL_ONLY, 1);
     chrome.test.succeed();
   },
+
+  // Tests that while in text annotation mode, on a PDF without any edits,
+  // clicking the download button will save the PDF as original, even if
+  // a textbox is open.
+  async function testSaveOriginalInTextMode() {
+    mockPlugin.clearMessages();
+    mockMetricsPrivate.reset();
+    loadTimeData.overrideValues({'pdfTextAnnotationsEnabled': true});
+    viewerToolbar.strings = Object.assign({}, viewerToolbar.strings);
+    await microtasksFinished();
+
+    viewerToolbar.setAnnotationMode(AnnotationMode.TEXT);
+    await microtasksFinished();
+    chrome.test.assertEq(AnnotationMode.TEXT, viewerToolbar.annotationMode);
+    createTextBox();
+    await microtasksFinished();
+    const textbox = viewer.shadowRoot.querySelector('ink-text-box');
+    assert(textbox);
+    chrome.test.assertTrue(isVisible(textbox));
+
+    const downloadControls = getDownloadControls();
+    const downloadButton = downloadControls.$.download;
+    const actionMenu = downloadControls.$.menu;
+    chrome.test.assertFalse(actionMenu.open);
+
+    downloadButton.click();
+
+    // A message should be sent to the plugin to save as original.
+    await eventToPromise('save', viewer);
+    const saveMessage = mockPlugin.findMessage('save');
+    chrome.test.assertTrue(saveMessage !== undefined);
+    chrome.test.assertEq(saveMessage.saveRequestType, SaveRequestType.ORIGINAL);
+    chrome.test.assertFalse(actionMenu.open);
+    chrome.test.assertEq(AnnotationMode.TEXT, viewerToolbar.annotationMode);
+    mockMetricsPrivate.assertCount(UserAction.SAVE_ORIGINAL_ONLY, 1);
+
+    // Textbox should be hidden.
+    chrome.test.assertFalse(isVisible(textbox));
+    chrome.test.succeed();
+  },
+
+  // Tests that while in text annotation mode, after editing an annotation,
+  // clicking the download button will prompt the download menu.
+  async function testSaveMenuWithTextBoxOpen() {
+    mockPlugin.clearMessages();
+    mockMetricsPrivate.reset();
+
+    const downloadControls = getDownloadControls();
+    const actionMenu = downloadControls.$.menu;
+
+    chrome.test.assertFalse(actionMenu.open);
+
+    createTextBox();
+    await microtasksFinished();
+    const textbox = viewer.shadowRoot.querySelector('ink-text-box');
+    assert(textbox);
+    chrome.test.assertTrue(isVisible(textbox));
+    textbox.$.textbox.value = 'Hello';
+    textbox.$.textbox.dispatchEvent(new CustomEvent('input'));
+    await microtasksFinished();
+
+    downloadControls.$.download.click();
+
+    await testSaveWithAnnotations();
+    // Textbox is closed and annotation is committed.
+    chrome.test.assertFalse(isVisible(textbox));
+    // The finishTextAnnotation message should have been sent before save.
+    const saveIndex =
+        mockPlugin.messages.findIndex(message => message.type === 'save');
+    const setTextIndex = mockPlugin.messages.findIndex(
+        message => message.type === 'finishTextAnnotation');
+    chrome.test.assertFalse(setTextIndex === -1);
+    chrome.test.assertTrue(setTextIndex < saveIndex);
+    mockMetricsPrivate.assertCount(UserAction.SAVE_WITH_INK2_ANNOTATION, 1);
+    chrome.test.succeed();
+  },
+
+  // Tests that while in text annotation mode, after undoing all edits on the
+  // PDF, clicking the download button will save the PDF as original.
+  async function testSaveOriginalAfterFullUndoText() {
+    mockPlugin.clearMessages();
+    mockMetricsPrivate.reset();
+
+    // Add another annotation. There should now be 2 text annotations on the
+    // PDF.
+    PluginController.getInstance().getEventTarget().dispatchEvent(
+        new CustomEvent(PluginControllerEventType.FINISH_INK_STROKE));
+    await microtasksFinished();
+
+    const undoButton =
+        getRequiredElement<HTMLButtonElement>(viewerToolbar, '#undo');
+    chrome.test.assertFalse(undoButton.disabled);
+
+    // Undo both text annotations.
+    undoButton.click();
+    undoButton.click();
+    await microtasksFinished();
+
+    // There shouldn't be any ink strokes or text annotations on the PDF.
+    chrome.test.assertTrue(undoButton.disabled);
+
+    const downloadControls = getDownloadControls();
+    const downloadButton = downloadControls.$.download;
+    const actionMenu = downloadControls.$.menu;
+    actionMenu.close();
+
+    downloadButton.click();
+
+    // A message should be sent to the plugin to save as original.
+    await eventToPromise('save', viewer);
+    const saveMessage = mockPlugin.findMessage('save');
+    chrome.test.assertTrue(saveMessage !== undefined);
+    chrome.test.assertEq(saveMessage.saveRequestType, SaveRequestType.ORIGINAL);
+    chrome.test.assertFalse(actionMenu.open);
+    mockMetricsPrivate.assertCount(UserAction.SAVE_ORIGINAL_ONLY, 1);
+    chrome.test.succeed();
+  },
+
+  // A redo operation will add the text annotation back to the PDF. Tests that
+  // while in annotation mode, after a redo operation, clicking the download
+  // button will prompt the download menu.
+  async function testSaveMenuAfterRedoText() {
+    mockPlugin.clearMessages();
+    mockMetricsPrivate.reset();
+    // Set a reply for the save message that will bypass opening a file dialog
+    // and saving the data to disk.
+    mockPlugin.setReplyToSave(true);
+
+    const redoButton =
+        getRequiredElement<HTMLButtonElement>(viewerToolbar, '#redo');
+    chrome.test.assertFalse(redoButton.disabled);
+
+    const downloadControls = getDownloadControls();
+    const downloadButton = downloadControls.$.download;
+
+    redoButton.click();
+    await microtasksFinished();
+    mockMetricsPrivate.assertCount(UserAction.SAVE_WITH_INK2_ANNOTATION, 0);
+
+    downloadButton.click();
+    await testSaveWithAnnotations();
+    mockMetricsPrivate.assertCount(UserAction.SAVE_WITH_INK2_ANNOTATION, 1);
+
+    // The test should be able to successfully exit, as PDF Viewer should have
+    // turned off the beforeunload dialog after the successful save.
+    chrome.test.succeed();
+  },
 ]);
diff --git a/chrome/test/data/pdf/ink2_text_box_test.ts b/chrome/test/data/pdf/ink2_text_box_test.ts
index 7405097..cc8edb8 100644
--- a/chrome/test/data/pdf/ink2_text_box_test.ts
+++ b/chrome/test/data/pdf/ink2_text_box_test.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {hexToColor, Ink2Manager, TEXT_COLORS, TextAlignment, TextStyle} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {hexToColor, Ink2Manager, PluginController, PluginControllerEventType, TEXT_COLORS, TextAlignment, TextBoxState, TextStyle} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import type {TextAnnotation} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import {isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
@@ -28,8 +28,6 @@
           styles: {
             [TextStyle.BOLD]: false,
             [TextStyle.ITALIC]: false,
-            [TextStyle.UNDERLINE]: false,
-            [TextStyle.STRIKETHROUGH]: false,
           },
           alignment: TextAlignment.LEFT,
           color: hexToColor(TEXT_COLORS[0]!.color),
@@ -142,15 +140,11 @@
     manager.setTextStyles({
       [TextStyle.BOLD]: true,
       [TextStyle.ITALIC]: true,
-      [TextStyle.UNDERLINE]: true,
-      [TextStyle.STRIKETHROUGH]: false,
     });
     await microtasksFinished();
     chrome.test.assertEq('700', textboxStyles.getPropertyValue('font-weight'));
     chrome.test.assertEq(
         'italic', textboxStyles.getPropertyValue('font-style'));
-    chrome.test.assertTrue(textboxStyles.getPropertyValue('text-decoration')
-                               .includes('underline'));
 
     // Color
     const newColor = hexToColor(TEXT_COLORS[1]!.color);
@@ -170,8 +164,6 @@
     manager.setTextStyles({
       [TextStyle.BOLD]: false,
       [TextStyle.ITALIC]: false,
-      [TextStyle.UNDERLINE]: false,
-      [TextStyle.STRIKETHROUGH]: false,
     });
     manager.setTextColor(hexToColor(TEXT_COLORS[0]!.color));
     manager.setTextAlignment(TextAlignment.LEFT);
@@ -415,8 +407,6 @@
         styles: {
           [TextStyle.BOLD]: false,
           [TextStyle.ITALIC]: false,
-          [TextStyle.UNDERLINE]: false,
-          [TextStyle.STRIKETHROUGH]: false,
         },
         alignment: TextAlignment.LEFT,
         color: hexToColor(TEXT_COLORS[0]!.color),
@@ -511,4 +501,73 @@
 
     chrome.test.succeed();
   },
+
+  async function testCloseAndEvents() {
+    let textBoxStates: TextBoxState[] = [];
+    textbox.addEventListener('state-changed', e => {
+      textBoxStates.push((e as CustomEvent<TextBoxState>).detail);
+    });
+
+    let finishInkStrokeEvents = 0;
+    PluginController.getInstance().getEventTarget().addEventListener(
+        PluginControllerEventType.FINISH_INK_STROKE, () => {
+          finishInkStrokeEvents++;
+        });
+
+    // Initialize to a 100x100 box at 400, 300.
+    initializeBox(100, 100, 400, 300);
+    await microtasksFinished();
+    chrome.test.assertTrue(isVisible(textbox));
+    assertDeepEquals([TextBoxState.NEW], textBoxStates);
+
+    // When a new box has no edits, commitTextAnnotation() will not trigger a
+    // plugin message or a PluginControllerEventType.FINISH_INK_STROKE event.
+    mockPlugin.clearMessages();
+    textbox.commitTextAnnotation();
+    await microtasksFinished();
+    chrome.test.assertFalse(isVisible(textbox));
+    chrome.test.assertEq(
+        undefined, mockPlugin.findMessage('finishTextAnnotation'));
+    chrome.test.assertEq(0, finishInkStrokeEvents);
+    assertDeepEquals([TextBoxState.NEW, TextBoxState.INACTIVE], textBoxStates);
+
+    // When text is edited, commitTextAnnotation() will trigger a plugin message
+    // and a PluginControllerEventType.FINISH_INK_STROKE event.
+    textBoxStates = [];
+    initializeBox(100, 100, 400, 300);
+    await microtasksFinished();
+    chrome.test.assertTrue(isVisible(textbox));
+    assertDeepEquals([TextBoxState.NEW], textBoxStates);
+    textbox.$.textbox.value = 'Hello';
+    textbox.$.textbox.dispatchEvent(new CustomEvent('input'));
+    await microtasksFinished();
+    assertDeepEquals([TextBoxState.NEW, TextBoxState.EDITED], textBoxStates);
+
+    textbox.commitTextAnnotation();
+    await microtasksFinished();
+    chrome.test.assertFalse(isVisible(textbox));
+    chrome.test.assertTrue(
+        mockPlugin.findMessage('finishTextAnnotation') !== undefined);
+    chrome.test.assertEq(1, finishInkStrokeEvents);
+    assertDeepEquals(
+        [TextBoxState.NEW, TextBoxState.EDITED, TextBoxState.INACTIVE],
+        textBoxStates);
+
+    // When existing text is not edited, commitTextAnnotation() will trigger a
+    // plugin message but no PluginControllerEventType.FINISH_INK_STROKE event.
+    textBoxStates = [];
+    initializeBox(100, 100, 400, 300, true);
+    await microtasksFinished();
+    chrome.test.assertTrue(isVisible(textbox));
+    assertDeepEquals([TextBoxState.NEW], textBoxStates);
+    textbox.commitTextAnnotation();
+    await microtasksFinished();
+    chrome.test.assertFalse(isVisible(textbox));
+    chrome.test.assertTrue(
+        mockPlugin.findMessage('finishTextAnnotation') !== undefined);
+    chrome.test.assertEq(1, finishInkStrokeEvents);
+    assertDeepEquals([TextBoxState.NEW, TextBoxState.INACTIVE], textBoxStates);
+
+    chrome.test.succeed();
+  },
 ]);
diff --git a/chrome/test/data/pdf/ink2_text_styles_selector_test.ts b/chrome/test/data/pdf/ink2_text_styles_selector_test.ts
index 6afa4d81..bf33a116 100644
--- a/chrome/test/data/pdf/ink2_text_styles_selector_test.ts
+++ b/chrome/test/data/pdf/ink2_text_styles_selector_test.ts
@@ -41,15 +41,10 @@
     // For each button, check that it can be toggled and confirm it is
     // displaying the expected icon.
     const buttons = styleSelector.shadowRoot.querySelectorAll('cr-icon-button');
-    chrome.test.assertEq(4, buttons.length);
+    chrome.test.assertEq(2, buttons.length);
     await testButton(buttons[0]!, TextStyle.BOLD, 'pdf-ink:text-format-bold');
     await testButton(
         buttons[1]!, TextStyle.ITALIC, 'pdf-ink:text-format-italic');
-    await testButton(
-        buttons[2]!, TextStyle.UNDERLINE, 'pdf-ink:text-format-underline');
-    await testButton(
-        buttons[3]!, TextStyle.STRIKETHROUGH,
-        'pdf-ink:text-format-strikethrough');
 
     chrome.test.succeed();
   },
diff --git a/chrome/test/data/pdf/ink2_viewer_toolbar_test.ts b/chrome/test/data/pdf/ink2_viewer_toolbar_test.ts
index faf733b2..6cf24f3 100644
--- a/chrome/test/data/pdf/ink2_viewer_toolbar_test.ts
+++ b/chrome/test/data/pdf/ink2_viewer_toolbar_test.ts
@@ -2,13 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {AnnotationMode, PluginController, UserAction} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {AnnotationMode, PluginController, PluginControllerEventType, UserAction} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import type {InkTextBoxElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {assert} from 'chrome://resources/js/assert.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {isMac} from 'chrome://resources/js/platform.js';
 import {keyDownOn} from 'chrome://webui-test/keyboard_mock_interactions.js';
 import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
-import {assertCheckboxMenuButton, enterFullscreenWithUserGesture, finishInkStroke, getRequiredElement, openToolbarMenu, setupMockMetricsPrivate, setupTestMockPluginForInk} from './test_util.js';
+import {assertCheckboxMenuButton, createTextBox, enterFullscreenWithUserGesture, finishInkStroke, getRequiredElement, openToolbarMenu, setupMockMetricsPrivate, setupTestMockPluginForInk} from './test_util.js';
 
 const viewer = document.body.querySelector('pdf-viewer')!;
 const viewerToolbar = viewer.$.toolbar;
@@ -20,6 +22,22 @@
   return isMac ? 'meta' : 'ctrl';
 }
 
+// Utils to add extra wait for Mac13 tests.
+async function createTextBoxAndWaitForStateChange(textBox: HTMLElement) {
+  const whenStateChanged = eventToPromise('state-changed', textBox);
+  createTextBox();
+  await whenStateChanged;
+  await microtasksFinished();
+}
+
+async function commitAnnotationAndWaitForStateChange(
+    textBox: InkTextBoxElement) {
+  const whenStateChanged = eventToPromise('state-changed', textBox);
+  textBox.commitTextAnnotation();
+  await whenStateChanged;
+  await microtasksFinished();
+}
+
 chrome.test.runTests([
   // Test that clicking the annotation button toggles annotation mode.
   async function testAnnotationButton() {
@@ -365,6 +383,91 @@
     viewerToolbar.resetStrokesForTesting();
     chrome.test.succeed();
   },
+  async function testUndoRedoTextAnnotation() {
+    // Set the feature param in loadTimeData and trigger Lit binding.
+    loadTimeData.overrideValues({'pdfTextAnnotationsEnabled': true});
+    viewerToolbar.strings = Object.assign({}, viewerToolbar.strings);
+    await microtasksFinished();
+
+    mockPlugin.clearMessages();
+    mockMetricsPrivate.reset();
+
+    // Enter draw mode to draw a stroke.
+    viewerToolbar.setAnnotationMode(AnnotationMode.DRAW);
+    await microtasksFinished();
+
+    const undoButton =
+        getRequiredElement<HTMLButtonElement>(viewerToolbar, '#undo');
+    const redoButton =
+        getRequiredElement<HTMLButtonElement>(viewerToolbar, '#redo');
+
+    // The buttons should be disabled when there aren't any changes.
+    chrome.test.assertTrue(undoButton.disabled);
+    chrome.test.assertTrue(redoButton.disabled);
+
+    // Draw a stroke. The undo button should be enabled.
+    finishInkStroke(controller);
+    await microtasksFinished();
+    chrome.test.assertTrue(
+        mockPlugin.findMessage('annotationUndo') === undefined);
+    chrome.test.assertFalse(undoButton.disabled);
+    chrome.test.assertTrue(redoButton.disabled);
+
+    // Button still enabled after changing to text annotation mode.
+    viewerToolbar.setAnnotationMode(AnnotationMode.TEXT);
+    await microtasksFinished();
+    chrome.test.assertFalse(undoButton.disabled);
+    chrome.test.assertTrue(redoButton.disabled);
+
+    // Create a textbox. The undo button should now be disabled.
+    const textBox = viewer.shadowRoot.querySelector('ink-text-box');
+    assert(textBox);
+    await createTextBoxAndWaitForStateChange(textBox);
+    chrome.test.assertTrue(undoButton.disabled);
+    chrome.test.assertTrue(redoButton.disabled);
+
+    // Simulate closing the textbox with no changes. Now the undo button is
+    // enabled again.
+    await commitAnnotationAndWaitForStateChange(textBox);
+    chrome.test.assertFalse(undoButton.disabled);
+    chrome.test.assertTrue(redoButton.disabled);
+
+    // Undo the stroke. The redo button should be enabled.
+    undoButton.click();
+    await microtasksFinished();
+    chrome.test.assertTrue(
+        mockPlugin.findMessage('annotationUndo') !== undefined);
+    chrome.test.assertTrue(undoButton.disabled);
+    chrome.test.assertFalse(redoButton.disabled);
+    mockMetricsPrivate.assertCount(UserAction.UNDO_INK2, 1);
+    mockMetricsPrivate.assertCount(UserAction.REDO_INK2, 0);
+
+    // Add a textbox. The redo button is disabled.
+    mockPlugin.clearMessages();
+    await createTextBoxAndWaitForStateChange(textBox);
+    chrome.test.assertTrue(undoButton.disabled);
+    chrome.test.assertTrue(redoButton.disabled);
+
+    // Make a change to the textbox before closing. Now the undo button
+    // should be enabled, since there is a new text annotation change. Redo
+    // is disabled since the new text annotation overrides the stroke that
+    // could have been redone.
+    const whenStateChanged = eventToPromise('state-changed', textBox);
+    textBox.$.textbox.value = 'Hello';
+    textBox.$.textbox.dispatchEvent(new CustomEvent('input'));
+    // Wait for textbox state edited.
+    await whenStateChanged;
+    await microtasksFinished();
+    await commitAnnotationAndWaitForStateChange(textBox);
+    chrome.test.assertFalse(undoButton.disabled);
+    chrome.test.assertTrue(redoButton.disabled);
+
+    // Reset state for later tests.
+    viewerToolbar.resetStrokesForTesting();
+    viewerToolbar.setAnnotationMode(AnnotationMode.OFF);
+    await microtasksFinished();
+    chrome.test.succeed();
+  },
   // Test the behavior of the undo redo keyboard shortcuts.
   async function testUndoRedoKeyboardShortcuts() {
     mockPlugin.clearMessages();
@@ -478,4 +581,81 @@
     viewerToolbar.resetStrokesForTesting();
     chrome.test.succeed();
   },
+  // Test the behavior of the undo redo keyboard shortcuts in text annotation
+  // mode.
+  async function testUndoRedoKeyboardShortcutsTextAnnotation() {
+    mockPlugin.clearMessages();
+    mockMetricsPrivate.reset();
+
+    chrome.test.assertEq(AnnotationMode.OFF, viewerToolbar.annotationMode);
+
+    // Enable text annotation mode.
+    viewerToolbar.setAnnotationMode(AnnotationMode.TEXT);
+    await microtasksFinished();
+    chrome.test.assertEq(AnnotationMode.TEXT, viewerToolbar.annotationMode);
+
+    // Simulate committing an edited text annotation.
+    PluginController.getInstance().getEventTarget().dispatchEvent(
+        new CustomEvent(PluginControllerEventType.FINISH_INK_STROKE));
+
+    // Undo shortcut.
+    keyDownOn(viewerToolbar, 0, getUndoRedoModifier(), 'z');
+    chrome.test.assertTrue(
+        mockPlugin.findMessage('annotationUndo') !== undefined);
+    mockMetricsPrivate.assertCount(UserAction.UNDO_INK2, 1);
+    mockMetricsPrivate.assertCount(UserAction.REDO_INK2, 0);
+    mockPlugin.clearMessages();
+
+    // Redo shortcut.
+    keyDownOn(viewerToolbar, 0, getUndoRedoModifier(), 'y');
+    chrome.test.assertTrue(
+        mockPlugin.findMessage('annotationRedo') !== undefined);
+    mockMetricsPrivate.assertCount(UserAction.UNDO_INK2, 1);
+    mockMetricsPrivate.assertCount(UserAction.REDO_INK2, 1);
+    mockPlugin.clearMessages();
+
+    // Shortcuts don't work when there is an active text box (instead, they
+    // are handled by the native <textarea> element).
+    const textBox = viewer.shadowRoot.querySelector('ink-text-box');
+    assert(textBox);
+    await createTextBoxAndWaitForStateChange(textBox);
+    // Undo shortcut.
+    keyDownOn(viewerToolbar, 0, getUndoRedoModifier(), 'z');
+    chrome.test.assertTrue(
+        mockPlugin.findMessage('annotationUndo') === undefined);
+    mockMetricsPrivate.assertCount(UserAction.UNDO_INK2, 1);
+    mockMetricsPrivate.assertCount(UserAction.REDO_INK2, 1);
+    mockPlugin.clearMessages();
+
+    // Close textbox. Undo works again.
+    await commitAnnotationAndWaitForStateChange(textBox);
+    keyDownOn(viewerToolbar, 0, getUndoRedoModifier(), 'z');
+    chrome.test.assertTrue(
+        mockPlugin.findMessage('annotationUndo') !== undefined);
+    mockMetricsPrivate.assertCount(UserAction.UNDO_INK2, 2);
+    mockMetricsPrivate.assertCount(UserAction.REDO_INK2, 1);
+    mockPlugin.clearMessages();
+
+    // Redo also doesn't work with a textbox open.
+    await createTextBoxAndWaitForStateChange(textBox);
+    // Undo shortcut.
+    keyDownOn(viewerToolbar, 0, getUndoRedoModifier(), 'y');
+    chrome.test.assertTrue(
+        mockPlugin.findMessage('annotationRedo') === undefined);
+    mockMetricsPrivate.assertCount(UserAction.UNDO_INK2, 2);
+    mockMetricsPrivate.assertCount(UserAction.REDO_INK2, 1);
+    mockPlugin.clearMessages();
+
+    // Close textbox. Redo works again.
+    await commitAnnotationAndWaitForStateChange(textBox);
+    keyDownOn(viewerToolbar, 0, getUndoRedoModifier(), 'y');
+    chrome.test.assertTrue(
+        mockPlugin.findMessage('annotationRedo') !== undefined);
+    mockMetricsPrivate.assertCount(UserAction.UNDO_INK2, 2);
+    mockMetricsPrivate.assertCount(UserAction.REDO_INK2, 2);
+    mockPlugin.clearMessages();
+
+    viewerToolbar.resetStrokesForTesting();
+    chrome.test.succeed();
+  },
 ]);
diff --git a/chrome/test/data/pdf/test_util.ts b/chrome/test/data/pdf/test_util.ts
index ac98638..2cb8643f 100644
--- a/chrome/test/data/pdf/test_util.ts
+++ b/chrome/test/data/pdf/test_util.ts
@@ -8,7 +8,7 @@
 import {resetForTesting as resetMetricsForTesting, UserAction, Viewport} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 // <if expr="enable_pdf_ink2">
 import type {AnnotationBrush, BeforeUnloadProxy, InkBrushSelectorElement, InkColorSelectorElement, InkSizeSelectorElement, SelectableIconButtonElement, ViewerBottomToolbarDropdownElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
-import {AnnotationBrushType, BeforeUnloadProxyImpl, Ink2Manager, PluginController, PluginControllerEventType} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {AnnotationBrushType, BeforeUnloadProxyImpl, Ink2Manager, PluginController, PluginControllerEventType, SaveRequestType} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 // </if>
 import {assert} from 'chrome://resources/js/assert.js';
 import {CrLitElement, html} from 'chrome://resources/lit/v3_0/lit.rollup.js';
@@ -175,6 +175,7 @@
   // <if expr="enable_pdf_ink2">
   private messageReply_: Object|null = null;
   private replyType_: string = '';
+  private replyToSave_: boolean = false;
   // </if>
 
   get messages(): any[] {
@@ -192,7 +193,9 @@
   postMessage(message: any, _transfer: Transferable[]) {
     assert(message.type);
     // <if expr="enable_pdf_ink2">
-    if (message.type === this.replyType_) {
+    if (message.type === 'save' && this.replyToSave_) {
+      this.replyToSaveMessage_(message);
+    } else if (message.type === this.replyType_) {
       assert(this.messageReply_);
       assert(message.messageId);
 
@@ -219,6 +222,48 @@
     this.replyType_ = type;
     this.messageReply_ = reply;
   }
+
+  /**
+   * Tells the plugin to respond to a "save" event by firing a 'saveData'
+   * or 'consumeSaveToken' message.
+   */
+  setReplyToSave(reply: boolean) {
+    this.replyToSave_ = reply;
+  }
+
+  private replyToSaveMessage_(message: any) {
+    assert(message.token);
+    if (message.saveRequestType === SaveRequestType.ORIGINAL) {
+      this.dispatchEvent(new MessageEvent('message', {
+        data: {
+          type: 'consumeSaveToken',
+          token: message.token,
+        },
+        origin: '*',
+      }));
+      return;
+    }
+    assert(
+        message.saveRequestType === SaveRequestType.ANNOTATION,
+        'Unexpected save request type');
+    const testData = '%PDF1.0 Hello World';
+    const buffer = new ArrayBuffer(testData.length);
+    // Encode the same way chrome/browser/resources/pdf/controller.ts decodes.
+    const view = new Uint8Array(buffer);
+    for (let i = 0; i < testData.length; i++) {
+      view[i] = testData.charCodeAt(i);
+    }
+    this.dispatchEvent(new MessageEvent('message', {
+      data: {
+        type: 'saveData',
+        token: message.token,
+        dataToSave: buffer,
+        fileName: 'test.pdf',
+        bypassSaveFileForTesting: true,
+      },
+      origin: '*',
+    }));
+  }
   // </if>
 }
 customElements.define(
@@ -667,4 +712,11 @@
     value1: object|any[]|undefined|null, value2: object|any[]|undefined|null) {
   chrome.test.assertTrue(chrome.test.checkDeepEq(value1, value2));
 }
+
+// Simulates initializing a textbox with a click.
+export function createTextBox() {
+  PluginController.getInstance().getEventTarget().dispatchEvent(new CustomEvent(
+      PluginControllerEventType.PLUGIN_MESSAGE,
+      {detail: {type: 'sendClickEvent', x: 50, y: 50}}));
+}
 // </if>
diff --git a/chrome/test/data/webui/bookmarks/test_browser_proxy.ts b/chrome/test/data/webui/bookmarks/test_browser_proxy.ts
index c53f5749..739d7973 100644
--- a/chrome/test/data/webui/bookmarks/test_browser_proxy.ts
+++ b/chrome/test/data/webui/bookmarks/test_browser_proxy.ts
@@ -57,8 +57,7 @@
     this.methodCalled('getBatchUploadPromoInfo');
     return Promise.resolve({
       canShow: false,
-      localBookmarksCount: 0,
-      email: '',
+      promoSubtitle: '',
     });
   }
 
diff --git a/chrome/test/data/webui/settings/autofill_section_focus_test.ts b/chrome/test/data/webui/settings/autofill_section_focus_test.ts
index df9bc5d..93bfd8d 100644
--- a/chrome/test/data/webui/settings/autofill_section_focus_test.ts
+++ b/chrome/test/data/webui/settings/autofill_section_focus_test.ts
@@ -7,6 +7,7 @@
 
 import {AutofillManagerImpl} from 'chrome://settings/lazy_load.js';
 import {assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import type {TestAutofillManager} from './autofill_fake_data.js';
 import {createAddressEntry} from './autofill_fake_data.js';
@@ -23,6 +24,7 @@
         ],
         {profile_enabled: {value: true}});
     const manager = AutofillManagerImpl.getInstance() as TestAutofillManager;
+    flush();
 
     await deleteAddress(section, manager, 1);
     const addressesAfterRemovingInTheMiddle =
diff --git a/chrome/test/data/webui/settings/autofill_section_test.ts b/chrome/test/data/webui/settings/autofill_section_test.ts
index 98c027d..1b12ca8 100644
--- a/chrome/test/data/webui/settings/autofill_section_test.ts
+++ b/chrome/test/data/webui/settings/autofill_section_test.ts
@@ -282,6 +282,7 @@
           ...STUB_USER_ACCOUNT_INFO,
           email,
         });
+    flush();
 
     {
       const dialog = await initiateEditing(section, 0);
@@ -382,6 +383,7 @@
     const section = await createAutofillSection([address], {});
     const addressList = section.$.addressList;
     const row = addressList.children[0];
+    flush();
     assertTrue(!!row);
 
     const addressSummary =
@@ -398,6 +400,25 @@
     assertEquals(addressSummary, actualSummary);
   });
 
+  test('verifyAccountHomeAddress', async function() {
+    const openWindowProxy = new TestOpenWindowProxy();
+    OpenWindowProxyImpl.setInstance(openWindowProxy);
+    const homeAddress = createAddressEntry();
+    homeAddress.metadata!.recordType =
+        chrome.autofillPrivate.AddressRecordType.ACCOUNT_HOME;
+    const autofillSection = await createAutofillSection([homeAddress], {});
+
+    const homeAddressButton =
+        autofillSection.shadowRoot!.querySelector<CrLinkRowElement>(
+            '#homeWorkAddress');
+    assertTrue(!!homeAddressButton);
+    // Validate that, when present, the button results in opening a URL.
+    homeAddressButton.click();
+    const url = await openWindowProxy.whenCalled('openUrl');
+    assertEquals(url, loadTimeData.getString('googleAccountHomeAddressUrl'));
+    autofillSection.remove();
+  });
+
   test('verifyAddressLocalIndication', async () => {
     const autofillManager = new TestAutofillManager();
     autofillManager.data.addresses = [createAddressEntry()];
@@ -442,6 +463,7 @@
     const address = createAddressEntry();
     const section = await createAutofillSection([address], {});
     const addressList = section.$.addressList;
+    flush();
     const row = addressList.children[0];
     assertTrue(!!row);
     const menuButton = row.querySelector<HTMLElement>('.address-menu');
diff --git a/chrome/updater/win/signing/enterprise_standalone_installer.wxs.xml b/chrome/updater/win/signing/enterprise_standalone_installer.wxs.xml
index ed91427..f462743 100644
--- a/chrome/updater/win/signing/enterprise_standalone_installer.wxs.xml
+++ b/chrome/updater/win/signing/enterprise_standalone_installer.wxs.xml
@@ -78,16 +78,18 @@
     </Property>
     <Property Id="AllowDowngradeSubstitution" Value="false" />
 
-    <!-- Allows running the `updater` with a `--recover` option to recover from
-         cases where the installed `updater` may be non-operational.
+    <!-- Allows running the `updater` with a `--force-install` option. This
+         force-installs the installer that is run with this switch and makes it
+         the active `updater`. In addition, if the MSI is tagged, this also
+         installs the application(s) that are implicitly specified in the tag.
     -->
-    <Property Id="RUNRECOVER" Secure="yes" />
+    <Property Id="RUNFORCEINSTALL" Secure="yes" />
 
     <!-- Eliminates "warning LGHT1076 : ICE71: The Media table has no entries." -->
     <Media Id="1" />
 
     <Feature Id="Complete" Level="1" AllowAbsent="yes">
-      <Level Value="0" Condition="RUNRECOVER &lt;&gt; &quot;&quot;" />
+      <Level Value="0" Condition="RUNFORCEINSTALL &lt;&gt; &quot;&quot;" />
       <ComponentRef Id="ProductClientState" />
     </Feature>
 
@@ -223,10 +225,10 @@
     />
 
     <CustomAction
-      Id="DoRunRecover"
+      Id="DoRunForceInstall"
       Impersonate="no"
       Execute="deferred"
-      ExeCommand="--recover --system"
+      ExeCommand="--force-install --system"
       Return="check"
       BinaryRef="$(var.ProductNameLegalIdentifier)Installer"
     />
@@ -236,7 +238,7 @@
 
       <Custom Action="NewerVersionError"
         After="FindRelatedProducts"
-        Condition="((RUNRECOVER = &quot;&quot;) AND NEWPRODUCTFOUND AND (ALLOWDOWNGRADE = &quot;&quot;))"
+        Condition="((RUNFORCEINSTALL = &quot;&quot;) AND NEWPRODUCTFOUND AND (ALLOWDOWNGRADE = &quot;&quot;))"
       />
 
       <!-- The conditions in the following custom actions trigger the action when either:
@@ -253,19 +255,19 @@
       <Custom
         Action="ExtractTagInfoFromInstaller"
         Before="SetProductTagProperty"
-        Condition="((RUNRECOVER = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
+        Condition="((RUNFORCEINSTALL = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
       />
       <!-- Now start forming [ProdutTag], first with the appguid and name. -->
       <Custom
         Action="SetProductTagProperty"
         Before="AppendCustomParamsToProductTagProperty"
-        Condition="((RUNRECOVER = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
+        Condition="((RUNFORCEINSTALL = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
       />
       <!-- Add on the build-time string. -->
       <Custom
         Action="AppendCustomParamsToProductTagProperty"
         Before="TaggedMSIOverrideProductTagProperty"
-        Condition="((RUNRECOVER = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
+        Condition="((RUNFORCEINSTALL = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
       />
       <!-- Override the tag completely if the MSI is tagged, or if the caller
            has set the `TAGSTRING` property. Among all actions that change
@@ -275,46 +277,46 @@
       <Custom
         Action="TaggedMSIOverrideProductTagProperty"
         Before="BuildInstallCommand"
-        Condition="((RUNRECOVER = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL)) AND (TAGSTRING &lt;&gt; &quot;&quot;)"
+        Condition="((RUNFORCEINSTALL = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL)) AND (TAGSTRING &lt;&gt; &quot;&quot;)"
       />
       <Custom
         Action="SetOptArgsProperty"
         Before="AppendEnterpriseToOptArgsProperty"
-        Condition="((RUNRECOVER = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
+        Condition="((RUNFORCEINSTALL = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
       />
       <Custom
         Action="AppendEnterpriseToOptArgsProperty"
         Before="BuildInstallCommand"
-        Condition="((RUNRECOVER = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL)) AND (NOGOOGLEUPDATEPING &lt;&gt; &quot;&quot;)"
+        Condition="((RUNFORCEINSTALL = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL)) AND (NOGOOGLEUPDATEPING &lt;&gt; &quot;&quot;)"
       />
 
       <!-- Set [AllowDowngradeSubstitution] to true if downgrades are allowed. -->
       <Custom
         Action="DoAllowDowngradeSubstitution"
         Before="BuildInstallCommand"
-        Condition="((RUNRECOVER = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL)) AND (ALLOWDOWNGRADE &lt;&gt; &quot;&quot;)"
+        Condition="((RUNFORCEINSTALL = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL)) AND (ALLOWDOWNGRADE &lt;&gt; &quot;&quot;)"
       />
 
       <Custom
         Action="BuildInstallCommand"
         Before="SetAppGuidProperty"
-        Condition="((RUNRECOVER = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
+        Condition="((RUNFORCEINSTALL = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
       />
       <Custom
         Action="SetAppGuidProperty"
         Before="ShowInstallerResultUIString"
-        Condition="((RUNRECOVER = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
+        Condition="((RUNFORCEINSTALL = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
       />
       <Custom
         Action="ShowInstallerResultUIString"
         Before="DoInstall"
-        Condition="((RUNRECOVER = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
+        Condition="((RUNFORCEINSTALL = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
       />
 
       <Custom
         Action="DoInstall"
         After="InstallFiles"
-        Condition="((RUNRECOVER = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
+        Condition="((RUNFORCEINSTALL = &quot;&quot;) AND ((?ProductClientState=2) AND ($ProductClientState=3)) OR ((?ProductClientState=3) AND REINSTALL))"
       />
 
       <!-- The conditions in the following custom actions trigger the action when the product is
@@ -323,24 +325,24 @@
       <Custom
         Action="CallUninstallerArgs.SetProperty"
         Before="CallUninstaller.SetProperty"
-        Condition="((RUNRECOVER = &quot;&quot;) AND (?ProductClientState=3) AND ($ProductClientState=2) AND NOT UPGRADINGPRODUCTCODE)"
+        Condition="((RUNFORCEINSTALL = &quot;&quot;) AND (?ProductClientState=3) AND ($ProductClientState=2) AND NOT UPGRADINGPRODUCTCODE)"
       />
       <Custom
         Action="CallUninstaller.SetProperty"
         Before="CallUninstaller"
-        Condition="((RUNRECOVER = &quot;&quot;) AND (?ProductClientState=3) AND ($ProductClientState=2) AND NOT UPGRADINGPRODUCTCODE)"
+        Condition="((RUNFORCEINSTALL = &quot;&quot;) AND (?ProductClientState=3) AND ($ProductClientState=2) AND NOT UPGRADINGPRODUCTCODE)"
       />
       <Custom
         Action="CallUninstaller"
         Before="RemoveFiles"
-        Condition="((RUNRECOVER = &quot;&quot;) AND (?ProductClientState=3) AND ($ProductClientState=2) AND NOT UPGRADINGPRODUCTCODE)"
+        Condition="((RUNFORCEINSTALL = &quot;&quot;) AND (?ProductClientState=3) AND ($ProductClientState=2) AND NOT UPGRADINGPRODUCTCODE)"
       />
       <!-- Google Update will uninstall itself if the product is the only app it
            so no need to have an uninstall operation. -->
 
-      <Custom Action="DoRunRecover"
+      <Custom Action="DoRunForceInstall"
         After="InstallInitialize"
-        Condition="RUNRECOVER &lt;&gt; &quot;&quot;"
+        Condition="RUNFORCEINSTALL &lt;&gt; &quot;&quot;"
       />
     </InstallExecuteSequence>
 
diff --git a/chromecast/android/lint-baseline.xml b/chromecast/android/lint-baseline.xml
index 0e231ba..833dd67 100644
--- a/chromecast/android/lint-baseline.xml
+++ b/chromecast/android/lint-baseline.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.11.0-alpha07" type="baseline" client="" dependencies="true" name="" variant="all" version="8.11.0-alpha07">
+<issues format="6" by="lint 8.11.0-alpha09" type="baseline" client="" dependencies="true" name="" variant="all" version="8.11.0-alpha09">
 
     <issue
         id="PictureInPictureIssue"
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index dfae418..8fc4e55 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-16276.0.0-1068685
\ No newline at end of file
+16276.0.0-1068704
\ No newline at end of file
diff --git a/chromeos/ash/experiences/arc/compat_mode/arc_splash_screen_dialog_view.cc b/chromeos/ash/experiences/arc/compat_mode/arc_splash_screen_dialog_view.cc
index 847099ce..b83053a0 100644
--- a/chromeos/ash/experiences/arc/compat_mode/arc_splash_screen_dialog_view.cc
+++ b/chromeos/ash/experiences/arc/compat_mode/arc_splash_screen_dialog_view.cc
@@ -288,7 +288,7 @@
   const int kCornerRadius = 20;
   auto* const frame = GetBubbleFrameView();
   if (frame) {
-    frame->SetCornerRadius(kCornerRadius);
+    frame->SetRoundedCorners(gfx::RoundedCornersF(kCornerRadius));
   }
 }
 
diff --git a/chromeos/ash/experiences/arc/compat_mode/resize_confirmation_dialog_view.cc b/chromeos/ash/experiences/arc/compat_mode/resize_confirmation_dialog_view.cc
index e1add7c..4402bb83 100644
--- a/chromeos/ash/experiences/arc/compat_mode/resize_confirmation_dialog_view.cc
+++ b/chromeos/ash/experiences/arc/compat_mode/resize_confirmation_dialog_view.cc
@@ -22,6 +22,7 @@
 #include "ui/base/mojom/dialog_button.mojom.h"
 #include "ui/color/color_id.h"
 #include "ui/color/color_provider.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/bubble/bubble_border.h"
@@ -111,7 +112,7 @@
   const int kCornerRadius = 20;
   auto* const frame = GetBubbleFrameView();
   if (frame) {
-    frame->SetCornerRadius(kCornerRadius);
+    frame->SetRoundedCorners(gfx::RoundedCornersF(kCornerRadius));
   }
 
   widget_observation_.Observe(GetWidget());
diff --git a/chromeos/ash/experiences/arc/compat_mode/resize_toggle_menu.cc b/chromeos/ash/experiences/arc/compat_mode/resize_toggle_menu.cc
index 69d04a9..cb0bb99 100644
--- a/chromeos/ash/experiences/arc/compat_mode/resize_toggle_menu.cc
+++ b/chromeos/ash/experiences/arc/compat_mode/resize_toggle_menu.cc
@@ -66,7 +66,7 @@
   void AddedToWidget() override {
     auto* const frame = GetBubbleFrameView();
     if (frame) {
-      frame->SetCornerRadius(corner_radius_);
+      frame->SetRoundedCorners(gfx::RoundedCornersF(corner_radius_));
     }
   }
 
diff --git a/chromeos/ash/experiences/arc/net/passpoint_dialog_view.cc b/chromeos/ash/experiences/arc/net/passpoint_dialog_view.cc
index f36b73d..5699f8db 100644
--- a/chromeos/ash/experiences/arc/net/passpoint_dialog_view.cc
+++ b/chromeos/ash/experiences/arc/net/passpoint_dialog_view.cc
@@ -96,7 +96,7 @@
   auto border = std::make_unique<views::BubbleBorder>(
       views::BubbleBorder::NONE, views::BubbleBorder::STANDARD_SHADOW);
   border->SetColor(ash::kColorAshDialogBackgroundColor);
-  border->SetCornerRadius(kCornerRadius);
+  border->set_rounded_corners(gfx::RoundedCornersF(kCornerRadius));
   SetBackground(std::make_unique<views::BubbleBackground>(border.get()));
   SetBorder(std::move(border));
 
diff --git a/clank b/clank
index 11c3547..4326f34 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 11c3547fd4f0706067ad840ff190038861085741
+Subproject commit 4326f348545fb5ef5cbb59389e7e3a903d2320eb
diff --git a/components/autofill/android/BUILD.gn b/components/autofill/android/BUILD.gn
index 4f4271a..535d7f35 100644
--- a/components/autofill/android/BUILD.gn
+++ b/components/autofill/android/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/config/android/rules.gni")
-import("//build/config/locales.gni")
 import("//third_party/jni_zero/jni_zero.gni")
 import("//tools/grit/grit_rule.gni")
 
@@ -43,10 +42,6 @@
 
 java_strings_grd("autofill_strings_grd") {
   grd_file = "java/strings/autofill_strings.grd"
-  outputs = [ "values/autofill_strings.xml" ] +
-            process_file_template(
-                android_bundle_locales_as_resources,
-                [ "values-{{source_name_part}}/autofill_strings.xml" ])
 }
 
 android_resources("autofill_payments_java_resources") {
diff --git a/components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.cc b/components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.cc
index a85e8dd..baa064b1 100644
--- a/components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.cc
@@ -13,33 +13,37 @@
 
 void IbanSuggestionGenerator::FetchSuggestionData(
     const FormStructure& form,
-    const AutofillField& trigger_field,
-    AutofillClient& client,
+    const AutofillField& field,
+    const AutofillClient& client,
     base::OnceCallback<
         void(std::pair<FillingProduct,
                        std::vector<SuggestionGenerator::SuggestionData>>)>
         callback) {
-  std::move(callback).Run(
-      {FillingProduct::kIban,
-       base::ToVector(
-           client.GetPaymentsAutofillClient()
-               ->GetPaymentsDataManager()
-               .GetOrderedIbansToSuggest(),
-           [](Iban& iban) { return SuggestionData(std::move(iban)); })});
+  std::vector<Iban> ibans = client.GetPaymentsAutofillClient()
+                                ->GetPaymentsDataManager()
+                                .GetOrderedIbansToSuggest();
+  std::vector<SuggestionData> suggestion_data = base::ToVector(
+      std::move(ibans),
+      [](Iban& iban) { return SuggestionData(std::move(iban)); });
+
+  // TODO(crbug.com/409962888): Once the Iban suggestion generation logic is
+  // removed from `IbanManager`, this function should check for the required
+  // conditions to show IBAN suggestions.
+  std::move(callback).Run({FillingProduct::kIban, std::move(suggestion_data)});
 }
 
 void IbanSuggestionGenerator::GenerateSuggestions(
     const FormStructure& form,
-    const AutofillField& trigger_field,
+    const AutofillField& field,
     AutofillClient& client,
     const std::vector<std::pair<FillingProduct, std::vector<SuggestionData>>>&
-        suggestion_data,
+        all_suggestion_data,
     base::OnceCallback<void(ReturnedSuggestions)> callback) {
   IbanManager* iban_manager =
       client.GetPaymentsAutofillClient()->GetIbanManager();
-  if (!iban_manager ||
-      GetSuggestionDataForFillingProduct(suggestion_data, FillingProduct::kIban)
-          .empty()) {
+  if (!iban_manager || ExtractSuggestionDataForFillingProduct(
+                           all_suggestion_data, FillingProduct::kIban)
+                           .empty()) {
     // There are no IBAN suggestions to return.
     std::move(callback).Run({FillingProduct::kIban, {}});
     return;
@@ -62,10 +66,11 @@
 
   // TODO(crbug.com/409962888): Move the suggestion generation logic from the
   // `IbanManager` to this class.
-  // TODO(crbug.com/409962888): `OnGetSingleFieldSuggestions` should use the
-  // suggestion data instead of calling the `PaymentsDataManager` again.
+  // TODO(crbug.com/409962888): Once the iban suggestion generation logic is
+  // removed from `SingleFieldFillRouter`, `OnGetSingleFieldSuggestions` should
+  // use the suggestion data instead of calling the `PaymentsDataManager` again.
   if (!iban_manager->OnGetSingleFieldSuggestions(
-          trigger_field, trigger_field, client, on_suggestions_returned)) {
+          field, field, client, on_suggestions_returned)) {
     wrapped_callback.Run({});
   }
 }
diff --git a/components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.h b/components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.h
index 6f3d55c..e0a0716 100644
--- a/components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.h
+++ b/components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.h
@@ -13,8 +13,8 @@
  public:
   void FetchSuggestionData(
       const FormStructure& form,
-      const AutofillField& trigger_field,
-      AutofillClient& client,
+      const AutofillField& field,
+      const AutofillClient& client,
       base::OnceCallback<
           void(std::pair<FillingProduct,
                          std::vector<SuggestionGenerator::SuggestionData>>)>
@@ -22,10 +22,10 @@
 
   void GenerateSuggestions(
       const FormStructure& form,
-      const AutofillField& trigger_field,
+      const AutofillField& field,
       AutofillClient& client,
       const std::vector<std::pair<FillingProduct, std::vector<SuggestionData>>>&
-          suggestion_data,
+          all_suggestion_data,
       base::OnceCallback<void(ReturnedSuggestions)> callback) override;
 };
 
diff --git a/components/autofill/core/browser/suggestions/suggestion_generator.cc b/components/autofill/core/browser/suggestions/suggestion_generator.cc
index c90ca9fb..8683446 100644
--- a/components/autofill/core/browser/suggestions/suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/suggestion_generator.cc
@@ -7,14 +7,14 @@
 namespace autofill {
 
 std::vector<SuggestionGenerator::SuggestionData>
-SuggestionGenerator::GetSuggestionDataForFillingProduct(
+SuggestionGenerator::ExtractSuggestionDataForFillingProduct(
     const std::vector<std::pair<FillingProduct, std::vector<SuggestionData>>>&
-        suggestion_data,
+        all_suggestion_data,
     FillingProduct filling_product) {
   auto it = std::ranges::find(
-      suggestion_data, filling_product,
+      all_suggestion_data, filling_product,
       &std::pair<FillingProduct, std::vector<SuggestionData>>::first);
-  if (it != suggestion_data.end()) {
+  if (it != all_suggestion_data.end()) {
     return it->second;
   }
   return std::vector<SuggestionData>();
diff --git a/components/autofill/core/browser/suggestions/suggestion_generator.h b/components/autofill/core/browser/suggestions/suggestion_generator.h
index 7409e88..92f901c9 100644
--- a/components/autofill/core/browser/suggestions/suggestion_generator.h
+++ b/components/autofill/core/browser/suggestions/suggestion_generator.h
@@ -14,6 +14,27 @@
 
 namespace autofill {
 
+// SuggestionGenerator is an interface that is used to generate suggestions for
+// a specific `FillingProduct`. Each `FillingProduct` has their own
+// implementation of the `SuggestionGenerator` interface.
+//
+// Generating suggestions consists of two phases:
+// 1. All generators are called to fetch the data that is gonna be used for
+//    creating the suggestions. No assumptions should be made about the order
+//    of those calls and some of those calls will be asynchronous.
+// 2. Every generator is called again with the data that was fetched for all
+//    `FillingProduct`s in step 1, and uses that to generate the suggestions
+//    for its specific `FillingProduct`.
+//
+// Note: Some product suggestions depend on the data from other products.
+// E.g. PlusAddresses and Address suggestions needs the data from both
+// products, before being able to generate suggestions.
+//
+// Because of the dependency for each suggestion generator to know about the
+// data from other FillingProducts, the generation is split into two phases. The
+// second phase is only executed if the first phase finished for all
+// `FillingProduct`s. Within each phase there is no guarantee about the order in
+// which the suggestion generators are called.
 class SuggestionGenerator {
  public:
   SuggestionGenerator() = default;
@@ -26,36 +47,43 @@
   using SuggestionData =
       std::variant<EntityInstance, AutofillProfile, CreditCard, Iban>;
 
-  // Obtains data for suggestions for a specific `FillingProduct` and calls
-  // `callback` with that product along with the suggestions data.
+  // Obtains data that will be used to generate suggestions on a given trigger
+  // `field` that belongs to `form` by calling `GenerateSuggestions` later (See
+  // top-level documentation of `SuggestionGenerator` for more details).
+  // Once the data is obtained, `callback` is called with the `FillingProduct`
+  // of which the data is for and the corresponding `SuggestionData`.
   virtual void FetchSuggestionData(
       const FormStructure& form,
-      const AutofillField& trigger_field,
-      AutofillClient& client,
+      const AutofillField& field,
+      const AutofillClient& client,
       base::OnceCallback<
           void(std::pair<FillingProduct,
                          std::vector<SuggestionGenerator::SuggestionData>>)>
           callback) = 0;
 
-  // Generates suggestions for a specific `FillingProduct` and calls `callback`
-  // with that product along with the suggestions that were generated.
+  // Generates suggestions given `all_suggestion_data` that were fetched by
+  // calling `FetchSuggestionData` on all generators (See top-level
+  // documentation of `SuggestionGenerator` for more details).
+  // Suggestions were triggered on `field` which belongs to `form`. `callback`
+  // is called when generation is complete and a list of `Suggestion`
+  // objects is passed along with the corresponding `FillingProduct`.
+  // TODO(crbug.com/409962888): Remove `client` from the parameters.
   virtual void GenerateSuggestions(
       const FormStructure& form,
-      const AutofillField& trigger_field,
+      const AutofillField& field,
       AutofillClient& client,
       const std::vector<std::pair<FillingProduct, std::vector<SuggestionData>>>&
-          suggestion_data,
+          all_suggestion_data,
       base::OnceCallback<void(ReturnedSuggestions)> callback) = 0;
 
-  protected:
-   // Returns the vector of `SuggestionData` for a specific `FillingProduct`
-   // from the `suggestion_data` vector.
-   std::vector<SuggestionGenerator::SuggestionData>
-   GetSuggestionDataForFillingProduct(
-       const std::vector<
-           std::pair<FillingProduct, std::vector<SuggestionData>>>&
-           suggestion_data,
-       FillingProduct filling_product);
+ protected:
+  // Returns the vector of `SuggestionData` for a specific `FillingProduct`
+  // from the `all_suggestion_data` vector.
+  std::vector<SuggestionGenerator::SuggestionData>
+  ExtractSuggestionDataForFillingProduct(
+      const std::vector<std::pair<FillingProduct, std::vector<SuggestionData>>>&
+          all_suggestion_data,
+      FillingProduct filling_product);
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/common/autocomplete_parsing_util.cc b/components/autofill/core/common/autocomplete_parsing_util.cc
index 2340905..785889ac 100644
--- a/components/autofill/core/common/autocomplete_parsing_util.cc
+++ b/components/autofill/core/common/autocomplete_parsing_util.cc
@@ -215,22 +215,23 @@
 
   AutocompleteParsingResult result;
 
-  // Parse the "webauthn" token.
-  if (tokens.back() == "webauthn") {
-    result.webauthn = true;
-    tokens.pop_back();
-    if (tokens.empty()) {
-      return result;
+  // The "webauthn" and "webidentity" tokens can appear in any order at the end
+  // of the list. Note that `tokens` won't be empty by this moment.
+  while (!tokens.empty()) {
+    if (tokens.back() == "webauthn") {
+      result.webauthn = true;
+      tokens.pop_back();
+    } else if (tokens.back() == "webidentity") {
+      result.webidentity = true;
+      tokens.pop_back();
+    } else {
+      // If the last token is neither "webauthn" nor "webidentity",
+      // stop processing these specific tokens.
+      break;
     }
   }
-
-  // Parse the "webidentity" token.
-  if (tokens.back() == "webidentity") {
-    result.webidentity = true;
-    tokens.pop_back();
-    if (tokens.empty()) {
-      return result;
-    }
+  if (tokens.empty()) {
+    return result;
   }
 
   // (1) The final token must be the field type.
diff --git a/components/autofill/core/common/autocomplete_parsing_util_unittest.cc b/components/autofill/core/common/autocomplete_parsing_util_unittest.cc
index de076c5..b891367 100644
--- a/components/autofill/core/common/autocomplete_parsing_util_unittest.cc
+++ b/components/autofill/core/common/autocomplete_parsing_util_unittest.cc
@@ -74,6 +74,18 @@
     {"webauthn",
      {{"", HtmlFieldMode::kNone, HtmlFieldType::kUnspecified,
        /*webauthn=*/true}}},
+    {"name webauthn webidentity",
+     {{"", HtmlFieldMode::kNone, HtmlFieldType::kName, /*webauthn=*/true,
+       /*webidentity=*/true}}},
+    {"name webidentity webauthn",
+     {{"", HtmlFieldMode::kNone, HtmlFieldType::kName, /*webauthn=*/true,
+       /*webidentity=*/true}}},
+    {"webauthn webidentity",
+     {{"", HtmlFieldMode::kNone, HtmlFieldType::kUnspecified,
+       /*webauthn=*/true, /*webidentity=*/true}}},
+    {"webidentity",
+     {{"", HtmlFieldMode::kNone, HtmlFieldType::kUnspecified,
+       /*webauthn=*/false, /*webidentity=*/true}}},
 
     // Too many tokens.
     {"hello section-one shipping home tel webauthn", std::nullopt}};
diff --git a/components/autofill/ios/browser/autofill_across_iframes_unittest.mm b/components/autofill/ios/browser/autofill_across_iframes_unittest.mm
index 994744f..241355f7 100644
--- a/components/autofill/ios/browser/autofill_across_iframes_unittest.mm
+++ b/components/autofill/ios/browser/autofill_across_iframes_unittest.mm
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #import <string>
-#include <variant>
+#import <variant>
 #import <vector>
 
 #import "base/containers/contains.h"
diff --git a/components/autofill/ios/browser/autofill_agent_unittests.mm b/components/autofill/ios/browser/autofill_agent_unittests.mm
index d6afe2d..899d6f9 100644
--- a/components/autofill/ios/browser/autofill_agent_unittests.mm
+++ b/components/autofill/ios/browser/autofill_agent_unittests.mm
@@ -31,7 +31,7 @@
 #import "components/autofill/core/common/autofill_prefs.h"
 #import "components/autofill/core/common/field_data_manager.h"
 #import "components/autofill/core/common/form_data.h"
-#include "components/autofill/core/common/form_field_data.h"
+#import "components/autofill/core/common/form_field_data.h"
 #import "components/autofill/core/common/mojom/autofill_types.mojom-shared.h"
 #import "components/autofill/core/common/unique_ids.h"
 #import "components/autofill/ios/browser/autofill_driver_ios.h"
diff --git a/components/autofill/ios/browser/autofill_client_ios_bridge.h b/components/autofill/ios/browser/autofill_client_ios_bridge.h
index 3e4c862..29b1beec 100644
--- a/components/autofill/ios/browser/autofill_client_ios_bridge.h
+++ b/components/autofill/ios/browser/autofill_client_ios_bridge.h
@@ -5,7 +5,7 @@
 #ifndef COMPONENTS_AUTOFILL_IOS_BROWSER_AUTOFILL_CLIENT_IOS_BRIDGE_H_
 #define COMPONENTS_AUTOFILL_IOS_BROWSER_AUTOFILL_CLIENT_IOS_BRIDGE_H_
 
-#include <vector>
+#import <vector>
 
 #import "base/functional/callback_forward.h"
 #import "base/memory/weak_ptr.h"
diff --git a/components/autofill/ios/browser/autofill_driver_ios.mm b/components/autofill/ios/browser/autofill_driver_ios.mm
index 62d1996d..188dc671 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.mm
+++ b/components/autofill/ios/browser/autofill_driver_ios.mm
@@ -4,7 +4,7 @@
 
 #import "components/autofill/ios/browser/autofill_driver_ios.h"
 
-#include <variant>
+#import <variant>
 
 #import "base/check_deref.h"
 #import "base/containers/contains.h"
diff --git a/components/autofill/ios/browser/autofill_driver_ios_factory_test_api.h b/components/autofill/ios/browser/autofill_driver_ios_factory_test_api.h
index 0383431..4196373 100644
--- a/components/autofill/ios/browser/autofill_driver_ios_factory_test_api.h
+++ b/components/autofill/ios/browser/autofill_driver_ios_factory_test_api.h
@@ -5,8 +5,8 @@
 #ifndef COMPONENTS_AUTOFILL_IOS_BROWSER_AUTOFILL_DRIVER_IOS_FACTORY_TEST_API_H_
 #define COMPONENTS_AUTOFILL_IOS_BROWSER_AUTOFILL_DRIVER_IOS_FACTORY_TEST_API_H_
 
-#include "components/autofill/core/browser/foundations/autofill_driver_test_api.h"
-#include "components/autofill/ios/browser/autofill_driver_ios_factory.h"
+#import "components/autofill/core/browser/foundations/autofill_driver_test_api.h"
+#import "components/autofill/ios/browser/autofill_driver_ios_factory.h"
 
 namespace web {
 class WebFrame;
diff --git a/components/autofill/ios/browser/autofill_driver_ios_test_api.h b/components/autofill/ios/browser/autofill_driver_ios_test_api.h
index 426921f..e9d73774 100644
--- a/components/autofill/ios/browser/autofill_driver_ios_test_api.h
+++ b/components/autofill/ios/browser/autofill_driver_ios_test_api.h
@@ -5,9 +5,9 @@
 #ifndef COMPONENTS_AUTOFILL_IOS_BROWSER_AUTOFILL_DRIVER_IOS_TEST_API_H_
 #define COMPONENTS_AUTOFILL_IOS_BROWSER_AUTOFILL_DRIVER_IOS_TEST_API_H_
 
-#include "components/autofill/core/browser/foundations/autofill_driver_test_api.h"
-#include "components/autofill/core/browser/foundations/autofill_manager_test_api.h"
-#include "components/autofill/ios/browser/autofill_driver_ios.h"
+#import "components/autofill/core/browser/foundations/autofill_driver_test_api.h"
+#import "components/autofill/core/browser/foundations/autofill_manager_test_api.h"
+#import "components/autofill/ios/browser/autofill_driver_ios.h"
 
 namespace autofill {
 
diff --git a/components/autofill/ios/browser/autofill_manager_observer_bridge.h b/components/autofill/ios/browser/autofill_manager_observer_bridge.h
index 46b7d07..d0c8926 100644
--- a/components/autofill/ios/browser/autofill_manager_observer_bridge.h
+++ b/components/autofill/ios/browser/autofill_manager_observer_bridge.h
@@ -7,7 +7,7 @@
 
 #import <Foundation/Foundation.h>
 
-#include "components/autofill/core/browser/foundations/autofill_manager.h"
+#import "components/autofill/core/browser/foundations/autofill_manager.h"
 
 // Objective-C version of the AutofillManager::Observer interface. Only the
 // methods that are currently in use in Objective-C are implemented.
diff --git a/components/autofill/ios/browser/autofill_util_unittests.mm b/components/autofill/ios/browser/autofill_util_unittests.mm
index b9addca..fd04906 100644
--- a/components/autofill/ios/browser/autofill_util_unittests.mm
+++ b/components/autofill/ios/browser/autofill_util_unittests.mm
@@ -4,7 +4,7 @@
 
 #import "components/autofill/ios/browser/autofill_util.h"
 
-#include <variant>
+#import <variant>
 
 #import "base/memory/scoped_refptr.h"
 #import "base/strings/utf_string_conversions.h"
diff --git a/components/autofill/ios/browser/credit_card_save_manager_test_observer_bridge.h b/components/autofill/ios/browser/credit_card_save_manager_test_observer_bridge.h
index 498091b..9854ed4 100644
--- a/components/autofill/ios/browser/credit_card_save_manager_test_observer_bridge.h
+++ b/components/autofill/ios/browser/credit_card_save_manager_test_observer_bridge.h
@@ -7,7 +7,7 @@
 
 #import <Foundation/Foundation.h>
 
-#include "components/autofill/core/browser/payments/credit_card_save_manager.h"
+#import "components/autofill/core/browser/payments/credit_card_save_manager.h"
 
 // A protocol to be adopted by EarlGrey tests to get notified of actions that
 // occur in autofill::CreditCardSaveManager.
diff --git a/components/autofill/ios/browser/credit_card_save_manager_test_observer_bridge.mm b/components/autofill/ios/browser/credit_card_save_manager_test_observer_bridge.mm
index 668435a2..50fd242 100644
--- a/components/autofill/ios/browser/credit_card_save_manager_test_observer_bridge.mm
+++ b/components/autofill/ios/browser/credit_card_save_manager_test_observer_bridge.mm
@@ -4,7 +4,7 @@
 
 #import "components/autofill/ios/browser/credit_card_save_manager_test_observer_bridge.h"
 
-#include "base/check.h"
+#import "base/check.h"
 
 namespace autofill {
 
diff --git a/components/autofill/ios/browser/credit_card_util.h b/components/autofill/ios/browser/credit_card_util.h
index 88d8c5d..3ffd3c53 100644
--- a/components/autofill/ios/browser/credit_card_util.h
+++ b/components/autofill/ios/browser/credit_card_util.h
@@ -7,7 +7,7 @@
 
 #import <Foundation/Foundation.h>
 
-#include <string>
+#import <string>
 
 namespace autofill {
 
diff --git a/components/autofill/ios/browser/credit_card_util.mm b/components/autofill/ios/browser/credit_card_util.mm
index 89bd47c2..cadd784a 100644
--- a/components/autofill/ios/browser/credit_card_util.mm
+++ b/components/autofill/ios/browser/credit_card_util.mm
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/autofill/ios/browser/credit_card_util.h"
+#import "components/autofill/ios/browser/credit_card_util.h"
 
-#include "base/strings/sys_string_conversions.h"
-#include "components/autofill/core/browser/autofill_type.h"
-#include "components/autofill/core/browser/data_model/payments/credit_card.h"
+#import "base/strings/sys_string_conversions.h"
+#import "components/autofill/core/browser/autofill_type.h"
+#import "components/autofill/core/browser/data_model/payments/credit_card.h"
 
 namespace autofill {
 
diff --git a/components/autofill/ios/browser/fake_autofill_agent.mm b/components/autofill/ios/browser/fake_autofill_agent.mm
index 4e1c288..f82cb6e7 100644
--- a/components/autofill/ios/browser/fake_autofill_agent.mm
+++ b/components/autofill/ios/browser/fake_autofill_agent.mm
@@ -4,9 +4,9 @@
 
 #import "components/autofill/ios/browser/fake_autofill_agent.h"
 
-#include "base/functional/bind.h"
-#include "ios/web/public/thread/web_task_traits.h"
-#include "ios/web/public/thread/web_thread.h"
+#import "base/functional/bind.h"
+#import "ios/web/public/thread/web_task_traits.h"
+#import "ios/web/public/thread/web_thread.h"
 
 using autofill::FormRendererId;
 using autofill::FieldRendererId;
diff --git a/components/autofill/ios/browser/form_fetch_batcher.mm b/components/autofill/ios/browser/form_fetch_batcher.mm
index 805b20f..164ddb9 100644
--- a/components/autofill/ios/browser/form_fetch_batcher.mm
+++ b/components/autofill/ios/browser/form_fetch_batcher.mm
@@ -4,12 +4,12 @@
 
 #import "components/autofill/ios/browser/form_fetch_batcher.h"
 
-#include <algorithm>
+#import <algorithm>
 
-#include "base/functional/bind.h"
+#import "base/functional/bind.h"
 #import "base/memory/weak_ptr.h"
 #import "base/metrics/histogram_functions.h"
-#include "base/not_fatal_until.h"
+#import "base/not_fatal_until.h"
 #import "base/task/task_runner.h"
 #import "base/time/time.h"
 #import "components/autofill/core/common/form_data.h"
diff --git a/components/autofill/ios/browser/form_suggestion_provider.h b/components/autofill/ios/browser/form_suggestion_provider.h
index 8f7725a..a5521cb 100644
--- a/components/autofill/ios/browser/form_suggestion_provider.h
+++ b/components/autofill/ios/browser/form_suggestion_provider.h
@@ -5,7 +5,7 @@
 #ifndef COMPONENTS_AUTOFILL_IOS_BROWSER_FORM_SUGGESTION_PROVIDER_H_
 #define COMPONENTS_AUTOFILL_IOS_BROWSER_FORM_SUGGESTION_PROVIDER_H_
 
-#include "components/autofill/core/browser/filling/filling_product.h"
+#import "components/autofill/core/browser/filling/filling_product.h"
 #import "components/autofill/ios/browser/form_suggestion.h"
 #import "components/autofill/ios/browser/form_suggestion_provider_query.h"
 
diff --git a/components/autofill/ios/browser/form_suggestion_provider_query.h b/components/autofill/ios/browser/form_suggestion_provider_query.h
index 9276040..bd94d70 100644
--- a/components/autofill/ios/browser/form_suggestion_provider_query.h
+++ b/components/autofill/ios/browser/form_suggestion_provider_query.h
@@ -7,7 +7,7 @@
 
 #import <Foundation/Foundation.h>
 
-#include "components/autofill/core/common/unique_ids.h"
+#import "components/autofill/core/common/unique_ids.h"
 
 namespace {
 // The "password" field type does not explicitly mean that the field contains a
diff --git a/components/autofill/ios/browser/form_suggestion_provider_query_unittests.mm b/components/autofill/ios/browser/form_suggestion_provider_query_unittests.mm
index 5ee8620..fee66b4 100644
--- a/components/autofill/ios/browser/form_suggestion_provider_query_unittests.mm
+++ b/components/autofill/ios/browser/form_suggestion_provider_query_unittests.mm
@@ -4,8 +4,8 @@
 
 #import "components/autofill/ios/browser/form_suggestion_provider_query.h"
 
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/platform_test.h"
+#import "testing/gtest/include/gtest/gtest.h"
+#import "testing/platform_test.h"
 
 using autofill::FormRendererId;
 using autofill::FieldRendererId;
diff --git a/components/autofill/ios/browser/ios_test_event_waiter.h b/components/autofill/ios/browser/ios_test_event_waiter.h
index 40c329a..fd0f66f0 100644
--- a/components/autofill/ios/browser/ios_test_event_waiter.h
+++ b/components/autofill/ios/browser/ios_test_event_waiter.h
@@ -5,7 +5,7 @@
 #ifndef COMPONENTS_AUTOFILL_IOS_BROWSER_IOS_TEST_EVENT_WAITER_H_
 #define COMPONENTS_AUTOFILL_IOS_BROWSER_IOS_TEST_EVENT_WAITER_H_
 
-#include <list>
+#import <list>
 
 #import "base/test/ios/wait_util.h"
 #import "base/time/time.h"
diff --git a/components/autofill/ios/browser/personal_data_manager_observer_bridge.h b/components/autofill/ios/browser/personal_data_manager_observer_bridge.h
index 37874f6..c946d81 100644
--- a/components/autofill/ios/browser/personal_data_manager_observer_bridge.h
+++ b/components/autofill/ios/browser/personal_data_manager_observer_bridge.h
@@ -7,7 +7,7 @@
 
 #import <Foundation/Foundation.h>
 
-#include "components/autofill/core/browser/data_manager/personal_data_manager_observer.h"
+#import "components/autofill/core/browser/data_manager/personal_data_manager_observer.h"
 
 // PersonalDataManagerObserver is used by PersonalDataManager to informs its
 // client implemented in Objective-C when it has finished loading personal data
diff --git a/components/autofill/ios/browser/personal_data_manager_observer_bridge.mm b/components/autofill/ios/browser/personal_data_manager_observer_bridge.mm
index 4c24bc3..9e5618d 100644
--- a/components/autofill/ios/browser/personal_data_manager_observer_bridge.mm
+++ b/components/autofill/ios/browser/personal_data_manager_observer_bridge.mm
@@ -4,7 +4,7 @@
 
 #import "components/autofill/ios/browser/personal_data_manager_observer_bridge.h"
 
-#include "base/check.h"
+#import "base/check.h"
 
 namespace autofill {
 
diff --git a/components/autofill/ios/browser/suggestion_controller_java_script_feature.h b/components/autofill/ios/browser/suggestion_controller_java_script_feature.h
index 22f1eb6..2491ab6 100644
--- a/components/autofill/ios/browser/suggestion_controller_java_script_feature.h
+++ b/components/autofill/ios/browser/suggestion_controller_java_script_feature.h
@@ -5,11 +5,11 @@
 #ifndef COMPONENTS_AUTOFILL_IOS_BROWSER_SUGGESTION_CONTROLLER_JAVA_SCRIPT_FEATURE_H_
 #define COMPONENTS_AUTOFILL_IOS_BROWSER_SUGGESTION_CONTROLLER_JAVA_SCRIPT_FEATURE_H_
 
-#include <string>
+#import <string>
 
-#include "base/functional/callback.h"
-#include "base/no_destructor.h"
-#include "ios/web/public/js_messaging/java_script_feature.h"
+#import "base/functional/callback.h"
+#import "base/no_destructor.h"
+#import "ios/web/public/js_messaging/java_script_feature.h"
 
 namespace web {
 class WebFrame;
diff --git a/components/autofill/ios/browser/suggestion_controller_java_script_feature.mm b/components/autofill/ios/browser/suggestion_controller_java_script_feature.mm
index 5a1587a6b..64cef3f 100644
--- a/components/autofill/ios/browser/suggestion_controller_java_script_feature.mm
+++ b/components/autofill/ios/browser/suggestion_controller_java_script_feature.mm
@@ -6,11 +6,11 @@
 
 #import <Foundation/Foundation.h>
 
-#include "base/functional/bind.h"
-#include "base/no_destructor.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/time/time.h"
-#include "base/values.h"
+#import "base/functional/bind.h"
+#import "base/no_destructor.h"
+#import "base/strings/sys_string_conversions.h"
+#import "base/time/time.h"
+#import "base/values.h"
 #import "components/autofill/ios/browser/autofill_java_script_feature.h"
 #import "components/autofill/ios/common/javascript_feature_util.h"
 
diff --git a/components/autofill/ios/browser/test_autofill_manager_injector.h b/components/autofill/ios/browser/test_autofill_manager_injector.h
index bc3e894..8d590110 100644
--- a/components/autofill/ios/browser/test_autofill_manager_injector.h
+++ b/components/autofill/ios/browser/test_autofill_manager_injector.h
@@ -5,13 +5,13 @@
 #ifndef COMPONENTS_AUTOFILL_IOS_BROWSER_TEST_AUTOFILL_MANAGER_INJECTOR_H_
 #define COMPONENTS_AUTOFILL_IOS_BROWSER_TEST_AUTOFILL_MANAGER_INJECTOR_H_
 
-#include <ranges>
+#import <ranges>
 
-#include "base/check_deref.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/raw_ref.h"
-#include "base/scoped_observation.h"
-#include "components/autofill/core/browser/foundations/autofill_client.h"
+#import "base/check_deref.h"
+#import "base/memory/raw_ptr.h"
+#import "base/memory/raw_ref.h"
+#import "base/scoped_observation.h"
+#import "components/autofill/core/browser/foundations/autofill_client.h"
 #import "components/autofill/core/browser/foundations/autofill_driver_test_api.h"
 #import "components/autofill/core/browser/foundations/autofill_manager_test_api.h"
 #import "components/autofill/core/browser/foundations/browser_autofill_manager.h"
diff --git a/components/autofill/ios/form_util/autofill_test_with_web_state.mm b/components/autofill/ios/form_util/autofill_test_with_web_state.mm
index b1c5abb9..5c110c5 100644
--- a/components/autofill/ios/form_util/autofill_test_with_web_state.mm
+++ b/components/autofill/ios/form_util/autofill_test_with_web_state.mm
@@ -7,7 +7,7 @@
 #import "base/test/ios/wait_util.h"
 #import "components/autofill/ios/form_util/form_handlers_java_script_feature.h"
 #import "components/autofill/ios/form_util/form_util_java_script_feature.h"
-#include "ios/web/public/js_messaging/web_frame.h"
+#import "ios/web/public/js_messaging/web_frame.h"
 #import "ios/web/public/test/js_test_util.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
 #import "ios/web/public/web_client.h"
diff --git a/components/autofill/ios/form_util/form_activity_observer_bridge.h b/components/autofill/ios/form_util/form_activity_observer_bridge.h
index 1351296..4e53ba87 100644
--- a/components/autofill/ios/form_util/form_activity_observer_bridge.h
+++ b/components/autofill/ios/form_util/form_activity_observer_bridge.h
@@ -7,8 +7,8 @@
 
 #import <Foundation/Foundation.h>
 
-#include "base/memory/raw_ptr.h"
-#include "components/autofill/ios/form_util/form_activity_observer.h"
+#import "base/memory/raw_ptr.h"
+#import "components/autofill/ios/form_util/form_activity_observer.h"
 
 @protocol FormActivityObserver<NSObject>
 @optional
diff --git a/components/autofill/ios/form_util/form_activity_tab_helper.h b/components/autofill/ios/form_util/form_activity_tab_helper.h
index 03dd3080..b33a4cab 100644
--- a/components/autofill/ios/form_util/form_activity_tab_helper.h
+++ b/components/autofill/ios/form_util/form_activity_tab_helper.h
@@ -5,9 +5,9 @@
 #ifndef COMPONENTS_AUTOFILL_IOS_FORM_UTIL_FORM_ACTIVITY_TAB_HELPER_H_
 #define COMPONENTS_AUTOFILL_IOS_FORM_UTIL_FORM_ACTIVITY_TAB_HELPER_H_
 
-#include "base/observer_list.h"
-#include "base/values.h"
-#include "ios/web/public/web_state_observer.h"
+#import "base/observer_list.h"
+#import "base/values.h"
+#import "ios/web/public/web_state_observer.h"
 #import "ios/web/public/web_state_user_data.h"
 
 namespace web {
diff --git a/components/autofill/ios/form_util/form_activity_tab_helper_fuzzer.mm b/components/autofill/ios/form_util/form_activity_tab_helper_fuzzer.mm
index 98f74222..7481d6e 100644
--- a/components/autofill/ios/form_util/form_activity_tab_helper_fuzzer.mm
+++ b/components/autofill/ios/form_util/form_activity_tab_helper_fuzzer.mm
@@ -2,21 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/autofill/ios/form_util/form_activity_tab_helper.h"
+#import "components/autofill/ios/form_util/form_activity_tab_helper.h"
 
-#include "base/logging.h"
+#import "base/logging.h"
 #import "base/memory/raw_ptr.h"
-#include "base/rand_util.h"
+#import "base/rand_util.h"
 #import "base/test/ios/wait_util.h"
-#include "ios/web/public/js_messaging/fuzzer_support/fuzzer_util.h"
-#include "ios/web/public/js_messaging/fuzzer_support/js_message.pb.h"
-#include "ios/web/public/js_messaging/script_message.h"
-#include "ios/web/public/js_messaging/web_frame.h"
+#import "ios/web/public/js_messaging/fuzzer_support/fuzzer_util.h"
+#import "ios/web/public/js_messaging/fuzzer_support/js_message.pb.h"
+#import "ios/web/public/js_messaging/script_message.h"
+#import "ios/web/public/js_messaging/web_frame.h"
 #import "ios/web/public/js_messaging/web_frames_manager.h"
-#include "ios/web/public/test/fuzzer_env_with_web_state.h"
-#include "ios/web/public/test/web_state_test_util.h"
+#import "ios/web/public/test/fuzzer_env_with_web_state.h"
+#import "ios/web/public/test/web_state_test_util.h"
 #import "ios/web/public/web_state.h"
-#include "testing/libfuzzer/proto/lpm_interface.h"
+#import "testing/libfuzzer/proto/lpm_interface.h"
 
 using base::test::ios::kWaitForJSCompletionTimeout;
 using base::test::ios::WaitUntilConditionOrTimeout;
diff --git a/components/autofill/ios/form_util/form_util_java_script_feature.h b/components/autofill/ios/form_util/form_util_java_script_feature.h
index 277f717f..932cff42 100644
--- a/components/autofill/ios/form_util/form_util_java_script_feature.h
+++ b/components/autofill/ios/form_util/form_util_java_script_feature.h
@@ -5,7 +5,7 @@
 #ifndef COMPONENTS_AUTOFILL_IOS_FORM_UTIL_FORM_UTIL_JAVA_SCRIPT_FEATURE_H_
 #define COMPONENTS_AUTOFILL_IOS_FORM_UTIL_FORM_UTIL_JAVA_SCRIPT_FEATURE_H_
 
-#include "base/no_destructor.h"
+#import "base/no_destructor.h"
 #import "ios/web/public/js_messaging/java_script_feature.h"
 
 namespace autofill {
diff --git a/components/autofill/ios/form_util/test_form_activity_tab_helper.mm b/components/autofill/ios/form_util/test_form_activity_tab_helper.mm
index 3306262..fa90dc8 100644
--- a/components/autofill/ios/form_util/test_form_activity_tab_helper.mm
+++ b/components/autofill/ios/form_util/test_form_activity_tab_helper.mm
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/autofill/ios/form_util/test_form_activity_tab_helper.h"
+#import "components/autofill/ios/form_util/test_form_activity_tab_helper.h"
 
-#include "base/observer_list.h"
-#include "components/autofill/ios/form_util/form_activity_observer.h"
-#include "components/autofill/ios/form_util/form_activity_params.h"
-#include "components/autofill/ios/form_util/form_activity_tab_helper.h"
+#import "base/observer_list.h"
+#import "components/autofill/ios/form_util/form_activity_observer.h"
+#import "components/autofill/ios/form_util/form_activity_params.h"
+#import "components/autofill/ios/form_util/form_activity_tab_helper.h"
 
 namespace autofill {
 TestFormActivityTabHelper::TestFormActivityTabHelper(web::WebState* web_state)
diff --git a/components/browser_ui/strings/android/BUILD.gn b/components/browser_ui/strings/android/BUILD.gn
index 9ab16990..4424ff66 100644
--- a/components/browser_ui/strings/android/BUILD.gn
+++ b/components/browser_ui/strings/android/BUILD.gn
@@ -2,13 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/locales.gni")
 import("//tools/grit/grit_rule.gni")
 
 java_strings_grd("browser_ui_strings_grd") {
   grd_file = "browser_ui_strings.grd"
-  outputs = [ "values/browser_ui_strings.xml" ] +
-            process_file_template(
-                android_bundle_locales_as_resources,
-                [ "values-{{source_name_part}}/browser_ui_strings.xml" ])
 }
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/tile/TileView.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/tile/TileView.java
index 8fd84b7..a8eec93 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/tile/TileView.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/tile/TileView.java
@@ -130,4 +130,9 @@
             mOnFocusViaSelectionListener.run();
         }
     }
+
+    /** Returns whether the tile can be moved using drag-and-drop. */
+    public boolean isDraggable() {
+        return false;
+    }
 }
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json
index 4abb8e0..875a4822 100644
--- a/components/certificate_transparency/data/log_list.json
+++ b/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@
 {
-  "version": "53.35",
-  "log_list_timestamp": "2025-05-06T12:54:31Z",
+  "version": "53.36",
+  "log_list_timestamp": "2025-05-07T12:54:09Z",
   "operators": [
     {
       "name": "Google",
diff --git a/components/content_settings/android/cookie_controls_bridge.cc b/components/content_settings/android/cookie_controls_bridge.cc
index 0bb6703..4259041 100644
--- a/components/content_settings/android/cookie_controls_bridge.cc
+++ b/components/content_settings/android/cookie_controls_bridge.cc
@@ -87,7 +87,7 @@
 
 void CookieControlsBridge::OnCookieControlsIconStatusChanged(
     bool icon_visible,
-    bool protections_on,
+    CookieControlsState controls_state,
     CookieBlocking3pcdStatus blocking_status,
     bool should_highlight) {
   // This function's main use is for web's User Bypass icon, which
diff --git a/components/content_settings/android/cookie_controls_bridge.h b/components/content_settings/android/cookie_controls_bridge.h
index 00e8e49..a09fa34f 100644
--- a/components/content_settings/android/cookie_controls_bridge.h
+++ b/components/content_settings/android/cookie_controls_bridge.h
@@ -60,7 +60,7 @@
 
   void OnCookieControlsIconStatusChanged(
       bool icon_visible,
-      bool protections_on,
+      CookieControlsState controls_state,
       CookieBlocking3pcdStatus blocking_status,
       bool should_highlight) override;
 
diff --git a/components/content_settings/browser/ui/cookie_controls_controller.cc b/components/content_settings/browser/ui/cookie_controls_controller.cc
index e8bc3361..5b9bdb2 100644
--- a/components/content_settings/browser/ui/cookie_controls_controller.cc
+++ b/components/content_settings/browser/ui/cookie_controls_controller.cc
@@ -169,9 +169,8 @@
     observer.OnStatusChanged(status.controls_state, status.enforcement,
                              status.blocking_status, status.expiration);
     observer.OnCookieControlsIconStatusChanged(
-        icon_visible,
-        status.controls_state == CookieControlsState::k3pcsBlocked,
-        status.blocking_status, should_highlight);
+        icon_visible, status.controls_state, status.blocking_status,
+        should_highlight);
   }
 }
 
@@ -454,9 +453,8 @@
       ShouldHighlightUserBypass(status.controls_state);
   for (auto& observer : observers_) {
     observer.OnCookieControlsIconStatusChanged(
-        icon_visible,
-        status.controls_state == CookieControlsState::k3pcsBlocked,
-        status.blocking_status, should_highlight);
+        icon_visible, status.controls_state, status.blocking_status,
+        should_highlight);
   }
 }
 
diff --git a/components/content_settings/browser/ui/cookie_controls_view.h b/components/content_settings/browser/ui/cookie_controls_view.h
index 8cefef144..fdc2fb1c 100644
--- a/components/content_settings/browser/ui/cookie_controls_view.h
+++ b/components/content_settings/browser/ui/cookie_controls_view.h
@@ -34,10 +34,8 @@
   virtual void OnCookieControlsIconStatusChanged(
       // Whether to show the user bypass icon.
       bool icon_visible,
-      // Whether protections (3PC blocking and ACT features) are on for the
-      // current site. NOTE: for the 3PC toggle this is true when the toggle is
-      // off/3PC are blocked.
-      bool protections_on,
+      // The state of the controls for the UI to change.
+      CookieControlsState controls_state,
       // 3PC blocking status for 3PCD: whether 3PC are limited or all blocked.
       CookieBlocking3pcdStatus blocking_status,
       // Whether we should highlight the user bypass icon.
diff --git a/components/cronet/android/java/src/org/chromium/net/telemetry/CronetLoggerImpl.java b/components/cronet/android/java/src/org/chromium/net/telemetry/CronetLoggerImpl.java
index 26ec659..9fc9901 100644
--- a/components/cronet/android/java/src/org/chromium/net/telemetry/CronetLoggerImpl.java
+++ b/components/cronet/android/java/src/org/chromium/net/telemetry/CronetLoggerImpl.java
@@ -89,7 +89,7 @@
                     /* httpFlagsKeys*/ new long[] {},
                     /* httpFlagsValues*/ new long[] {},
                     info.cronetImplVersion,
-                    convertToProtoCronetEngineCreatedSource(info.source),
+                    convertToProtoCronetEngineBuilderInitializedSource(info.source),
                     Process.myUid());
         }
     }
@@ -224,7 +224,7 @@
                     convertToProtoFailureReason(trafficInfo.getFailureReason()),
                     OptionalBoolean.fromBoolean(trafficInfo.getIsSocketReused()).getValue(),
                     trafficInfo.getCronetVersion(),
-                    convertToProtoCronetEngineCreatedSource(trafficInfo.getCronetSource()));
+                    convertToProtoCronetEngineBuilderInitializedSource(trafficInfo.getCronetSource()));
         } catch (Exception e) {
             // using addAndGet because another thread might have modified samplesRateLimited's value
             mSamplesRateLimited.addAndGet(samplesRateLimitedCount);
@@ -319,6 +319,8 @@
                 return CronetStatsLog.CRONET_ENGINE_CREATED__SOURCE__CRONET_SOURCE_GMSCORE_DYNAMITE;
             case CRONET_SOURCE_FALLBACK:
                 return CronetStatsLog.CRONET_ENGINE_CREATED__SOURCE__CRONET_SOURCE_FALLBACK;
+            case CRONET_SOURCE_PLATFORM:
+                return CronetStatsLog.CRONET_ENGINE_CREATED__SOURCE__CRONET_SOURCE_PLATFORM;
             case CRONET_SOURCE_UNSPECIFIED:
                 return CronetStatsLog.CRONET_ENGINE_CREATED__SOURCE__CRONET_SOURCE_UNSPECIFIED;
             default:
diff --git a/components/cronet/android/lint-baseline.xml b/components/cronet/android/lint-baseline.xml
index 6203927..52c06b0 100644
--- a/components/cronet/android/lint-baseline.xml
+++ b/components/cronet/android/lint-baseline.xml
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.11.0-alpha07" type="baseline" client="" dependencies="true" name="" variant="all" version="8.11.0-alpha07">
+<issues format="6" by="lint 8.11.0-alpha09" type="baseline" client="" dependencies="true" name="" variant="all" version="8.11.0-alpha09">
 
 </issues>
diff --git a/components/download/internal/common/download_item_impl.cc b/components/download/internal/common/download_item_impl.cc
index f52b8cc..f0248e7 100644
--- a/components/download/internal/common/download_item_impl.cc
+++ b/components/download/internal/common/download_item_impl.cc
@@ -1469,6 +1469,15 @@
 
   DCHECK(AllDataSaved());
   destination_info_.end_time = base::Time::Now();
+#if BUILDFLAG(IS_ANDROID)
+  if (GetTargetFilePath().IsContentUri()) {
+    GetDownloadTaskRunner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            base::IgnoreResult(&DownloadCollectionBridge::PublishDownload),
+            GetTargetFilePath()));
+  }
+#endif
   TransitionTo(COMPLETE_INTERNAL);
   UpdateObservers();
 }
diff --git a/components/download/internal/common/download_utils.cc b/components/download/internal/common/download_utils.cc
index 93b15e7..be6122a 100644
--- a/components/download/internal/common/download_utils.cc
+++ b/components/download/internal/common/download_utils.cc
@@ -803,6 +803,25 @@
   std::move(callback).Run(virtual_path, base::FilePath());
 }
 
+#if BUILDFLAG(IS_ANDROID)
+// Determine the file path for the save package file given the `suggested_path`.
+COMPONENTS_DOWNLOAD_EXPORT
+void DetermineSavePackagePath(const GURL& url,
+                              const base::FilePath& suggested_path,
+                              LocalPathCallback callback) {
+  base::FilePath mhtml_path = suggested_path.ReplaceExtension("mhtml");
+  if (DownloadCollectionBridge::ShouldPublishDownload(mhtml_path)) {
+    GetDownloadTaskRunner()->PostTaskAndReplyWithResult(
+        FROM_HERE,
+        base::BindOnce(&CreateIntermediateUri, url, GURL(), mhtml_path,
+                       mhtml_path.BaseName(), "multipart/related"),
+        base::BindOnce(&OnInterMediateUriCreated, std::move(callback)));
+    return;
+  }
+  std::move(callback).Run(mhtml_path, mhtml_path.BaseName());
+}
+#endif
+
 bool IsInterruptedDownloadAutoResumable(download::DownloadItem* download_item,
                                         int auto_resumption_size_limit) {
   DCHECK_EQ(download::DownloadItem::INTERRUPTED, download_item->GetState());
diff --git a/components/download/public/common/download_utils.h b/components/download/public/common/download_utils.h
index 385b544..f6d9094 100644
--- a/components/download/public/common/download_utils.h
+++ b/components/download/public/common/download_utils.h
@@ -129,6 +129,14 @@
                         const base::FilePath& virtual_path,
                         LocalPathCallback callback);
 
+#if BUILDFLAG(IS_ANDROID)
+// Determine the file path for the save package file given the `suggested_path`.
+COMPONENTS_DOWNLOAD_EXPORT
+void DetermineSavePackagePath(const GURL& url,
+                              const base::FilePath& suggested_path,
+                              LocalPathCallback callback);
+#endif
+
 // Finch parameter key value for number of bytes used for content validation
 // during resumption.
 constexpr char kDownloadContentValidationLengthFinchKey[] =
diff --git a/components/embedder_support/android/BUILD.gn b/components/embedder_support/android/BUILD.gn
index f98e1ae..f5071d32 100644
--- a/components/embedder_support/android/BUILD.gn
+++ b/components/embedder_support/android/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/config/android/rules.gni")
-import("//build/config/locales.gni")
 import("//third_party/jni_zero/jni_zero.gni")
 import("//tools/grit/grit_rule.gni")
 
@@ -178,9 +177,6 @@
 
 java_strings_grd("web_contents_delegate_strings_grd") {
   grd_file = "java/strings/web_contents_delegate_android_strings.grd"
-  outputs = [ "values/web_contents_delegate_android_strings.xml" ] + process_file_template(
-                android_bundle_locales_as_resources,
-                [ "values-{{source_name_part}}/web_contents_delegate_android_strings.xml" ])
 }
 
 static_library("web_contents_delegate") {
diff --git a/components/enterprise/client_certificates/core/certificate_provisioning_service.cc b/components/enterprise/client_certificates/core/certificate_provisioning_service.cc
index 8719633..16522b9c 100644
--- a/components/enterprise/client_certificates/core/certificate_provisioning_service.cc
+++ b/components/enterprise/client_certificates/core/certificate_provisioning_service.cc
@@ -110,6 +110,10 @@
     return context_delegate_->GetPolicyPref();
   }
 
+  const std::string logging_context() const {
+    return context_delegate_->GetLoggingContext();
+  }
+
   PrefChangeRegistrar pref_observer_;
   raw_ptr<PrefService> pref_service_;
   raw_ptr<CertificateStore> certificate_store_;
@@ -226,18 +230,30 @@
     LOG_POLICY(ERROR, DEVICE_TRUST)
         << "Permanent identity loading failed: "
         << StoreErrorToString(expected_permanent_identity.error());
-    OnProvisioningError(ProvisioningError::kIdentityLoadingFailed,
-                        expected_permanent_identity.error());
-    return;
+
+    // Loading the private key can fail if, somehow, the private key was lost.
+    // This can happen in some backup and restore scenarios. If that happens,
+    // simply treat the failure as if no permanent identity existed in the
+    // first place.
+    if (expected_permanent_identity.error() != StoreError::kLoadKeyFailed) {
+      OnProvisioningError(ProvisioningError::kIdentityLoadingFailed,
+                          expected_permanent_identity.error());
+      return;
+    }
+
+    LOG_POLICY(INFO, DEVICE_TRUST)
+        << "Failed to load the serialized private key, provisioning a new "
+           "identity as fallback...";
   }
 
   // Setting as certificate creation by default, more specific scenarios will
   // overwrite this value later.
   provisioning_context_->scenario = ProvisioningScenario::kCertificateCreation;
 
-  std::optional<ClientIdentity>& permanent_identity_optional =
-      expected_permanent_identity.value();
-  if (permanent_identity_optional.has_value()) {
+  if (expected_permanent_identity.has_value() &&
+      expected_permanent_identity->has_value()) {
+    std::optional<ClientIdentity>& permanent_identity_optional =
+        expected_permanent_identity.value();
     if (permanent_identity_optional->is_valid()) {
       // Already have a full identity, so cache it.
       cached_identity_ = permanent_identity_optional.value();
@@ -348,7 +364,7 @@
   scoped_refptr<PrivateKey> private_key =
       std::move(expected_private_key.value());
   if (private_key) {
-    LogPrivateKeyCreationSource(private_key->GetSource());
+    LogPrivateKeyCreationSource(logging_context(), private_key->GetSource());
   }
 
   LOG_POLICY(INFO, DEVICE_TRUST) << "Fetching a certificate from the server...";
@@ -366,7 +382,7 @@
     HttpCodeOrClientError upload_code,
     scoped_refptr<net::X509Certificate> certificate) {
   last_upload_code_ = upload_code;
-  LogCertificateCreationResponse(upload_code, !!certificate);
+  LogCertificateCreationResponse(logging_context(), upload_code, !!certificate);
 
   if (!certificate) {
     if (last_upload_code_->has_value()) {
@@ -445,12 +461,14 @@
 void CertificateProvisioningServiceImpl::OnProvisioningError(
     ProvisioningError provisioning_error,
     std::optional<StoreError> store_error) {
-  LogProvisioningError(provisioning_error, std::move(store_error));
+  LogProvisioningError(logging_context(), provisioning_error,
+                       std::move(store_error));
   OnFinishedProvisioning(/*success=*/false);
 }
 
 void CertificateProvisioningServiceImpl::OnFinishedProvisioning(bool success) {
-  LogProvisioningContext(provisioning_context_.value(), success);
+  LogProvisioningContext(logging_context(), provisioning_context_.value(),
+                         success);
   provisioning_context_.reset();
 
   std::optional<ClientIdentity> identity =
diff --git a/components/enterprise/client_certificates/core/certificate_provisioning_service_unittest.cc b/components/enterprise/client_certificates/core/certificate_provisioning_service_unittest.cc
index a8800129..62e74f3 100644
--- a/components/enterprise/client_certificates/core/certificate_provisioning_service_unittest.cc
+++ b/components/enterprise/client_certificates/core/certificate_provisioning_service_unittest.cc
@@ -50,6 +50,7 @@
 constexpr int kSuccessUploadCode = 200;
 constexpr char kIdentityName[] = "IdentityName";
 constexpr char kTempIdentityName[] = "TempIdentityName";
+constexpr char kLoggingContext[] = "Profile";
 
 scoped_refptr<net::X509Certificate> LoadTestCert() {
   static constexpr char kTestCertFileName[] = "client_1.pem";
@@ -142,6 +143,19 @@
     task_environment_.AdvanceClock(base::Days(500 * 365));
   }
 
+  std::unique_ptr<MockContextDelegate> CreateContextDelegate() {
+    auto context_delegate = std::make_unique<MockContextDelegate>();
+    EXPECT_CALL(*context_delegate, GetPolicyPref())
+        .WillRepeatedly(Return(pref()));
+    EXPECT_CALL(*context_delegate, GetIdentityName())
+        .WillRepeatedly(Return(kIdentityName));
+    EXPECT_CALL(*context_delegate, GetTemporaryIdentityName())
+        .WillRepeatedly(Return(kTempIdentityName));
+    EXPECT_CALL(*context_delegate, GetLoggingContext())
+        .WillRepeatedly(Return(kLoggingContext));
+    return context_delegate;
+  }
+
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
 
@@ -161,18 +175,6 @@
   EXPECT_CALL(mock_store_, GetIdentity(kIdentityName, _))
       .WillOnce(RunOnceCallback<1>(std::nullopt));
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(11)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(4)
-      .WillRepeatedly(Return(kIdentityName));
-  EXPECT_CALL(*mock_context_delegate, GetTemporaryIdentityName())
-      .Times(2)
-      .WillRepeatedly(Return(kTempIdentityName));
-
   auto mocked_private_key = base::MakeRefCounted<StrictMock<MockPrivateKey>>();
   auto fake_cert = LoadTestCert();
   EXPECT_CALL(mock_store_, CreatePrivateKey(kTempIdentityName, _))
@@ -187,8 +189,7 @@
               CommitIdentity(kTempIdentityName, kIdentityName, fake_cert, _))
       .WillOnce(RunOnceCallback<3>(std::nullopt));
 
-  CreateProvisioningService(std::move(mock_context_delegate),
-                            std::move(mock_client));
+  CreateProvisioningService(CreateContextDelegate(), std::move(mock_client));
 
   VerifySuccessState(mocked_private_key, fake_cert);
 
@@ -228,18 +229,6 @@
   EXPECT_CALL(mock_store_, GetIdentity(kIdentityName, _))
       .WillOnce(RunOnceCallback<1>(std::nullopt));
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(8)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(4)
-      .WillRepeatedly(Return(kIdentityName));
-  EXPECT_CALL(*mock_context_delegate, GetTemporaryIdentityName())
-      .Times(2)
-      .WillRepeatedly(Return(kTempIdentityName));
-
   auto mocked_private_key = base::MakeRefCounted<StrictMock<MockPrivateKey>>();
   auto fake_cert = LoadTestCert();
   EXPECT_CALL(mock_store_, CreatePrivateKey(kTempIdentityName, _))
@@ -254,8 +243,7 @@
               CommitIdentity(kTempIdentityName, kIdentityName, fake_cert, _))
       .WillOnce(RunOnceCallback<3>(std::nullopt));
 
-  CreateProvisioningService(std::move(mock_context_delegate),
-                            std::move(mock_client));
+  CreateProvisioningService(CreateContextDelegate(), std::move(mock_client));
 
   SetPolicyPref(true);
 
@@ -284,15 +272,6 @@
        CreatedWithPref_ExistingIdentityLoaded) {
   SetPolicyPref(true);
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(3)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(2)
-      .WillRepeatedly(Return(kIdentityName));
-
   auto mocked_private_key = base::MakeRefCounted<StrictMock<MockPrivateKey>>();
   auto fake_cert = LoadTestCert();
   ClientIdentity existing_permanent_identity(kIdentityName, mocked_private_key,
@@ -302,7 +281,7 @@
       .WillOnce(RunOnceCallback<1>(existing_permanent_identity));
 
   CreateProvisioningService(
-      std::move(mock_context_delegate),
+      CreateContextDelegate(),
       std::make_unique<StrictMock<MockKeyUploadClient>>());
   EXPECT_EQ(
       histogram_tester_.GetTotalCountsForPrefix("Enterprise.ClientCertificate")
@@ -317,15 +296,6 @@
        CreatedWithPref_ExistingIdentity_NoCertificate) {
   SetPolicyPref(true);
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(7)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(4)
-      .WillRepeatedly(Return(kIdentityName));
-
   auto mocked_private_key = base::MakeRefCounted<StrictMock<MockPrivateKey>>();
   ClientIdentity existing_permanent_identity(kIdentityName, mocked_private_key,
                                              /*certificate=*/nullptr);
@@ -341,8 +311,7 @@
 
   EXPECT_CALL(mock_store_, CommitCertificate(kIdentityName, fake_cert, _))
       .WillOnce(RunOnceCallback<2>(std::nullopt));
-  CreateProvisioningService(std::move(mock_context_delegate),
-                            std::move(mock_client));
+  CreateProvisioningService(CreateContextDelegate(), std::move(mock_client));
 
   VerifySuccessState(mocked_private_key, fake_cert);
 
@@ -371,23 +340,13 @@
        CreatedWithPref_Empty_GetIdentityFails) {
   SetPolicyPref(true);
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(7)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(2)
-      .WillRepeatedly(Return(kIdentityName));
-
   base::OnceCallback<void(StoreErrorOr<std::optional<ClientIdentity>>)>
       get_identity_callback;
   EXPECT_CALL(mock_store_, GetIdentity(kIdentityName, _))
       .WillOnce(MoveArg<1>(&get_identity_callback));
 
   auto mock_client = std::make_unique<StrictMock<MockKeyUploadClient>>();
-  CreateProvisioningService(std::move(mock_context_delegate),
-                            std::move(mock_client));
+  CreateProvisioningService(CreateContextDelegate(), std::move(mock_client));
   ASSERT_TRUE(service_);
 
   base::test::TestFuture<std::optional<ClientIdentity>> test_future;
@@ -419,6 +378,36 @@
       4U);
 }
 
+// Tests what happens when the GetIdentity provisioning step fails with
+// a key loading store error. The provisioning service should then provision
+// a new identity.
+TEST_F(CertificateProvisioningServiceTest,
+       CreatedWithPref_Empty_GetIdentityFailsDueToKeyLoad_Fallback) {
+  EXPECT_CALL(mock_store_, GetIdentity(kIdentityName, _))
+      .WillOnce(
+          RunOnceCallback<1>(base::unexpected(StoreError::kLoadKeyFailed)));
+
+  auto mocked_private_key = base::MakeRefCounted<StrictMock<MockPrivateKey>>();
+  auto fake_cert = LoadTestCert();
+  EXPECT_CALL(mock_store_, CreatePrivateKey(kTempIdentityName, _))
+      .WillOnce(RunOnceCallback<1>(mocked_private_key));
+
+  auto mock_client = std::make_unique<StrictMock<MockKeyUploadClient>>();
+  EXPECT_CALL(*mock_client,
+              CreateCertificate(testing::Eq(mocked_private_key), _))
+      .WillOnce(RunOnceCallback<1>(kSuccessUploadCode, fake_cert));
+
+  EXPECT_CALL(mock_store_,
+              CommitIdentity(kTempIdentityName, kIdentityName, fake_cert, _))
+      .WillOnce(RunOnceCallback<3>(std::nullopt));
+
+  CreateProvisioningService(CreateContextDelegate(), std::move(mock_client));
+  ASSERT_TRUE(service_);
+  SetPolicyPref(true);
+
+  VerifySuccessState(mocked_private_key, fake_cert);
+}
+
 // Tests what happens when the CreateKey provisioning step fails.
 TEST_F(CertificateProvisioningServiceTest,
        CreatedWithPref_Empty_CreateKeyFails) {
@@ -433,19 +422,7 @@
 
   auto mock_client = std::make_unique<StrictMock<MockKeyUploadClient>>();
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(7)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(2)
-      .WillRepeatedly(Return(kIdentityName));
-  EXPECT_CALL(*mock_context_delegate, GetTemporaryIdentityName())
-      .WillOnce(Return(kTempIdentityName));
-
-  CreateProvisioningService(std::move(mock_context_delegate),
-                            std::move(mock_client));
+  CreateProvisioningService(CreateContextDelegate(), std::move(mock_client));
   ASSERT_TRUE(service_);
 
   base::test::TestFuture<std::optional<ClientIdentity>> test_future;
@@ -466,17 +443,6 @@
   EXPECT_CALL(mock_store_, GetIdentity(kIdentityName, _))
       .WillOnce(RunOnceCallback<1>(std::nullopt));
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(7)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(2)
-      .WillRepeatedly(Return(kIdentityName));
-  EXPECT_CALL(*mock_context_delegate, GetTemporaryIdentityName())
-      .WillOnce(Return(kTempIdentityName));
-
   auto mocked_private_key = base::MakeRefCounted<StrictMock<MockPrivateKey>>();
   EXPECT_CALL(mock_store_, CreatePrivateKey(kTempIdentityName, _))
       .WillOnce(RunOnceCallback<1>(mocked_private_key));
@@ -487,8 +453,7 @@
               CreateCertificate(testing::Eq(mocked_private_key), _))
       .WillOnce(MoveArg<1>(&create_certificate_callback));
 
-  CreateProvisioningService(std::move(mock_context_delegate),
-                            std::move(mock_client));
+  CreateProvisioningService(CreateContextDelegate(), std::move(mock_client));
   ASSERT_TRUE(service_);
 
   base::test::TestFuture<std::optional<ClientIdentity>> test_future;
@@ -511,17 +476,6 @@
   EXPECT_CALL(mock_store_, GetIdentity(kIdentityName, _))
       .WillOnce(RunOnceCallback<1>(std::nullopt));
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(7)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(2)
-      .WillRepeatedly(Return(kIdentityName));
-  EXPECT_CALL(*mock_context_delegate, GetTemporaryIdentityName())
-      .WillOnce(Return(kTempIdentityName));
-
   auto mocked_private_key = base::MakeRefCounted<StrictMock<MockPrivateKey>>();
   EXPECT_CALL(mock_store_, CreatePrivateKey(kTempIdentityName, _))
       .WillOnce(RunOnceCallback<1>(mocked_private_key));
@@ -532,8 +486,7 @@
               CreateCertificate(testing::Eq(mocked_private_key), _))
       .WillOnce(MoveArg<1>(&create_certificate_callback));
 
-  CreateProvisioningService(std::move(mock_context_delegate),
-                            std::move(mock_client));
+  CreateProvisioningService(CreateContextDelegate(), std::move(mock_client));
   ASSERT_TRUE(service_);
 
   base::test::TestFuture<std::optional<ClientIdentity>> test_future;
@@ -556,18 +509,6 @@
       .WillOnce(RunOnceCallback<1>(
           base::unexpected(StoreError::kConflictingIdentity)));
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(7)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(4)
-      .WillRepeatedly(Return(kIdentityName));
-  EXPECT_CALL(*mock_context_delegate, GetTemporaryIdentityName())
-      .Times(3)
-      .WillRepeatedly(Return(kTempIdentityName));
-
   auto mocked_private_key = base::MakeRefCounted<StrictMock<MockPrivateKey>>();
   ClientIdentity existing_temporary_identity(kTempIdentityName,
                                              mocked_private_key,
@@ -585,8 +526,7 @@
               CommitIdentity(kTempIdentityName, kIdentityName, fake_cert, _))
       .WillOnce(RunOnceCallback<3>(std::nullopt));
 
-  CreateProvisioningService(std::move(mock_context_delegate),
-                            std::move(mock_client));
+  CreateProvisioningService(CreateContextDelegate(), std::move(mock_client));
 
   VerifySuccessState(mocked_private_key, fake_cert);
 }
@@ -605,20 +545,8 @@
       .WillOnce(RunOnceCallback<1>(
           base::unexpected(StoreError::kInvalidDatabaseState)));
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(5)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(2)
-      .WillRepeatedly(Return(kIdentityName));
-  EXPECT_CALL(*mock_context_delegate, GetTemporaryIdentityName())
-      .Times(2)
-      .WillRepeatedly(Return(kTempIdentityName));
-
   CreateProvisioningService(
-      std::move(mock_context_delegate),
+      CreateContextDelegate(),
       std::make_unique<StrictMock<MockKeyUploadClient>>());
 
   VerifyIdledWithoutCache();
@@ -637,20 +565,8 @@
   EXPECT_CALL(mock_store_, GetIdentity(kTempIdentityName, _))
       .WillOnce(RunOnceCallback<1>(std::nullopt));
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(5)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(2)
-      .WillRepeatedly(Return(kIdentityName));
-  EXPECT_CALL(*mock_context_delegate, GetTemporaryIdentityName())
-      .Times(2)
-      .WillRepeatedly(Return(kTempIdentityName));
-
   CreateProvisioningService(
-      std::move(mock_context_delegate),
+      CreateContextDelegate(),
       std::make_unique<StrictMock<MockKeyUploadClient>>());
 
   VerifyIdledWithoutCache();
@@ -674,20 +590,8 @@
   EXPECT_CALL(mock_store_, GetIdentity(kTempIdentityName, _))
       .WillOnce(RunOnceCallback<1>(existing_temporary_identity));
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(5)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(2)
-      .WillRepeatedly(Return(kIdentityName));
-  EXPECT_CALL(*mock_context_delegate, GetTemporaryIdentityName())
-      .Times(2)
-      .WillRepeatedly(Return(kTempIdentityName));
-
   CreateProvisioningService(
-      std::move(mock_context_delegate),
+      CreateContextDelegate(),
       std::make_unique<StrictMock<MockKeyUploadClient>>());
 
   VerifyIdledWithoutCache();
@@ -715,20 +619,7 @@
               CommitIdentity(kTempIdentityName, kIdentityName, fake_cert, _))
       .WillOnce(MoveArg<3>(&commit_identity_callback));
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(7)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(3)
-      .WillRepeatedly(Return(kIdentityName));
-  EXPECT_CALL(*mock_context_delegate, GetTemporaryIdentityName())
-      .Times(2)
-      .WillRepeatedly(Return(kTempIdentityName));
-
-  CreateProvisioningService(std::move(mock_context_delegate),
-                            std::move(mock_client));
+  CreateProvisioningService(CreateContextDelegate(), std::move(mock_client));
   ASSERT_TRUE(service_);
 
   base::test::TestFuture<std::optional<ClientIdentity>> test_future;
@@ -764,17 +655,7 @@
   EXPECT_CALL(mock_store_, CommitCertificate(kIdentityName, fake_cert, _))
       .WillOnce(MoveArg<2>(&commit_cert_callback));
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(7)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(3)
-      .WillRepeatedly(Return(kIdentityName));
-
-  CreateProvisioningService(std::move(mock_context_delegate),
-                            std::move(mock_client));
+  CreateProvisioningService(CreateContextDelegate(), std::move(mock_client));
   ASSERT_TRUE(service_);
 
   base::test::TestFuture<std::optional<ClientIdentity>> test_future;
@@ -809,14 +690,7 @@
               CreateCertificate(testing::Eq(mocked_private_key), _))
       .WillOnce(RunOnceCallback<1>(kSuccessUploadCode, fake_cert));
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(5)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(4)
-      .WillRepeatedly(Return(kIdentityName));
+  auto mock_context_delegate = CreateContextDelegate();
   EXPECT_CALL(*mock_context_delegate,
               OnClientCertificateDeleted(expired_test_cert));
 
@@ -871,15 +745,8 @@
               CreateCertificate(testing::Eq(mocked_private_key), _))
       .WillOnce(RunOnceCallback<1>(kSuccessUploadCode, nullptr));
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
+  auto mock_context_delegate = CreateContextDelegate();
   auto* mock_context_delegate_ptr = mock_context_delegate.get();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(11)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(6)
-      .WillRepeatedly(Return(kIdentityName));
 
   CreateProvisioningService(std::move(mock_context_delegate),
                             std::move(mock_client));
@@ -934,20 +801,7 @@
               CommitIdentity(kTempIdentityName, kIdentityName, fake_cert, _))
       .WillOnce(RunOnceCallback<3>(std::nullopt));
 
-  auto mock_context_delegate =
-      std::make_unique<StrictMock<MockContextDelegate>>();
-  EXPECT_CALL(*mock_context_delegate, GetPolicyPref())
-      .Times(9)
-      .WillRepeatedly(Return(pref()));
-  EXPECT_CALL(*mock_context_delegate, GetIdentityName())
-      .Times(4)
-      .WillRepeatedly(Return(kIdentityName));
-  EXPECT_CALL(*mock_context_delegate, GetTemporaryIdentityName())
-      .Times(2)
-      .WillRepeatedly(Return(kTempIdentityName));
-
-  CreateProvisioningService(std::move(mock_context_delegate),
-                            std::move(mock_client));
+  CreateProvisioningService(CreateContextDelegate(), std::move(mock_client));
 
   ASSERT_TRUE(service_);
 
diff --git a/components/enterprise/client_certificates/core/context_delegate.h b/components/enterprise/client_certificates/core/context_delegate.h
index f6d899b..0432543 100644
--- a/components/enterprise/client_certificates/core/context_delegate.h
+++ b/components/enterprise/client_certificates/core/context_delegate.h
@@ -32,6 +32,9 @@
 
   // Returns the pref associated with a policy for the current context.
   virtual std::string GetPolicyPref() = 0;
+
+  // Returns the logging context used by histogram variants.
+  virtual std::string GetLoggingContext() = 0;
 };
 
 }  // namespace client_certificates
diff --git a/components/enterprise/client_certificates/core/metrics_util.cc b/components/enterprise/client_certificates/core/metrics_util.cc
index 8c202b7..3df04cc 100644
--- a/components/enterprise/client_certificates/core/metrics_util.cc
+++ b/components/enterprise/client_certificates/core/metrics_util.cc
@@ -29,67 +29,83 @@
 
 }  // namespace
 
-void LogProvisioningError(ProvisioningError provisioning_error,
+void LogProvisioningError(const std::string& logging_context,
+                          ProvisioningError provisioning_error,
                           std::optional<StoreError> store_error) {
   static constexpr char kProvisioningErrorHistogram[] =
-      "Enterprise.ClientCertificate.Profile.Provisioning.Error";
-  base::UmaHistogramEnumeration(kProvisioningErrorHistogram,
-                                provisioning_error);
+      "Enterprise.ClientCertificate.%s.Provisioning.Error";
+  base::UmaHistogramEnumeration(
+      base::StringPrintf(kProvisioningErrorHistogram, logging_context.c_str()),
+      provisioning_error);
 
   if (store_error.has_value()) {
     static constexpr char kProvisioningStoreErrorHistogram[] =
-        "Enterprise.ClientCertificate.Profile.Provisioning.Store.Error";
-    base::UmaHistogramEnumeration(kProvisioningStoreErrorHistogram,
-                                  store_error.value());
+        "Enterprise.ClientCertificate.%s.Provisioning.Store.Error";
+    base::UmaHistogramEnumeration(
+        base::StringPrintf(kProvisioningStoreErrorHistogram,
+                           logging_context.c_str()),
+        store_error.value());
   }
 }
 
-void LogCertificateCreationResponse(HttpCodeOrClientError upload_code,
+void LogCertificateCreationResponse(const std::string& logging_context,
+                                    HttpCodeOrClientError upload_code,
                                     bool has_certificate) {
   if (!upload_code.has_value()) {
     static constexpr char kCreateCertificateClientErrorHistogram[] =
-        "Enterprise.ClientCertificate.Profile.CreateCertificate.ClientError";
-    base::UmaHistogramEnumeration(kCreateCertificateClientErrorHistogram,
-                                  upload_code.error());
+        "Enterprise.ClientCertificate.%s.CreateCertificate.ClientError";
+    base::UmaHistogramEnumeration(
+        base::StringPrintf(kCreateCertificateClientErrorHistogram,
+                           logging_context.c_str()),
+        upload_code.error());
     return;
   }
 
   static constexpr char kCreateCertificateCodeHistogram[] =
-      "Enterprise.ClientCertificate.Profile.CreateCertificate.UploadCode";
-  base::UmaHistogramSparse(kCreateCertificateCodeHistogram,
+      "Enterprise.ClientCertificate.%s.CreateCertificate.UploadCode";
+  base::UmaHistogramSparse(base::StringPrintf(kCreateCertificateCodeHistogram,
+                                              logging_context.c_str()),
                            upload_code.value());
 
   if (upload_code.value() / 100 == 2) {
     static constexpr char kCreateCertificateSuccessHasCertHistogram[] =
-        "Enterprise.ClientCertificate.Profile.CreateCertificate.Success."
+        "Enterprise.ClientCertificate.%s.CreateCertificate.Success."
         "HasCert";
-    base::UmaHistogramBoolean(kCreateCertificateSuccessHasCertHistogram,
-                              has_certificate);
+    base::UmaHistogramBoolean(
+        base::StringPrintf(kCreateCertificateSuccessHasCertHistogram,
+                           logging_context.c_str()),
+        has_certificate);
   }
 }
 
-void LogProvisioningContext(ProvisioningContext context, bool success) {
+void LogProvisioningContext(const std::string& logging_context,
+                            ProvisioningContext context,
+                            bool success) {
   static constexpr char kProvisioningOutcomeHistogramFormat[] =
-      "Enterprise.ClientCertificate.Profile.Provisioning.%s.Outcome";
+      "Enterprise.ClientCertificate.%s.Provisioning.%s.Outcome";
   static constexpr char kProvisioningLatencyHistogramFormat[] =
-      "Enterprise.ClientCertificate.Profile.Provisioning.%s.%s.Latency";
+      "Enterprise.ClientCertificate.%s.Provisioning.%s.%s.Latency";
 
   auto scenario_string = ProvisioningScenarioToString(context.scenario);
   base::UmaHistogramBoolean(
       base::StringPrintf(kProvisioningOutcomeHistogramFormat,
-                         scenario_string.data()),
+                         logging_context.c_str(), scenario_string.data()),
       success);
   base::UmaHistogramTimes(
       base::StringPrintf(kProvisioningLatencyHistogramFormat,
-                         scenario_string.data(),
+                         logging_context.c_str(), scenario_string.data(),
                          SuccessToString(success).data()),
       base::TimeTicks::Now() - context.start_time);
 }
 
-void LogPrivateKeyCreationSource(PrivateKeySource source) {
+void LogPrivateKeyCreationSource(const std::string& logging_context,
+                                 PrivateKeySource source) {
   static constexpr char kCreatePrivateKeySourceHistogram[] =
-      "Enterprise.ClientCertificate.Profile.CreatePrivateKey.Source";
-  base::UmaHistogramEnumeration(kCreatePrivateKeySourceHistogram, source);
+      "Enterprise.ClientCertificate.%s.CreatePrivateKey.Source";
+  base::UmaHistogramEnumeration(
+      base::StringPrintf(kCreatePrivateKeySourceHistogram,
+                         logging_context.c_str()),
+      source);
 }
 
 }  // namespace client_certificates
diff --git a/components/enterprise/client_certificates/core/metrics_util.h b/components/enterprise/client_certificates/core/metrics_util.h
index 1547139..0769049e 100644
--- a/components/enterprise/client_certificates/core/metrics_util.h
+++ b/components/enterprise/client_certificates/core/metrics_util.h
@@ -39,15 +39,20 @@
   ProvisioningScenario scenario{ProvisioningScenario::kUnknown};
 };
 
-void LogProvisioningError(ProvisioningError provisioning_error,
+void LogProvisioningError(const std::string& logging_context,
+                          ProvisioningError provisioning_error,
                           std::optional<StoreError> store_error);
 
-void LogCertificateCreationResponse(HttpCodeOrClientError upload_code,
+void LogCertificateCreationResponse(const std::string& logging_context,
+                                    HttpCodeOrClientError upload_code,
                                     bool has_certificate);
 
-void LogProvisioningContext(ProvisioningContext context, bool success);
+void LogProvisioningContext(const std::string& logging_context,
+                            ProvisioningContext context,
+                            bool success);
 
-void LogPrivateKeyCreationSource(PrivateKeySource source);
+void LogPrivateKeyCreationSource(const std::string& logging_context,
+                                 PrivateKeySource source);
 
 }  // namespace client_certificates
 
diff --git a/components/enterprise/client_certificates/core/mock_context_delegate.h b/components/enterprise/client_certificates/core/mock_context_delegate.h
index 3147067c..aa7b635 100644
--- a/components/enterprise/client_certificates/core/mock_context_delegate.h
+++ b/components/enterprise/client_certificates/core/mock_context_delegate.h
@@ -24,6 +24,7 @@
   MOCK_METHOD(std::string, GetIdentityName, (), (override));
   MOCK_METHOD(std::string, GetTemporaryIdentityName, (), (override));
   MOCK_METHOD(std::string, GetPolicyPref, (), (override));
+  MOCK_METHOD(std::string, GetLoggingContext, (), (override));
 };
 
 }  // namespace client_certificates
diff --git a/components/enterprise/connectors/core/reporting_event_router.h b/components/enterprise/connectors/core/reporting_event_router.h
index 2f313c61..fcfcbc7 100644
--- a/components/enterprise/connectors/core/reporting_event_router.h
+++ b/components/enterprise/connectors/core/reporting_event_router.h
@@ -32,10 +32,10 @@
 
   bool IsEventEnabled(const std::string& event);
 
-  void OnLoginEvent(const GURL& url,
-                    bool is_federated,
-                    const url::SchemeHostPort& federated_origin,
-                    const std::u16string& username);
+  virtual void OnLoginEvent(const GURL& url,
+                            bool is_federated,
+                            const url::SchemeHostPort& federated_origin,
+                            const std::u16string& username);
 
   void OnPasswordBreach(
       const std::string& trigger,
diff --git a/components/feed/core/v2/public/public_types.cc b/components/feed/core/v2/public/public_types.cc
index 07c59b3..9f5d67c 100644
--- a/components/feed/core/v2/public/public_types.cc
+++ b/components/feed/core/v2/public/public_types.cc
@@ -20,9 +20,6 @@
   DCHECK_EQ(gaia.empty(), email.empty());
   return gaia.empty();
 }
-bool AccountInfo::operator==(const AccountInfo& rhs) const {
-  return tie(gaia, email) == tie(rhs.gaia, rhs.email);
-}
 
 std::ostream& operator<<(std::ostream& os, const AccountInfo& o) {
   if (o.IsEmpty()) {
diff --git a/components/feed/core/v2/public/types.h b/components/feed/core/v2/public/types.h
index 449e1aee..407b1208 100644
--- a/components/feed/core/v2/public/types.h
+++ b/components/feed/core/v2/public/types.h
@@ -28,8 +28,7 @@
   AccountInfo();
   AccountInfo(const GaiaId& gaia, const std::string& email);
   explicit AccountInfo(CoreAccountInfo account_info);
-  bool operator==(const AccountInfo& rhs) const;
-  bool operator!=(const AccountInfo& rhs) const { return !(*this == rhs); }
+  friend bool operator==(const AccountInfo&, const AccountInfo&) = default;
   bool IsEmpty() const;
 
   GaiaId gaia;
diff --git a/components/javascript_dialogs/android/BUILD.gn b/components/javascript_dialogs/android/BUILD.gn
index a1838e5..9b0ebd7 100644
--- a/components/javascript_dialogs/android/BUILD.gn
+++ b/components/javascript_dialogs/android/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/config/android/rules.gni")
-import("//build/config/locales.gni")
 import("//third_party/jni_zero/jni_zero.gni")
 import("//tools/grit/grit_rule.gni")
 
@@ -49,7 +48,4 @@
 
 java_strings_grd("javascript_dialogs_strings_grd") {
   grd_file = "javascript_dialogs_android_strings.grd"
-  outputs = [ "values/javascript_dialogs_android_strings.xml" ] + process_file_template(
-                android_bundle_locales_as_resources,
-                [ "values-{{source_name_part}}/javascript_dialogs_android_strings.xml" ])
 }
diff --git a/components/language/ios/browser/language_detection_javascript_unittest.mm b/components/language/ios/browser/language_detection_javascript_unittest.mm
index 8bd156c..9b113340 100644
--- a/components/language/ios/browser/language_detection_javascript_unittest.mm
+++ b/components/language/ios/browser/language_detection_javascript_unittest.mm
@@ -210,6 +210,43 @@
 }
 
 // Tests if `__gCrWeb.languageDetection.detectLanguage` correctly informs the
+// native side when the notranslate html attribute is specified
+TEST_F(LanguageDetectionJavascriptTest, DetectLanguageWithHTMLNoTranslate) {
+  // A simple page using the notranslate meta tag.
+  NSString* html = @"<html translate='no'><head>"
+                   @"<meta http-equiv='content-language' content='foo'>"
+                   @"</head></html>";
+  LoadHtml(html);
+  ASSERT_TRUE(TriggerLanguageDetection());
+
+  ASSERT_TRUE(handler().lastReceivedMessage.body[@"httpContentLanguage"]);
+  EXPECT_NSEQ(@"foo",
+              handler().lastReceivedMessage.body[@"httpContentLanguage"]);
+  ASSERT_TRUE(handler().lastReceivedMessage.body[@"hasNoTranslate"]);
+  EXPECT_TRUE(
+      [handler().lastReceivedMessage.body[@"hasNoTranslate"] boolValue]);
+}
+
+// Tests if `__gCrWeb.languageDetection.detectLanguage` does not confuse a body
+// or div language='no' for a page wide translate disabling.
+TEST_F(LanguageDetectionJavascriptTest, DetectLanguageWithDIVNoTranslate) {
+  // A simple page using the notranslate meta tag.
+  NSString* html = @"<html><head>"
+                   @"<meta http-equiv='content-language' content='foo'>"
+                   @"</head><body translate='no'>"
+                   @"<div translate='no'>test</div></html>";
+  LoadHtml(html);
+  ASSERT_TRUE(TriggerLanguageDetection());
+
+  ASSERT_TRUE(handler().lastReceivedMessage.body[@"httpContentLanguage"]);
+  EXPECT_NSEQ(@"foo",
+              handler().lastReceivedMessage.body[@"httpContentLanguage"]);
+  ASSERT_TRUE(handler().lastReceivedMessage.body[@"hasNoTranslate"]);
+  EXPECT_FALSE(
+      [handler().lastReceivedMessage.body[@"hasNoTranslate"] boolValue]);
+}
+
+// Tests if `__gCrWeb.languageDetection.detectLanguage` correctly informs the
 // native side when no notranslate meta tag is specified.
 TEST_F(LanguageDetectionJavascriptTest, DetectLanguageWithoutNoTranslateMeta) {
   // A simple page using the notranslate meta tag.
diff --git a/components/language/ios/browser/resources/language_detection.ts b/components/language/ios/browser/resources/language_detection.ts
index ea92c656..05a3318 100644
--- a/components/language/ios/browser/resources/language_detection.ts
+++ b/components/language/ios/browser/resources/language_detection.ts
@@ -22,9 +22,16 @@
 
 /**
  * Searches page elements for "notranslate" meta tag.
- * @return  true if "notranslate" meta tag is defined.
+ * @return  true if "notranslate" meta tag is defined or the translate attribute
+ * equal to no on html document.
  */
 function hasNoTranslate(): boolean {
+  if (document.documentElement.hasAttribute('translate')) {
+    if (document.documentElement.getAttribute('translate')!.toLowerCase() ===
+        'no') {
+      return true;
+    }
+  }
   for (const metaTag of document.getElementsByTagName('meta')) {
     if (metaTag.name === 'google') {
       if (metaTag.content === 'notranslate' ||
diff --git a/components/live_caption/views/caption_bubble.cc b/components/live_caption/views/caption_bubble.cc
index 58bda27..92ec2326 100644
--- a/components/live_caption/views/caption_bubble.cc
+++ b/components/live_caption/views/caption_bubble.cc
@@ -43,6 +43,7 @@
 #include "ui/events/event.h"
 #include "ui/gfx/animation/slide_animation.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/menus/simple_menu_model.h"
@@ -278,7 +279,7 @@
         buttons_(buttons) {
     auto border = std::make_unique<views::BubbleBorder>(
         views::BubbleBorder::FLOAT, views::BubbleBorder::DIALOG_SHADOW);
-    border->SetCornerRadius(kCornerRadiusDip);
+    border->set_rounded_corners(gfx::RoundedCornersF(kCornerRadiusDip));
     views::BubbleFrameView::SetBubbleBorder(std::move(border));
   }
 
diff --git a/components/messages/android/DEPS b/components/messages/android/DEPS
index 32a168c..943e51a 100644
--- a/components/messages/android/DEPS
+++ b/components/messages/android/DEPS
@@ -8,7 +8,6 @@
   "+components/browser_ui/widget/android",
   "+components/browser_ui/styles/android",
   "+third_party/skia/include",
-  "+ui/accessibility/android",
   "+ui/android",
   "+ui/gfx/android",
 ]
diff --git a/components/messages/android/internal/BUILD.gn b/components/messages/android/internal/BUILD.gn
index fae626d..3895eaa 100644
--- a/components/messages/android/internal/BUILD.gn
+++ b/components/messages/android/internal/BUILD.gn
@@ -40,7 +40,6 @@
     "//third_party/androidx:androidx_core_core_java",
     "//third_party/androidx:androidx_interpolator_interpolator_java",
     "//third_party/androidx:androidx_swiperefreshlayout_swiperefreshlayout_java",
-    "//ui/accessibility:ax_base_java",
     "//ui/android:ui_full_java",
     "//url:gurl_java",
   ]
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java
index 0058d1b..039e652 100644
--- a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java
+++ b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java
@@ -8,17 +8,14 @@
 import android.content.res.Resources;
 import android.provider.Settings;
 import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.core.view.ViewCompat;
-import androidx.core.view.accessibility.AccessibilityEventCompat;
 
 import org.chromium.base.supplier.Supplier;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.components.messages.MessageStateHandler.Position;
-import org.chromium.ui.accessibility.AccessibilityState;
 import org.chromium.ui.listmenu.ListMenuHost.PopupMenuShownListener;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
@@ -201,7 +198,6 @@
                 () -> {
                     setOnTouchRunnable(null);
                     setOnTitleChanged(null);
-                    sendPaneChangeAccessibilityEvent(/* isShowing= */ false);
                     messageHidden.run();
                 });
     }
@@ -229,29 +225,6 @@
             msg = mView.getResources().getString(R.string.message_new_actions_available);
         }
         ViewCompat.setAccessibilityPaneTitle(mParentView, msg);
-        sendPaneChangeAccessibilityEvent(/* isShowing= */ true);
-    }
-
-    /**
-     * Sends accessibility events for pane appearance/disappearance when the message is shown/hidden
-     * respectively. This should ideally move accessibility focus automatically to/out of the
-     * message view as applicable.
-     *
-     * @param isShowing Whether the message is visible. {@code true} if shown, {@code false} if
-     *     hidden.
-     */
-    @SuppressWarnings("WrongConstant")
-    private void sendPaneChangeAccessibilityEvent(boolean isShowing) {
-        if (!AccessibilityState.isAnyAccessibilityServiceEnabled()) return;
-        AccessibilityEvent event =
-                AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-        if (isShowing) {
-            event.setContentChangeTypes(AccessibilityEventCompat.CONTENT_CHANGE_TYPE_PANE_APPEARED);
-        } else {
-            event.setContentChangeTypes(
-                    AccessibilityEventCompat.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
-        }
-        mView.requestSendAccessibilityEvent(mView, event);
     }
 
     private void setOnTitleChanged(@Nullable Runnable runnable) {
diff --git a/components/neterror/resources/BUILD.gn b/components/neterror/resources/BUILD.gn
index f0497f5..172a8e1 100644
--- a/components/neterror/resources/BUILD.gn
+++ b/components/neterror/resources/BUILD.gn
@@ -24,7 +24,7 @@
   "dino_game/night_mode.ts",
   "dino_game/obstacle.ts",
   "dino_game/offline.js",
-  "dino_game/offline-sprite-definitions.js",
+  "dino_game/offline_sprite_definitions.ts",
   "dino_game/trex.js",
   "dino_game/utils.ts",
 ]
diff --git a/components/neterror/resources/dino_game/background_el.ts b/components/neterror/resources/dino_game/background_el.ts
index cc88a85..1e6438d 100644
--- a/components/neterror/resources/dino_game/background_el.ts
+++ b/components/neterror/resources/dino_game/background_el.ts
@@ -6,6 +6,7 @@
 
 import {IS_HIDPI} from './constants.js';
 import {Runner} from './offline.js';
+import type {SpriteDefinition} from './offline_sprite_definitions.js';
 import type {SpritePosition} from './sprite_position.js';
 import {getRandomNum, getRunnerImageSprite} from './utils.js';
 
@@ -15,11 +16,10 @@
  */
 function getSpriteConfigForType(type: string): BackgroundElSpriteConfig|null {
   if ('spriteDefinition' in Runner) {
-    const spriteDefinition =
-        Runner.spriteDefinition as RunnerBackgroundElSpriteConfig;
+    const spriteDefinition = Runner.spriteDefinition as SpriteDefinition;
     if (spriteDefinition) {
-      if (spriteDefinition.BACKGROUND_EL[type]) {
-        return spriteDefinition.BACKGROUND_EL[type];
+      if (spriteDefinition.backgroundEl[type]) {
+        return spriteDefinition.backgroundEl[type];
       }
     }
   }
@@ -27,19 +27,18 @@
   return null;
 }
 
-interface RunnerBackgroundElSpriteConfig {
-  BACKGROUND_EL: {[key: string]: BackgroundElSpriteConfig};
-}
-
 export interface BackgroundElSpriteConfig {
   height: number;
   width: number;
   offset: number;
   xPos: number;
   fixed: boolean;
-  fixedXPos: number;
-  fixedYPos1: number;
-  fixedYPos2: number;
+  // Only present when fixed is true.
+  fixedXPos?: number;
+  // Only present when fixed is true.
+  fixedYPos1?: number;
+  // Only present when fixed is true.
+  fixedYPos2?: number;
 }
 
 export interface BackgroundElConfig {
@@ -117,6 +116,7 @@
    */
   init() {
     if (this.spriteConfig.fixed) {
+      assert(this.spriteConfig.fixedXPos);
       this.xPos = this.spriteConfig.fixedXPos;
     }
     this.yPos = getGlobalConfig().yPos - this.spriteConfig.height +
diff --git a/components/neterror/resources/dino_game/game_over_panel.js b/components/neterror/resources/dino_game/game_over_panel.js
index 1baf837..fd5c73b 100644
--- a/components/neterror/resources/dino_game/game_over_panel.js
+++ b/components/neterror/resources/dino_game/game_over_panel.js
@@ -4,8 +4,8 @@
 
 import {IS_HIDPI, IS_RTL} from './constants.js';
 import {Dimensions} from './dimensions.js';
-import {spriteDefinitionByType} from './offline-sprite-definitions.js';
 import {Runner} from './offline.js';
+import {spriteDefinitionByType} from './offline_sprite_definitions.js';
 import {Trex} from './trex.js';
 import {getTimeStamp} from './utils.js';
 
diff --git a/components/neterror/resources/dino_game/horizon.js b/components/neterror/resources/dino_game/horizon.js
index 642f300e..e1f6da6 100644
--- a/components/neterror/resources/dino_game/horizon.js
+++ b/components/neterror/resources/dino_game/horizon.js
@@ -7,8 +7,8 @@
 import {HorizonLine} from './horizon_line.js';
 import {NightMode} from './night_mode.js';
 import {Obstacle, setMaxGapCoefficient as setMaxObstacleGapCoefficient, setMaxObstacleLength} from './obstacle.js';
-import {ObstacleType, spriteDefinitionByType} from './offline-sprite-definitions.js';
 import {Runner} from './offline.js';
+import {ObstacleType, spriteDefinitionByType} from './offline_sprite_definitions.js';
 import {getRandomNum} from './utils.js';
 
 /**
@@ -62,17 +62,17 @@
    * Initialise the horizon. Just add the line and a cloud. No obstacles.
    */
   init() {
-    obstacleTypes = spriteDefinitionByType.original.OBSTACLES;
+    obstacleTypes = spriteDefinitionByType.original.obstacles;
     this.addCloud();
 
     // Multiple Horizon lines
-    for (let i = 0; i < Runner.spriteDefinition.LINES.length; i++) {
+    for (let i = 0; i < Runner.spriteDefinition.lines.length; i++) {
       this.horizonLines.push(
-          new HorizonLine(this.canvas, Runner.spriteDefinition.LINES[i]));
+          new HorizonLine(this.canvas, Runner.spriteDefinition.lines[i]));
     }
 
     this.nightMode =
-        new NightMode(this.canvas, this.spritePos.MOON, this.dimensions.WIDTH);
+        new NightMode(this.canvas, this.spritePos.moon, this.dimensions.height);
   }
 
   /**
@@ -106,18 +106,18 @@
     this.altGameModeActive = true;
     this.spritePos = spritePos;
 
-    obstacleTypes = Runner.spriteDefinition.OBSTACLES;
+    obstacleTypes = Runner.spriteDefinition.obstacles;
     this.adjustObstacleSpeed();
 
-    setMaxObstacleGapCoefficient(Runner.spriteDefinition.MAX_GAP_COEFFICIENT);
-    setMaxObstacleLength(Runner.spriteDefinition.MAX_OBSTACLE_LENGTH);
+    setMaxObstacleGapCoefficient(Runner.spriteDefinition.maxGapCoefficient);
+    setMaxObstacleLength(Runner.spriteDefinition.maxObstacleLength);
 
-    setBackgroundElGlobalConfig(Runner.spriteDefinition.BACKGROUND_EL_CONFIG);
+    setBackgroundElGlobalConfig(Runner.spriteDefinition.backgroundElConfig);
 
     this.horizonLines = [];
-    for (let i = 0; i < Runner.spriteDefinition.LINES.length; i++) {
+    for (let i = 0; i < Runner.spriteDefinition.lines.length; i++) {
       this.horizonLines.push(
-          new HorizonLine(this.canvas, Runner.spriteDefinition.LINES[i]));
+          new HorizonLine(this.canvas, Runner.spriteDefinition.lines[i]));
     }
     this.reset();
   }
@@ -141,7 +141,7 @@
       this.horizonLines[i].update(deltaTime, currentSpeed);
     }
 
-    if (!this.altGameModeActive || Runner.spriteDefinition.HAS_CLOUDS) {
+    if (!this.altGameModeActive || Runner.spriteDefinition.hasClouds) {
       this.nightMode.update(showNightMode);
       this.updateClouds(deltaTime, currentSpeed);
     }
@@ -171,7 +171,7 @@
 
       // Check for adding a new element.
       if (numElements < maxBgEl &&
-          (this.dimensions.WIDTH - lastEl.xPos) > lastEl.gap &&
+          (this.dimensions.width - lastEl.xPos) > lastEl.gap &&
           frequency > Math.random()) {
         bgElAddFunction();
       }
@@ -234,7 +234,7 @@
       if (lastObstacle && !lastObstacle.followingObstacleCreated &&
           lastObstacle.isVisible() &&
           (lastObstacle.xPos + lastObstacle.width + lastObstacle.gap) <
-              this.dimensions.WIDTH) {
+              this.dimensions.width) {
         this.addNewObstacle(currentSpeed);
         lastObstacle.followingObstacleCreated = true;
       }
@@ -254,7 +254,7 @@
    */
   addNewObstacle(currentSpeed) {
     const obstacleCount =
-        obstacleTypes[obstacleTypes.length - 1].type !== 'COLLECTABLE' ||
+        obstacleTypes[obstacleTypes.length - 1].type !== 'collectable' ||
             (Runner.isAltGameModeEnabled() && !this.altGameModeActive ||
              this.altGameModeActive) ?
         obstacleTypes.length - 1 :
@@ -327,15 +327,14 @@
    */
   addCloud() {
     this.clouds.push(
-        new Cloud(this.canvas, this.spritePos.CLOUD, this.dimensions.WIDTH));
+        new Cloud(this.canvas, this.spritePos.cloud, this.dimensions.width));
   }
 
   /**
    * Add a random background element to the horizon.
    */
   addBackgroundEl() {
-    const backgroundElTypes =
-        Object.keys(Runner.spriteDefinition.BACKGROUND_EL);
+    const backgroundElTypes = Object.keys(Runner.spriteDefinition.backgroundEl);
 
     if (backgroundElTypes.length > 0) {
       let index = getRandomNum(0, backgroundElTypes.length - 1);
@@ -349,7 +348,7 @@
 
       this.lastEl = type;
       this.backgroundEls.push(new BackgroundEl(
-          this.canvas, this.spritePos.BACKGROUND_EL, this.dimensions.WIDTH,
+          this.canvas, this.spritePos.backgroundEl, this.dimensions.width,
           type));
     }
   }
diff --git a/components/neterror/resources/dino_game/night_mode.ts b/components/neterror/resources/dino_game/night_mode.ts
index 923bbeb4..bd8ab86 100644
--- a/components/neterror/resources/dino_game/night_mode.ts
+++ b/components/neterror/resources/dino_game/night_mode.ts
@@ -5,7 +5,7 @@
 import {assert} from 'chrome://resources/js/assert.js';
 
 import {IS_HIDPI} from './constants.js';
-import {spriteDefinitionByType} from './offline-sprite-definitions.js';
+import {spriteDefinitionByType} from './offline_sprite_definitions.js';
 import type {SpritePosition} from './sprite_position.js';
 import {getRandomNum, getRunnerOrigImageSprite} from './utils.js';
 
@@ -113,7 +113,7 @@
     let moonSourceX = this.spritePos.x + currentPhaseSpritePosition;
     const moonOutputWidth = moonSourceWidth;
     let starSize = Config.STAR_SIZE;
-    let starSourceX = spriteDefinitionByType.original.LDPI.STAR.x;
+    let starSourceX = spriteDefinitionByType.original.ldpi.star.x;
     const runnerOrigImageSprite = getRunnerOrigImageSprite();
     assert(runnerOrigImageSprite);
 
@@ -122,7 +122,7 @@
       moonSourceHeight *= 2;
       moonSourceX = this.spritePos.x + (currentPhaseSpritePosition * 2);
       starSize *= 2;
-      starSourceX = spriteDefinitionByType.original.HDPI.STAR.x;
+      starSourceX = spriteDefinitionByType.original.hdpi.star.x;
     }
 
     this.canvasCtx.save();
@@ -160,11 +160,11 @@
       };
 
       if (IS_HIDPI) {
-        starPosition.sourceY = spriteDefinitionByType.original.HDPI.STAR.y +
+        starPosition.sourceY = spriteDefinitionByType.original.hdpi.star.y +
             Config.STAR_SIZE * 2 * i;
       } else {
         starPosition.sourceY =
-            spriteDefinitionByType.original.LDPI.STAR.y + Config.STAR_SIZE * i;
+            spriteDefinitionByType.original.ldpi.star.y + Config.STAR_SIZE * i;
       }
 
       this.stars[i] = starPosition;
diff --git a/components/neterror/resources/dino_game/obstacle.ts b/components/neterror/resources/dino_game/obstacle.ts
index 21e6a989..f80df5a 100644
--- a/components/neterror/resources/dino_game/obstacle.ts
+++ b/components/neterror/resources/dino_game/obstacle.ts
@@ -6,8 +6,8 @@
 
 import {FPS, IS_HIDPI, IS_MOBILE} from './constants.js';
 import type {Dimensions} from './dimensions.js';
-import type {ObstacleType} from './offline-sprite-definitions.js';
-import {CollisionBox} from './offline-sprite-definitions.js';
+import type {ObstacleType} from './offline_sprite_definitions.js';
+import {CollisionBox} from './offline_sprite_definitions.js';
 import type {SpritePosition} from './sprite_position.js';
 import {getRandomNum, getRunnerAltCommonImageSprite, getRunnerAltGameImageSprite, getRunnerAudioCues, getRunnerImageSprite, getRunnerSlowdown} from './utils.js';
 
@@ -67,7 +67,7 @@
     this.size = getRandomNum(1, maxObstacleLength);
     this.xPos = dimensions.width + xOffset;
     this.altGameModeActive = isAltGameMode;
-    const imageSprite = this.typeConfig.type === 'COLLECTABLE' ?
+    const imageSprite = this.typeConfig.type === 'collectable' ?
         getRunnerAltCommonImageSprite() :
         this.altGameModeActive ? getRunnerAltGameImageSprite() :
                                  getRunnerImageSprite();
@@ -173,6 +173,7 @@
 
       // Update frame
       if (this.typeConfig.numFrames) {
+        assert(this.typeConfig.frameRate);
         this.timer += deltaTime;
         if (this.timer >= this.typeConfig.frameRate) {
           this.currentFrame =
diff --git a/components/neterror/resources/dino_game/offline-sprite-definitions.js b/components/neterror/resources/dino_game/offline-sprite-definitions.js
deleted file mode 100644
index 0222546..0000000
--- a/components/neterror/resources/dino_game/offline-sprite-definitions.js
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/* @const
- * Add matching sprite definition and config to spriteDefinitionByType.
- */
-export const GAME_TYPE = [];
-
-//******************************************************************************
-
-/**
- * Collision box object.
- * @param {number} x X position.
- * @param {number} y Y Position.
- * @param {number} w Width.
- * @param {number} h Height.
- * @constructor
- */
-export function CollisionBox(x, y, w, h) {
-  this.x = x;
-  this.y = y;
-  this.width = w;
-  this.height = h;
-}
-
-/**
- * Obstacle definitions.
- * minGap: minimum pixel space between obstacles.
- * multipleSpeed: Speed at which multiples are allowed.
- * speedOffset: speed faster / slower than the horizon.
- * minSpeed: Minimum speed which the obstacle can make an appearance.
- *
- * @typedef {{
- *   type: string,
- *   width: number,
- *   height: number,
- *   yPos: number | Array<number>,
- *   yPosMobile: number | Array<number>,
- *   multipleSpeed: number,
- *   minGap: number,
- *   minSpeed: number,
- *   speedOffset: number,
- *   numFrames: number,
- *   frameRate: number,
- *   collisionBoxes: Array<CollisionBox>,
- * }}
- */
-export let ObstacleType;
-
-/**
- * T-Rex runner sprite definitions.
- */
-export const spriteDefinitionByType = {
-  original: {
-    LDPI: {
-      BACKGROUND_EL: {x: 86, y: 2},
-      CACTUS_LARGE: {x: 332, y: 2},
-      CACTUS_SMALL: {x: 228, y: 2},
-      OBSTACLE_2: {x: 332, y: 2},
-      OBSTACLE: {x: 228, y: 2},
-      CLOUD: {x: 86, y: 2},
-      HORIZON: {x: 2, y: 54},
-      MOON: {x: 484, y: 2},
-      PTERODACTYL: {x: 134, y: 2},
-      RESTART: {x: 2, y: 68},
-      TEXT_SPRITE: {x: 655, y: 2},
-      TREX: {x: 848, y: 2},
-      STAR: {x: 645, y: 2},
-      COLLECTABLE: {x: 0, y: 0},
-      ALT_GAME_END: {x: 32, y: 0},
-    },
-    HDPI: {
-      BACKGROUND_EL: {x: 166, y: 2},
-      CACTUS_LARGE: {x: 652, y: 2},
-      CACTUS_SMALL: {x: 446, y: 2},
-      OBSTACLE_2: {x: 652, y: 2},
-      OBSTACLE: {x: 446, y: 2},
-      CLOUD: {x: 166, y: 2},
-      HORIZON: {x: 2, y: 104},
-      MOON: {x: 954, y: 2},
-      PTERODACTYL: {x: 260, y: 2},
-      RESTART: {x: 2, y: 130},
-      TEXT_SPRITE: {x: 1294, y: 2},
-      TREX: {x: 1678, y: 2},
-      STAR: {x: 1276, y: 2},
-      COLLECTABLE: {x: 0, y: 0},
-      ALT_GAME_END: {x: 64, y: 0},
-    },
-    MAX_GAP_COEFFICIENT: 1.5,
-    MAX_OBSTACLE_LENGTH: 3,
-    HAS_CLOUDS: 1,
-    BOTTOM_PAD: 10,
-    TREX: {
-      WAITING_1: {x: 44, w: 44, h: 47, xOffset: 0},
-      WAITING_2: {x: 0, w: 44, h: 47, xOffset: 0},
-      RUNNING_1: {x: 88, w: 44, h: 47, xOffset: 0},
-      RUNNING_2: {x: 132, w: 44, h: 47, xOffset: 0},
-      JUMPING: {x: 0, w: 44, h: 47, xOffset: 0},
-      CRASHED: {x: 220, w: 44, h: 47, xOffset: 0},
-      COLLISION_BOXES: [
-        new CollisionBox(22, 0, 17, 16),
-        new CollisionBox(1, 18, 30, 9),
-        new CollisionBox(10, 35, 14, 8),
-        new CollisionBox(1, 24, 29, 5),
-        new CollisionBox(5, 30, 21, 4),
-        new CollisionBox(9, 34, 15, 4),
-      ],
-    },
-    /** @type {Array<ObstacleType>} */
-    OBSTACLES: [
-      {
-        type: 'CACTUS_SMALL',
-        width: 17,
-        height: 35,
-        yPos: 105,
-        multipleSpeed: 4,
-        minGap: 120,
-        minSpeed: 0,
-        collisionBoxes: [
-          new CollisionBox(0, 7, 5, 27),
-          new CollisionBox(4, 0, 6, 34),
-          new CollisionBox(10, 4, 7, 14),
-        ],
-      },
-      {
-        type: 'CACTUS_LARGE',
-        width: 25,
-        height: 50,
-        yPos: 90,
-        multipleSpeed: 7,
-        minGap: 120,
-        minSpeed: 0,
-        collisionBoxes: [
-          new CollisionBox(0, 12, 7, 38),
-          new CollisionBox(8, 0, 7, 49),
-          new CollisionBox(13, 10, 10, 38),
-        ],
-      },
-      {
-        type: 'PTERODACTYL',
-        width: 46,
-        height: 40,
-        yPos: [100, 75, 50],    // Variable height.
-        yPosMobile: [100, 50],  // Variable height mobile.
-        multipleSpeed: 999,
-        minSpeed: 8.5,
-        minGap: 150,
-        collisionBoxes: [
-          new CollisionBox(15, 15, 16, 5),
-          new CollisionBox(18, 21, 24, 6),
-          new CollisionBox(2, 14, 4, 3),
-          new CollisionBox(6, 10, 4, 7),
-          new CollisionBox(10, 8, 6, 9),
-        ],
-        numFrames: 2,
-        frameRate: 1000 / 6,
-        speedOffset: .8,
-      },
-      {
-        type: 'COLLECTABLE',
-        width: 31,
-        height: 24,
-        yPos: 104,
-        multipleSpeed: 1000,
-        minGap: 9999,
-        minSpeed: 0,
-        collisionBoxes: [
-          new CollisionBox(0, 0, 32, 25),
-        ],
-      },
-    ],
-    BACKGROUND_EL: {
-      'CLOUD': {
-        height: 14,
-        maxCloudGap: 400,
-        maxSkyLevel: 30,
-        minCloudGap: 100,
-        minSkyLevel: 71,
-        offset: 4,
-        width: 46,
-        xPos: 1,
-        yPos: 120,
-      },
-    },
-    BACKGROUND_EL_CONFIG: {
-      maxBgEls: 1,
-      maxGap: 400,
-      minGap: 100,
-      pos: 0,
-      speed: 0.5,
-      yPos: 125,
-    },
-    LINES: [
-      {sourceX: 2, sourceY: 52, width: 600, height: 12, yPos: 127},
-    ],
-    ALT_GAME_OVER_TEXT_CONFIG: {
-      TEXT_X: 32,
-      TEXT_Y: 0,
-      TEXT_WIDTH: 246,
-      TEXT_HEIGHT: 17,
-      FLASH_DURATION: 1500,
-      FLASHING: false,
-    },
-  },
-};
diff --git a/components/neterror/resources/dino_game/offline.js b/components/neterror/resources/dino_game/offline.js
index 1f005fa..c79b432 100644
--- a/components/neterror/resources/dino_game/offline.js
+++ b/components/neterror/resources/dino_game/offline.js
@@ -13,7 +13,7 @@
 import {GeneratedSoundFx} from './generated_sound_fx.js';
 import {Horizon} from './horizon.js';
 import {Obstacle} from './obstacle.js';
-import {CollisionBox, GAME_TYPE, spriteDefinitionByType} from './offline-sprite-definitions.js';
+import {CollisionBox, GAME_TYPE, spriteDefinitionByType} from './offline_sprite_definitions.js';
 import {Trex} from './trex.js';
 import {getTimeStamp} from './utils.js';
 
@@ -351,10 +351,10 @@
    */
   loadImages() {
     let scale = '1x';
-    this.spriteDef = Runner.spriteDefinition.LDPI;
+    this.spriteDef = Runner.spriteDefinition.ldpi;
     if (IS_HIDPI) {
       scale = '2x';
-      this.spriteDef = Runner.spriteDefinition.HDPI;
+      this.spriteDef = Runner.spriteDefinition.hdpi;
     }
 
     Runner.imageSprite = /** @type {HTMLImageElement} */
@@ -500,10 +500,10 @@
 
     // Distance meter
     this.distanceMeter = new DistanceMeter(
-        this.canvas, this.spriteDef.TEXT_SPRITE, this.dimensions.width);
+        this.canvas, this.spriteDef.textSprite, this.dimensions.width);
 
     // Draw t-rex
-    this.tRex = new Trex(this.canvas, this.spriteDef.TREX);
+    this.tRex = new Trex(this.canvas, this.spriteDef.tRex);
 
     this.outerContainerEl.appendChild(this.containerEl);
     this.outerContainerEl.appendChild(this.slowSpeedCheckboxLabel);
@@ -675,13 +675,13 @@
     Runner.spriteDefinition = spriteDefinitionByType[Runner.gameType];
 
     if (IS_HIDPI) {
-      this.spriteDef = Runner.spriteDefinition.HDPI;
+      this.spriteDef = Runner.spriteDefinition.hdpi;
     } else {
-      this.spriteDef = Runner.spriteDefinition.LDPI;
+      this.spriteDef = Runner.spriteDefinition.ldpi;
     }
 
     this.altGameModeActive = true;
-    this.tRex.enableAltGameMode(this.spriteDef.TREX);
+    this.tRex.enableAltGameMode(this.spriteDef.tRex);
     this.horizon.enableAltGameMode(this.spriteDef);
     if (Runner.audioCues) {
       this.generatedSoundFx.background();
@@ -1276,18 +1276,18 @@
 
     // Game over panel.
     if (!this.gameOverPanel) {
-      const origSpriteDef = IS_HIDPI ? spriteDefinitionByType.original.HDPI :
-                                       spriteDefinitionByType.original.LDPI;
+      const origSpriteDef = IS_HIDPI ? spriteDefinitionByType.original.hdpi :
+                                       spriteDefinitionByType.original.ldpi;
 
       if (this.canvas) {
         if (Runner.isAltGameModeEnabled) {
           this.gameOverPanel = new GameOverPanel(
-              this.canvas, origSpriteDef.TEXT_SPRITE, origSpriteDef.RESTART,
-              this.dimensions, origSpriteDef.ALT_GAME_END,
+              this.canvas, origSpriteDef.textSprite, origSpriteDef.restart,
+              this.dimensions, origSpriteDef.altGameEnd,
               this.altGameModeActive);
         } else {
           this.gameOverPanel = new GameOverPanel(
-              this.canvas, origSpriteDef.TEXT_SPRITE, origSpriteDef.RESTART,
+              this.canvas, origSpriteDef.textSprite, origSpriteDef.restart,
               this.dimensions);
         }
       }
@@ -1630,7 +1630,7 @@
  * @return {Array<CollisionBox>|undefined}
  */
 function checkForCollision(obstacle, tRex, opt_canvasCtx) {
-  const obstacleBoxXPos = Runner.defaultDimensions.WIDTH + obstacle.xPos;
+  const obstacleBoxXPos = Runner.defaultDimensions.width + obstacle.xPos;
 
   // Adjustments are made to the bounding box as there is a 1 pixel white
   // border around the t-rex and obstacles.
@@ -1654,7 +1654,7 @@
     let tRexCollisionBoxes = [];
 
     if (Runner.isAltGameModeEnabled()) {
-      tRexCollisionBoxes = Runner.spriteDefinition.TREX.COLLISION_BOXES;
+      tRexCollisionBoxes = Runner.spriteDefinition.tRex.COLLISION_BOXES;
     } else {
       tRexCollisionBoxes = tRex.ducking ? Trex.collisionBoxes.DUCKING :
                                           Trex.collisionBoxes.RUNNING;
diff --git a/components/neterror/resources/dino_game/offline_sprite_definitions.ts b/components/neterror/resources/dino_game/offline_sprite_definitions.ts
new file mode 100644
index 0000000..2dc4b7a
--- /dev/null
+++ b/components/neterror/resources/dino_game/offline_sprite_definitions.ts
@@ -0,0 +1,247 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import type {BackgroundElConfig, BackgroundElSpriteConfig} from './background_el.js';
+import type {HorizonLineConfig} from './horizon_line.js';
+import type {SpritePosition} from './sprite_position.js';
+
+/*
+ * List of alternative game types defined in spriteDefinitionByType.
+ */
+export const GAME_TYPE: string[] = [];
+
+//******************************************************************************
+
+/**
+ * Collision box object.
+ */
+export class CollisionBox {
+  x: number;
+  y: number;
+  width: number;
+  height: number;
+
+  constructor(x: number, y: number, width: number, height: number) {
+    this.x = x;
+    this.y = y;
+    this.width = width;
+    this.height = height;
+  }
+}
+
+/**
+ * Obstacle definitions.
+ * minGap: minimum pixel space between obstacles.
+ * multipleSpeed: Speed at which multiples are allowed.
+ * speedOffset: speed faster / slower than the horizon.
+ * minSpeed: Minimum speed which the obstacle can make an appearance.
+ */
+export interface ObstacleType {
+  type: string;
+  width: number;
+  height: number;
+  yPos: number|number[];
+  // Only set whenever yPos is an array.
+  yPosMobile?: number[];
+  multipleSpeed: number;
+  minGap: number;
+  minSpeed: number;
+  speedOffset?: number;
+  collisionBoxes: CollisionBox[];
+  numFrames?: number;
+  // Only set whenever numFrames is set.
+  frameRate?: number;
+}
+
+interface SpritePositions {
+  backgroundEl: SpritePosition;
+  cactusLarge: SpritePosition;
+  cactusSmall: SpritePosition;
+  obstacle2: SpritePosition;
+  obstacle: SpritePosition;
+  cloud: SpritePosition;
+  horizon: SpritePosition;
+  moon: SpritePosition;
+  pterodactyl: SpritePosition;
+  restart: SpritePosition;
+  textSprite: SpritePosition;
+  tRex: SpritePosition;
+  star: SpritePosition;
+  collectable: SpritePosition;
+  altGameEnd: SpritePosition;
+}
+
+export interface SpriteDefinition {
+  ldpi: SpritePositions;
+  hdpi: SpritePositions;
+  maxGapCoefficient: number;
+  maxObstacleLength: number;
+  hasClouds: boolean;
+  bottomPad: number;
+  // TODO(crbug.com/373951324): Use type from trex.ts after it gets migrated.
+  tRex: any;
+  obstacles: ObstacleType[];
+  backgroundEl: {
+    [key: string]: BackgroundElSpriteConfig,
+  };
+  backgroundElConfig: BackgroundElConfig;
+  lines: HorizonLineConfig[];
+  altGameOverTextConfig: {[key: string]: number|boolean};
+}
+
+interface SpriteDefinitionByType {
+  original: SpriteDefinition;
+}
+
+/**
+ * T-Rex runner sprite definitions.
+ */
+export const spriteDefinitionByType: SpriteDefinitionByType = {
+  original: {
+    ldpi: {
+      backgroundEl: {x: 86, y: 2},
+      cactusLarge: {x: 332, y: 2},
+      cactusSmall: {x: 228, y: 2},
+      obstacle2: {x: 332, y: 2},
+      obstacle: {x: 228, y: 2},
+      cloud: {x: 86, y: 2},
+      horizon: {x: 2, y: 54},
+      moon: {x: 484, y: 2},
+      pterodactyl: {x: 134, y: 2},
+      restart: {x: 2, y: 68},
+      textSprite: {x: 655, y: 2},
+      tRex: {x: 848, y: 2},
+      star: {x: 645, y: 2},
+      collectable: {x: 0, y: 0},
+      altGameEnd: {x: 32, y: 0},
+    },
+    hdpi: {
+      backgroundEl: {x: 166, y: 2},
+      cactusLarge: {x: 652, y: 2},
+      cactusSmall: {x: 446, y: 2},
+      obstacle2: {x: 652, y: 2},
+      obstacle: {x: 446, y: 2},
+      cloud: {x: 166, y: 2},
+      horizon: {x: 2, y: 104},
+      moon: {x: 954, y: 2},
+      pterodactyl: {x: 260, y: 2},
+      restart: {x: 2, y: 130},
+      textSprite: {x: 1294, y: 2},
+      tRex: {x: 1678, y: 2},
+      star: {x: 1276, y: 2},
+      collectable: {x: 0, y: 0},
+      altGameEnd: {x: 64, y: 0},
+    },
+    maxGapCoefficient: 1.5,
+    maxObstacleLength: 3,
+    hasClouds: true,
+    bottomPad: 10,
+    tRex: {
+      WAITING_1: {x: 44, w: 44, h: 47, xOffset: 0},
+      WAITING_2: {x: 0, w: 44, h: 47, xOffset: 0},
+      RUNNING_1: {x: 88, w: 44, h: 47, xOffset: 0},
+      RUNNING_2: {x: 132, w: 44, h: 47, xOffset: 0},
+      JUMPING: {x: 0, w: 44, h: 47, xOffset: 0},
+      CRASHED: {x: 220, w: 44, h: 47, xOffset: 0},
+      COLLISION_BOXES: [
+        {x: 22, y: 0, width: 17, height: 16},
+        {x: 1, y: 18, width: 30, height: 9},
+        {x: 10, y: 35, width: 14, height: 8},
+        {x: 1, y: 24, width: 29, height: 5},
+        {x: 5, y: 30, width: 21, height: 4},
+        {x: 9, y: 34, width: 15, height: 4},
+      ],
+    },
+    obstacles: [
+      {
+        type: 'cactusSmall',
+        width: 17,
+        height: 35,
+        yPos: 105,
+        multipleSpeed: 4,
+        minGap: 120,
+        minSpeed: 0,
+        collisionBoxes: [
+          {x: 0, y: 7, width: 5, height: 27},
+          {x: 4, y: 0, width: 6, height: 34},
+          {x: 10, y: 4, width: 7, height: 14},
+        ],
+      },
+      {
+        type: 'cactusLarge',
+        width: 25,
+        height: 50,
+        yPos: 90,
+        multipleSpeed: 7,
+        minGap: 120,
+        minSpeed: 0,
+        collisionBoxes: [
+          {x: 0, y: 12, width: 7, height: 38},
+          {x: 8, y: 0, width: 7, height: 49},
+          {x: 13, y: 10, width: 10, height: 38},
+        ],
+      },
+      {
+        type: 'pterodactyl',
+        width: 46,
+        height: 40,
+        yPos: [100, 75, 50],    // Variable height.
+        yPosMobile: [100, 50],  // Variable height mobile.
+        multipleSpeed: 999,
+        minSpeed: 8.5,
+        minGap: 150,
+        collisionBoxes: [
+          {x: 15, y: 15, width: 16, height: 5},
+          {x: 18, y: 21, width: 24, height: 6},
+          {x: 2, y: 14, width: 4, height: 3},
+          {x: 6, y: 10, width: 4, height: 7},
+          {x: 10, y: 8, width: 6, height: 9},
+        ],
+        numFrames: 2,
+        frameRate: 1000 / 6,
+        speedOffset: .8,
+      },
+      {
+        type: 'collectable',
+        width: 31,
+        height: 24,
+        yPos: 104,
+        multipleSpeed: 1000,
+        minGap: 9999,
+        minSpeed: 0,
+        collisionBoxes: [
+          {x: 0, y: 0, width: 32, height: 25},
+        ],
+      },
+    ],
+    backgroundEl: {
+      'CLOUD': {
+        height: 14,
+        offset: 4,
+        width: 46,
+        xPos: 1,
+        fixed: false,
+      },
+    },
+    backgroundElConfig: {
+      maxBgEls: 1,
+      maxGap: 400,
+      minGap: 100,
+      pos: 0,
+      speed: 0.5,
+      yPos: 125,
+    },
+    lines: [
+      {sourceX: 2, sourceY: 52, width: 600, height: 12, yPos: 127},
+    ],
+    altGameOverTextConfig: {
+      TEXT_X: 32,
+      TEXT_Y: 0,
+      TEXT_WIDTH: 246,
+      TEXT_HEIGHT: 17,
+      FLASH_DURATION: 1500,
+      FLASHING: false,
+    },
+  },
+};
diff --git a/components/neterror/resources/dino_game/trex.js b/components/neterror/resources/dino_game/trex.js
index 9ddcf07..8491c99 100644
--- a/components/neterror/resources/dino_game/trex.js
+++ b/components/neterror/resources/dino_game/trex.js
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 import {FPS, IS_HIDPI} from './constants.js';
-import {CollisionBox} from './offline-sprite-definitions.js';
 import {Runner} from './offline.js';
+import {CollisionBox} from './offline_sprite_definitions.js';
 import {getTimeStamp} from './utils.js';
 
 
@@ -80,7 +80,7 @@
   enableAltGameMode(spritePos) {
     this.altGameModeEnabled = true;
     this.spritePos = spritePos;
-    const spriteDefinition = Runner.spriteDefinition['TREX'];
+    const spriteDefinition = Runner.spriteDefinition['tRex'];
 
     // Update animation frames.
     Trex.animFrames.RUNNING.frames =
@@ -112,7 +112,7 @@
 
     // Adjust bottom horizon placement.
     this.groundYPos = Runner.defaultDimensions.height - this.config.HEIGHT -
-        Runner.spriteDefinition['BOTTOM_PAD'];
+        Runner.spriteDefinition['bottomPad'];
     this.yPos = this.groundYPos;
     this.reset();
   }
@@ -218,7 +218,7 @@
         this.config.WIDTH_CRASHED :
         this.config.WIDTH;
 
-    let jumpOffset = Runner.spriteDefinition.TREX.JUMPING.xOffset;
+    let jumpOffset = Runner.spriteDefinition.tRex.JUMPING.xOffset;
 
     // Width of sprite can change on jump or crashed.
     if (this.altGameModeEnabled) {
diff --git a/components/omnibox/browser/autocomplete_grouper_sections.cc b/components/omnibox/browser/autocomplete_grouper_sections.cc
index 0d764c4..268b6be 100644
--- a/components/omnibox/browser/autocomplete_grouper_sections.cc
+++ b/components/omnibox/browser/autocomplete_grouper_sections.cc
@@ -461,6 +461,7 @@
 DesktopWebSearchZpsSection::DesktopWebSearchZpsSection(
     omnibox::GroupConfigMap& group_configs,
     size_t limit,
+    size_t contextual_action_limit,
     size_t contextual_search_limit)
     : Section(limit,
               {
@@ -469,6 +470,11 @@
                             {omnibox::GROUP_VISITED_DOC_RELATED, limit},
                             {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, limit},
                         }),
+                  Group(contextual_action_limit,
+                        {
+                            {omnibox::GROUP_CONTEXTUAL_SEARCH_ACTION,
+                             contextual_action_limit},
+                        }),
                   Group(contextual_search_limit,
                         {
                             {omnibox::GROUP_CONTEXTUAL_SEARCH,
@@ -484,7 +490,7 @@
                  {
                      Group(2,
                            {
-                               {omnibox::GROUP_ZERO_SUGGEST_IN_PRODUCT_HELP, 2},
+                               {omnibox::GROUP_CONTEXTUAL_SEARCH_ACTION, 2},
                            }),
                  },
                  group_configs) {}
diff --git a/components/omnibox/browser/autocomplete_grouper_sections.h b/components/omnibox/browser/autocomplete_grouper_sections.h
index f2e5f9f..b70354147 100644
--- a/components/omnibox/browser/autocomplete_grouper_sections.h
+++ b/components/omnibox/browser/autocomplete_grouper_sections.h
@@ -215,6 +215,7 @@
 // Section expressing the Desktop Search ZPS limits and grouping for the Web.
 // - up to `limit` suggestions total.
 //  - up to `limit` page related or personalized search suggestions.
+//  - up to `contextual_action_limit` contextual search action suggestions.
 //  - up to `contextual_search_limit` contextual search suggestions.
 // TODO(crbug.com/409810808): Extending `ZpsSection` would reorder the matches
 // demoting contextual search suggestions in `ZpsSection::InitFromMatches()`.
@@ -224,6 +225,7 @@
  public:
   explicit DesktopWebSearchZpsSection(omnibox::GroupConfigMap& group_configs,
                                       size_t limit,
+                                      size_t contextual_action_limit,
                                       size_t contextual_search_limit);
 };
 
diff --git a/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc b/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc
index 932d37d..cf9fe19 100644
--- a/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc
+++ b/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc
@@ -1801,8 +1801,9 @@
     sections.push_back(
         std::make_unique<DesktopWebURLZpsSection>(group_configs, 4u));
     // Max 4 search suggestions.
-    sections.push_back(
-        std::make_unique<DesktopWebSearchZpsSection>(group_configs, 4u, 4u));
+    sections.push_back(std::make_unique<DesktopWebSearchZpsSection>(
+        group_configs, /*limit=*/4u, /*contextual_action_limit=*/0u,
+        /*contextual_search_limit=*/4u));
     auto out_matches = Section::GroupMatches(std::move(sections), matches);
     VerifyMatches(out_matches, expected_relevances);
   };
@@ -1859,10 +1860,9 @@
     sections.push_back(
         std::make_unique<DesktopWebURLZpsSection>(group_configs, 3u));
     // Max 3 suggestions, with an upper limit of 3 search suggestions.
-    sections.push_back(
-        std::make_unique<DesktopWebSearchZpsSection>(group_configs, 3u, 3u));
-    sections.push_back(
-        std::make_unique<DesktopWebZpsActionsSection>(group_configs));
+    sections.push_back(std::make_unique<DesktopWebSearchZpsSection>(
+        group_configs, /*limit=*/4u, /*contextual_action_limit=*/1u,
+        /*contextual_search_limit=*/3u));
     auto out_matches = Section::GroupMatches(std::move(sections), matches);
     VerifyMatches(out_matches, expected_relevances);
   };
@@ -1870,8 +1870,8 @@
     SCOPED_TRACE("ZPS action matches group after contextual search matches");
     test(
         {
-            CreateMatch(300, omnibox::GROUP_ZERO_SUGGEST_IN_PRODUCT_HELP),
-            CreateMatch(299, omnibox::GROUP_ZERO_SUGGEST_IN_PRODUCT_HELP),
+            CreateMatch(300, omnibox::GROUP_CONTEXTUAL_SEARCH_ACTION),
+            CreateMatch(299, omnibox::GROUP_CONTEXTUAL_SEARCH_ACTION),
             CreateMatch(200, omnibox::GROUP_CONTEXTUAL_SEARCH),
             CreateMatch(199, omnibox::GROUP_CONTEXTUAL_SEARCH),
             CreateMatch(100, omnibox::GROUP_MOST_VISITED),
@@ -1887,8 +1887,8 @@
             CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
         },
-        // 3 URLs, 1 other search, 2 contextual searches, and 2 actions.
-        {100, 99, 98, 94, 200, 199, 300, 299});
+        // 3 URLs, 1 other search, 1 action, 2 contextual searches.
+        {100, 99, 98, 94, 300, 200, 199});
   }
 }
 
@@ -1899,7 +1899,8 @@
     sections.push_back(
         std::make_unique<DesktopWebURLZpsSection>(group_configs, 3u));
     sections.push_back(std::make_unique<DesktopWebSearchZpsSection>(
-        group_configs, /*limit=*/3u, /*contextual_search_limit=*/0u));
+        group_configs, /*limit=*/4u, /*contextual_action_limit=*/0u,
+        /*contextual_search_limit=*/0u));
     sections.push_back(
         std::make_unique<DesktopWebZpsActionsSection>(group_configs));
     auto out_matches = Section::GroupMatches(std::move(sections), matches);
@@ -1912,9 +1913,9 @@
             CreateMatch(99, omnibox::GROUP_MOST_VISITED),
             CreateMatch(98, omnibox::GROUP_VISITED_DOC_RELATED),
             CreateMatch(97, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(96, omnibox::GROUP_ZERO_SUGGEST_IN_PRODUCT_HELP),
+            CreateMatch(96, omnibox::GROUP_CONTEXTUAL_SEARCH_ACTION),
             CreateMatch(95, omnibox::GROUP_CONTEXTUAL_SEARCH),
-            CreateMatch(94, omnibox::GROUP_ZERO_SUGGEST_IN_PRODUCT_HELP),
+            CreateMatch(94, omnibox::GROUP_CONTEXTUAL_SEARCH_ACTION),
             CreateMatch(93, omnibox::GROUP_CONTEXTUAL_SEARCH),
         },
         // URLs, then searches, then actions, stable sorted.
diff --git a/components/omnibox/browser/autocomplete_match.h b/components/omnibox/browser/autocomplete_match.h
index b0925ae..b9a16e9 100644
--- a/components/omnibox/browser/autocomplete_match.h
+++ b/components/omnibox/browser/autocomplete_match.h
@@ -151,13 +151,8 @@
     ACMatchClassification(size_t offset, int style)
         : offset(offset), style(style) {}
 
-    bool operator==(const ACMatchClassification& other) const {
-      return offset == other.offset && style == other.style;
-    }
-
-    bool operator!=(const ACMatchClassification& other) const {
-      return offset != other.offset || style != other.style;
-    }
+    friend bool operator==(const ACMatchClassification&,
+                           const ACMatchClassification&) = default;
 
     // Offset within the string that this classification starts
     size_t offset;
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc
index 3d19a83a..a0e84f64 100644
--- a/components/omnibox/browser/autocomplete_result.cc
+++ b/components/omnibox/browser/autocomplete_result.cc
@@ -533,21 +533,25 @@
         const size_t contextual_zps_limit =
             omnibox_feature_configs::ContextualSearch::Get()
                 .contextual_zps_limit;
+        // Make space for the extra action when there is any contextual search
+        // budget. It needs to be included above any contextual search matches
+        // but does not count against their limit.
+        const size_t contextual_action_limit =
+            contextual_zps_limit > 0u ? 1u : 0u;
         sections.push_back(std::make_unique<DesktopWebURLZpsSection>(
             suggestion_groups_map_, max_url_suggestions));
         sections.push_back(std::make_unique<DesktopWebSearchZpsSection>(
-            suggestion_groups_map_, max_search_suggestions,
-            contextual_zps_limit));
+            suggestion_groups_map_,
+            max_search_suggestions + contextual_action_limit,
+            contextual_action_limit, contextual_zps_limit));
         if (omnibox_feature_configs::ContextualSearch::Get()
-                .starter_pack_page) {
-          if (omnibox_feature_configs::ContextualSearch::Get().actions_at_top) {
-            sections.insert(sections.begin(),
-                            std::make_unique<DesktopWebZpsActionsSection>(
-                                suggestion_groups_map_));
-          } else {
-            sections.push_back(std::make_unique<DesktopWebZpsActionsSection>(
-                suggestion_groups_map_));
-          }
+                .IsContextualSearchEnabled() &&
+            omnibox_feature_configs::ContextualSearch::Get().actions_at_top) {
+          // Since this one is above the search section, it will be given the
+          // contextual search actions so they appear at top.
+          sections.insert(sections.begin(),
+                          std::make_unique<DesktopWebZpsActionsSection>(
+                              suggestion_groups_map_));
         }
 #if BUILDFLAG(ENABLE_EXTENSIONS)
         if (base::FeatureList::IsEnabled(
diff --git a/components/omnibox/browser/contextual_search_provider.cc b/components/omnibox/browser/contextual_search_provider.cc
index 079c383..0340c02 100644
--- a/components/omnibox/browser/contextual_search_provider.cc
+++ b/components/omnibox/browser/contextual_search_provider.cc
@@ -314,8 +314,7 @@
   match.contents_class = {{0, ACMatchClassification::NONE}};
   match.transition = ui::PAGE_TRANSITION_GENERATED;
   match.suggest_type = omnibox::SuggestType::TYPE_NATIVE_CHROME;
-  match.suggestion_group_id =
-      omnibox::GroupId::GROUP_ZERO_SUGGEST_IN_PRODUCT_HELP;
+  match.suggestion_group_id = omnibox::GroupId::GROUP_CONTEXTUAL_SEARCH_ACTION;
 
   auto add_action = [&](auto action) {
     match.relevance--;
diff --git a/components/omnibox/browser/omnibox_popup_selection.cc b/components/omnibox/browser/omnibox_popup_selection.cc
index c96f848..c95c7f94 100644
--- a/components/omnibox/browser/omnibox_popup_selection.cc
+++ b/components/omnibox/browser/omnibox_popup_selection.cc
@@ -14,25 +14,7 @@
 
 constexpr bool kIsDesktop = !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS);
 
-const size_t OmniboxPopupSelection::kNoMatch = static_cast<size_t>(-1);
-
-bool OmniboxPopupSelection::operator==(const OmniboxPopupSelection& b) const {
-  return line == b.line && state == b.state && action_index == b.action_index;
-}
-
-bool OmniboxPopupSelection::operator!=(const OmniboxPopupSelection& b) const {
-  return !operator==(b);
-}
-
-bool OmniboxPopupSelection::operator<(const OmniboxPopupSelection& b) const {
-  if (line == b.line) {
-    if (state == b.state) {
-      return action_index < b.action_index;
-    }
-    return state < b.state;
-  }
-  return line < b.line;
-}
+constexpr size_t OmniboxPopupSelection::kNoMatch = static_cast<size_t>(-1);
 
 bool OmniboxPopupSelection::IsChangeToKeyword(
     OmniboxPopupSelection from) const {
diff --git a/components/omnibox/browser/omnibox_popup_selection.h b/components/omnibox/browser/omnibox_popup_selection.h
index 4fc332b..b809b3d7 100644
--- a/components/omnibox/browser/omnibox_popup_selection.h
+++ b/components/omnibox/browser/omnibox_popup_selection.h
@@ -99,9 +99,10 @@
                                  size_t action_index = 0)
       : line(line), state(state), action_index(action_index) {}
 
-  bool operator==(const OmniboxPopupSelection&) const;
-  bool operator!=(const OmniboxPopupSelection&) const;
-  bool operator<(const OmniboxPopupSelection&) const;
+  friend bool operator==(const OmniboxPopupSelection&,
+                         const OmniboxPopupSelection&) = default;
+  friend auto operator<=>(const OmniboxPopupSelection&,
+                          const OmniboxPopupSelection&) = default;
 
   // Returns true if going to this selection from given `from` selection
   // results in activation of keyword state when it wasn't active before.
diff --git a/components/omnibox/browser/on_device_tail_model_executor.h b/components/omnibox/browser/on_device_tail_model_executor.h
index 78562ab0..953e35e3 100644
--- a/components/omnibox/browser/on_device_tail_model_executor.h
+++ b/components/omnibox/browser/on_device_tail_model_executor.h
@@ -90,12 +90,8 @@
     RnnCellStates& operator=(RnnCellStates&& other) noexcept;
     ~RnnCellStates();
 
-    bool operator==(const RnnCellStates& other) const {
-      return c_i == other.c_i && m_i == other.m_i;
-    }
-    bool operator!=(const RnnCellStates& other) const {
-      return !(*this == other);
-    }
+    friend bool operator==(const RnnCellStates&,
+                           const RnnCellStates&) = default;
 
     // Cell states, see definitions at
     // https://github.com/tensorflow/lingvo/blob/master/lingvo/core/rnn_cell.py#L221.
diff --git a/components/omnibox/common/omnibox_feature_configs.cc b/components/omnibox/common/omnibox_feature_configs.cc
index ec9649b..2f212475 100644
--- a/components/omnibox/common/omnibox_feature_configs.cc
+++ b/components/omnibox/common/omnibox_feature_configs.cc
@@ -127,6 +127,10 @@
     default;
 ContextualSearch::~ContextualSearch() = default;
 
+bool ContextualSearch::IsContextualSearchEnabled() const {
+  return contextual_zps_limit > 0;
+}
+
 DocumentProvider::DocumentProvider() {
   enabled = base::FeatureList::IsEnabled(omnibox::kDocumentProvider);
   min_query_length =
diff --git a/components/omnibox/common/omnibox_feature_configs.h b/components/omnibox/common/omnibox_feature_configs.h
index a1529e4..fb3b3777 100644
--- a/components/omnibox/common/omnibox_feature_configs.h
+++ b/components/omnibox/common/omnibox_feature_configs.h
@@ -136,6 +136,9 @@
   DECLARE_FEATURE(kContextualSearchBoxUsesContextualSearchProvider);
   DECLARE_FEATURE(kContextualSearchUseVerticalBar);
 
+  // Whether to use contextual search features, for example the lens action.
+  bool IsContextualSearchEnabled() const;
+
   // Whether the starter pack page scope is enabled.
   bool starter_pack_page;
 
diff --git a/components/optimization_guide/core/signature_model_executor.h b/components/optimization_guide/core/signature_model_executor.h
index 5bb4d55..2dd5f290 100644
--- a/components/optimization_guide/core/signature_model_executor.h
+++ b/components/optimization_guide/core/signature_model_executor.h
@@ -56,6 +56,10 @@
         static_cast<GenericModelExecutionTask<OutputType, InputType>*>(
             execution_task)
             ->GetSignatureRunner(GetSignature());
+    if (!signature_runner) {
+      *out_status = ExecutionStatus::kErrorModelFileNotValid;
+      return std::nullopt;
+    }
     Preprocess(GetTensorsMappedByInputNames(signature_runner), input);
     TfLiteStatus status = signature_runner->Invoke();
     if (status == kTfLiteCancelled) {
diff --git a/components/page_info/core/about_this_site_service_unittest.cc b/components/page_info/core/about_this_site_service_unittest.cc
index 256ffe3..ec30d1c1 100644
--- a/components/page_info/core/about_this_site_service_unittest.cc
+++ b/components/page_info/core/about_this_site_service_unittest.cc
@@ -106,15 +106,10 @@
   return OptimizationGuideDecision::kUnknown;
 }
 
-class AboutThisSiteServiceTest : public ::testing::TestWithParam<bool> {
+class AboutThisSiteServiceTest : public ::testing::Test {
  public:
   void SetUp() override {
-    // Parameterize test until kAboutThisSiteAsyncFetching is enabled by
-    // default.
-    if (GetParam()) {
-      tab_helper_mock_ = std::make_unique<testing::StrictMock<MockTabHelper>>();
-    }
-
+    tab_helper_mock_ = std::make_unique<testing::StrictMock<MockTabHelper>>();
     service_ = std::make_unique<testing::StrictMock<MockAboutThisSiteService>>(
         search_engines_test_environment_.template_url_service());
     SetOptimizationGuideAllowed(true);
@@ -138,16 +133,11 @@
 };
 
 // Tests that correct proto messages are accepted.
-TEST_P(AboutThisSiteServiceTest, ValidResponse) {
+TEST_F(AboutThisSiteServiceTest, ValidResponse) {
   base::HistogramTester t;
-  if (GetParam()) {
     EXPECT_CALL(*tab_helper(), GetAboutThisSiteMetadata())
         .WillOnce(Return(DecisionWithMetadata{OptimizationGuideDecision::kTrue,
                                               CreateValidMetadata()}));
-  } else {
-    EXPECT_CALL(*service(), CanApplyOptimization(_, _))
-        .WillOnce(Invoke(&ReturnDescription));
-  }
 
   auto info = service()->GetAboutThisSiteInfo(
       GURL("https://foo.com"), ukm::UkmRecorder::GetNewSourceID(),
@@ -162,7 +152,7 @@
 }
 
 // Tests the language specific feature check.
-TEST_P(AboutThisSiteServiceTest, FeatureCheck) {
+TEST_F(AboutThisSiteServiceTest, FeatureCheck) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(kPageInfoAboutThisSite);
 
@@ -180,16 +170,11 @@
 }
 
 // Tests that incorrect proto messages are discarded.
-TEST_P(AboutThisSiteServiceTest, InvalidResponse) {
+TEST_F(AboutThisSiteServiceTest, InvalidResponse) {
   base::HistogramTester t;
-  if (GetParam()) {
     EXPECT_CALL(*tab_helper(), GetAboutThisSiteMetadata())
         .WillOnce(Return(DecisionWithMetadata{OptimizationGuideDecision::kTrue,
                                               CreateInvalidDescription()}));
-  } else {
-    EXPECT_CALL(*service(), CanApplyOptimization(_, _))
-        .WillOnce(Invoke(&ReturnInvalidDescription));
-  }
 
   auto info = service()->GetAboutThisSiteInfo(
       GURL("https://foo.com"), ukm::UkmRecorder::GetNewSourceID(),
@@ -202,17 +187,12 @@
 }
 
 // Tests that no response is handled.
-TEST_P(AboutThisSiteServiceTest, NoResponse) {
+TEST_F(AboutThisSiteServiceTest, NoResponse) {
   base::HistogramTester t;
   std::optional<proto::AboutThisSiteMetadata> expected;
-  if (GetParam()) {
     EXPECT_CALL(*tab_helper(), GetAboutThisSiteMetadata())
         .WillOnce(Return(DecisionWithMetadata{OptimizationGuideDecision::kFalse,
                                               std::nullopt}));
-  } else {
-    EXPECT_CALL(*service(), CanApplyOptimization(_, _))
-        .WillOnce(Invoke(&ReturnNoResult));
-  }
 
   auto info = service()->GetAboutThisSiteInfo(
       GURL("https://foo.com"), ukm::UkmRecorder::GetNewSourceID(),
@@ -225,16 +205,11 @@
 }
 
 // Tests that unknown response is handled.
-TEST_P(AboutThisSiteServiceTest, Unknown) {
+TEST_F(AboutThisSiteServiceTest, Unknown) {
   base::HistogramTester t;
-  if (GetParam()) {
     EXPECT_CALL(*tab_helper(), GetAboutThisSiteMetadata())
         .WillOnce(Return(DecisionWithMetadata{
             OptimizationGuideDecision::kUnknown, std::nullopt}));
-  } else {
-    EXPECT_CALL(*service(), CanApplyOptimization(_, _))
-        .WillOnce(Invoke(&ReturnUnknown));
-  }
 
   auto info = service()->GetAboutThisSiteInfo(
       GURL("https://foo.com"), ukm::UkmRecorder::GetNewSourceID(),
@@ -247,7 +222,7 @@
 }
 
 // Tests that ATP not shown when Google is not set as DSE
-TEST_P(AboutThisSiteServiceTest, NotShownWhenNoGoogleDSE) {
+TEST_F(AboutThisSiteServiceTest, NotShownWhenNoGoogleDSE) {
   base::HistogramTester t;
 
   // Changing default provider to other than Google
@@ -274,7 +249,7 @@
 }
 
 // Tests that IP addresses and localhost are handled.
-TEST_P(AboutThisSiteServiceTest, LocalHosts) {
+TEST_F(AboutThisSiteServiceTest, LocalHosts) {
   base::HistogramTester t;
 
   auto info = service()->GetAboutThisSiteInfo(
@@ -296,7 +271,7 @@
 }
 
 // Tests the local creation of the Diner URL for navigation.
-TEST_P(AboutThisSiteServiceTest, CreateMoreAboutUrlForNavigation) {
+TEST_F(AboutThisSiteServiceTest, CreateMoreAboutUrlForNavigation) {
   auto url =
       service()->CreateMoreAboutUrlForNavigation(GURL("https://foo.com"));
   EXPECT_EQ(url,
@@ -306,7 +281,7 @@
 }
 
 // Tests the local creation of the Diner URL for navigation with anchor.
-TEST_P(AboutThisSiteServiceTest, CreateMoreAboutUrlForNavigationWithAnchor) {
+TEST_F(AboutThisSiteServiceTest, CreateMoreAboutUrlForNavigationWithAnchor) {
   auto url = service()->CreateMoreAboutUrlForNavigation(
       GURL("https://foo.com#anchor"));
   EXPECT_EQ(url,
@@ -317,7 +292,7 @@
 
 // Tests the local creation of the Diner URL for navigation from an origin with
 // path.
-TEST_P(AboutThisSiteServiceTest, CreateMoreAboutUrlForNavigationWithPath) {
+TEST_F(AboutThisSiteServiceTest, CreateMoreAboutUrlForNavigationWithPath) {
   auto url = service()->CreateMoreAboutUrlForNavigation(
       GURL("https://foo.com/index.html"));
   EXPECT_EQ(url,
@@ -328,7 +303,7 @@
 
 // Tests the local creation of the Diner URL for navigation from an invalid
 // origin.
-TEST_P(AboutThisSiteServiceTest, CreateMoreAboutUrlForNavigationInvalid) {
+TEST_F(AboutThisSiteServiceTest, CreateMoreAboutUrlForNavigationInvalid) {
   auto url = service()->CreateMoreAboutUrlForNavigation(
       GURL("https://127.0.0.1/index.html"));
   EXPECT_EQ(url,
@@ -339,7 +314,7 @@
 
 // Tests the local creation of the Diner URL for navigation from an invalid
 // origin (blank).
-TEST_P(AboutThisSiteServiceTest, CreateMoreAboutUrlForNavigationInvalidBlank) {
+TEST_F(AboutThisSiteServiceTest, CreateMoreAboutUrlForNavigationInvalidBlank) {
   auto url = service()->CreateMoreAboutUrlForNavigation(GURL("about:blank"));
   EXPECT_EQ(url,
             "https://www.google.com/search?"
@@ -349,7 +324,7 @@
 
 // Tests the local creation of the Diner URL for navigation from an invalid
 // origin (file).
-TEST_P(AboutThisSiteServiceTest, CreateMoreAboutUrlForNavigationInvalidFile) {
+TEST_F(AboutThisSiteServiceTest, CreateMoreAboutUrlForNavigationInvalidFile) {
   auto url = service()->CreateMoreAboutUrlForNavigation(GURL("file:///a/b/c"));
   EXPECT_EQ(url,
             "https://www.google.com/search?"
@@ -357,9 +332,4 @@
             "&tbm=ilp&ctx=chrome_nav");
 }
 
-// Test with TabHelper based fetching enabled and disabled.
-INSTANTIATE_TEST_SUITE_P(/* no label */,
-                         AboutThisSiteServiceTest,
-                         testing::Bool());
-
 }  // namespace page_info
diff --git a/components/password_manager/core/browser/password_form.h b/components/password_manager/core/browser/password_form.h
index 7b5fba9..f9635cc 100644
--- a/components/password_manager/core/browser/password_form.h
+++ b/components/password_manager/core/browser/password_form.h
@@ -575,8 +575,6 @@
   // An exact equality comparison of all the fields is only useful for tests.
   // Production code should be using `ArePasswordFormUniqueKeysEqual` instead.
   friend bool operator==(const PasswordForm&, const PasswordForm&) = default;
-  friend bool operator!=(const PasswordForm& lhs,
-                         const PasswordForm& rhs) = default;
 #endif
 };
 
diff --git a/components/payments/content/android/payment_feature_map.cc b/components/payments/content/android/payment_feature_map.cc
index 35278383..a3e239f 100644
--- a/components/payments/content/android/payment_feature_map.cc
+++ b/components/payments/content/android/payment_feature_map.cc
@@ -70,6 +70,6 @@
              base::FEATURE_DISABLED_BY_DEFAULT);
 BASE_FEATURE(kUpdatePaymentDetailsIntentFilterInPaymentApp,
              "UpdatePaymentDetailsIntentFilterInPaymentApp",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 }  // namespace payments::android
diff --git a/components/performance_manager/persistence/site_data/leveldb_site_data_store.cc b/components/performance_manager/persistence/site_data/leveldb_site_data_store.cc
index 5ec5121..0a7c899 100644
--- a/components/performance_manager/persistence/site_data/leveldb_site_data_store.cc
+++ b/components/performance_manager/persistence/site_data/leveldb_site_data_store.cc
@@ -15,7 +15,6 @@
 #include "base/hash/md5.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_functions.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task/thread_pool.h"
@@ -30,72 +29,30 @@
 
 namespace {
 
+#if !BUILDFLAG(IS_ANDROID)
+// Disable some testing features in Android to reduce APK size.
 std::atomic<bool> g_use_in_memory_db_for_testing = false;
-
-// The name of the following histograms is the same as the one used in the
-// //c/b/resource_coordinator version of this file. It's fine to keep the same
-// name as these 2 codepath will never be enabled at the same time. These
-// histograms should be removed once it has been confirmed that the data is
-// similar to the one from the other implementation.
-//
-// TODO(crbug.com/40902006): Remove these histograms when SiteDB is confirmed to
-// be working for BackgroundTabLoadingPolicy.
-const char kInitStatusHistogramLabel[] =
-    "PerformanceManager.SiteDB.DatabaseInit";
-const char kInitStatusAfterRepairHistogramLabel[] =
-    "PerformanceManager.SiteDB.DatabaseInitAfterRepair";
-const char kInitStatusAfterDeleteHistogramLabel[] =
-    "PerformanceManager.SiteDB.DatabaseInitAfterDelete";
-
-enum class InitStatus {
-  kInitStatusOk,
-  kInitStatusCorruption,
-  kInitStatusIOError,
-  kInitStatusUnknownError,
-  kInitStatusMax
-};
-
-// Report the database's initialization status metrics.
-void ReportInitStatus(const char* histogram_name,
-                      const leveldb::Status& status) {
-  if (status.ok()) {
-    base::UmaHistogramEnumeration(histogram_name, InitStatus::kInitStatusOk,
-                                  InitStatus::kInitStatusMax);
-  } else if (status.IsCorruption()) {
-    base::UmaHistogramEnumeration(histogram_name,
-                                  InitStatus::kInitStatusCorruption,
-                                  InitStatus::kInitStatusMax);
-  } else if (status.IsIOError()) {
-    base::UmaHistogramEnumeration(histogram_name,
-                                  InitStatus::kInitStatusIOError,
-                                  InitStatus::kInitStatusMax);
-  } else {
-    base::UmaHistogramEnumeration(histogram_name,
-                                  InitStatus::kInitStatusUnknownError,
-                                  InitStatus::kInitStatusMax);
-  }
-}
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Attempt to repair the database stored in |db_path|.
 bool RepairDatabase(const std::string& db_path) {
   leveldb_env::Options options;
   options.reuse_logs = false;
   options.max_open_files = 0;
-  bool repair_succeeded = leveldb::RepairDB(db_path, options).ok();
-  base::UmaHistogramBoolean("PerformanceManager.SiteDB.DatabaseRepair",
-                            repair_succeeded);
-  return repair_succeeded;
+  return leveldb::RepairDB(db_path, options).ok();
 }
 
 bool ShouldAttemptDbRepair(const leveldb::Status& status) {
   // A corrupt database might be repaired (some data might be loss but it's
   // better than losing everything).
-  if (status.IsCorruption())
+  if (status.IsCorruption()) {
     return true;
+  }
   // An I/O error might be caused by a missing manifest, it's sometime possible
   // to repair this (some data might be loss).
-  if (status.IsIOError())
+  if (status.IsIOError()) {
     return true;
+  }
 
   return false;
 }
@@ -177,8 +134,9 @@
   void SetInitializationCallbackForTesting(base::OnceClosure callback) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     init_callback_for_testing_ = std::move(callback);
-    if (DBIsInitialized())
+    if (DBIsInitialized()) {
       std::move(init_callback_for_testing_).Run();
+    }
   }
 
  private:
@@ -195,10 +153,12 @@
   // Implementation for the ClearDatabase function.
   void ClearDatabaseImpl();
 
+#if !BUILDFLAG(IS_ANDROID)
   // A levelDB environment that gets used for testing. This allows using an
   // in-memory database when needed.
   std::unique_ptr<leveldb::Env> env_for_testing_
       GUARDED_BY_CONTEXT(sequence_checker_);
+#endif  // !BUILDFLAG(IS_ANDROID)
 
   // The on disk location of the database.
   const base::FilePath db_path_ GUARDED_BY_CONTEXT(sequence_checker_);
@@ -219,11 +179,13 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   OpeningType opening_type = OpenOrCreateDatabaseImpl();
 
-  if (init_callback_for_testing_)
+  if (init_callback_for_testing_) {
     std::move(init_callback_for_testing_).Run();
+  }
 
-  if (!db_)
+  if (!db_) {
     return;
+  }
   std::string db_metadata;
   leveldb::Status s = db_->Get(
       read_options_, LevelDBSiteDataStore::kDbMetadataKey, &db_metadata);
@@ -233,8 +195,9 @@
     // for now.
     size_t version = std::numeric_limits<size_t>::max();
     CHECK(base::StringToSizeT(db_metadata, &version));
-    if (version == LevelDBSiteDataStore::kDbVersion)
+    if (version == LevelDBSiteDataStore::kDbVersion) {
       is_expected_version = true;
+    }
   }
   // TODO(sebmarchand): Add a migration engine rather than flushing the database
   // for every version change, https://crbug.com/866540.
@@ -242,8 +205,9 @@
     DLOG(ERROR) << "Invalid DB version, recreating it.";
     ClearDatabaseImpl();
     // The database might fail to open.
-    if (!db_)
+    if (!db_) {
       return;
+    }
     opening_type = OpeningType::kNewDb;
   }
   if (opening_type == OpeningType::kNewDb) {
@@ -263,8 +227,9 @@
     const url::Origin& origin) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (!db_)
+  if (!db_) {
     return std::nullopt;
+  }
 
   leveldb::Status s;
   std::string protobuf_value;
@@ -291,8 +256,9 @@
     const SiteDataProto& site_characteristic_proto) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (!db_)
+  if (!db_) {
     return;
+  }
 
   leveldb::Status s;
   {
@@ -307,16 +273,15 @@
         << "Error while inserting an element in the site characteristics "
         << "database: " << s.ToString();
   }
-  base::UmaHistogramBoolean(
-      "PerformanceManager.SiteDB.WriteCompleted.WriteSiteDataIntoStore", true);
 }
 
 void LevelDBSiteDataStore::AsyncHelper::RemoveSiteDataFromDB(
     const std::vector<url::Origin>& site_origins) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (!db_)
+  if (!db_) {
     return;
+  }
 
   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                 base::BlockingType::MAY_BLOCK);
@@ -325,11 +290,9 @@
     batch.Delete(SerializeOriginIntoDatabaseKey(iter));
   leveldb::Status status = db_->Write(write_options_, &batch);
   if (!status.ok()) {
-    LOG(WARNING) << "Failed to remove some entries from the site "
-                 << "characteristics database: " << status.ToString();
+    DLOG(WARNING) << "Failed to remove some entries from the site "
+                  << "characteristics database: " << status.ToString();
   }
-  base::UmaHistogramBoolean(
-      "PerformanceManager.SiteDB.WriteCompleted.ClearSiteDataForOrigins", true);
 }
 
 void LevelDBSiteDataStore::AsyncHelper::ClearDatabase() {
@@ -339,14 +302,13 @@
   }
 
   ClearDatabaseImpl();
-  base::UmaHistogramBoolean(
-      "PerformanceManager.SiteDB.WriteCompleted.ClearAllSiteData", true);
 }
 
 DatabaseSizeResult LevelDBSiteDataStore::AsyncHelper::GetDatabaseSize() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!db_)
+  if (!db_) {
     return DatabaseSizeResult();
+  }
 
   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                 base::BlockingType::MAY_BLOCK);
@@ -362,8 +324,9 @@
   ret.on_disk_size_kb = base::ComputeDirectorySize(db_path_) / 1024;
 #if BUILDFLAG(IS_WIN)
   OpenOrCreateDatabase();
-  if (!db_)
+  if (!db_) {
     return DatabaseSizeResult();
+  }
 #endif
 
   // Default read options will fill the cache as we go.
@@ -394,35 +357,36 @@
   leveldb_env::Options options;
   options.create_if_missing = true;
 
+#if !BUILDFLAG(IS_ANDROID)
   if (g_use_in_memory_db_for_testing.load(std::memory_order_relaxed)) {
     env_for_testing_ = leveldb_chrome::NewMemEnv("LevelDBSiteDataStore");
     options.env = env_for_testing_.get();
   }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
-  leveldb::Status status =
-      leveldb_env::OpenDB(options, db_path_.AsUTF8Unsafe(), &db_);
+  const std::string db_path_str = db_path_.AsUTF8Unsafe();
+  const leveldb::Status status =
+      leveldb_env::OpenDB(options, db_path_str, &db_);
 
-  ReportInitStatus(kInitStatusHistogramLabel, status);
-
-  if (status.ok())
+  if (status.ok()) {
     return opening_type;
+  }
 
-  if (!ShouldAttemptDbRepair(status))
+  if (!ShouldAttemptDbRepair(status)) {
     return opening_type;
+  }
 
-  if (RepairDatabase(db_path_.AsUTF8Unsafe())) {
-    status = leveldb_env::OpenDB(options, db_path_.AsUTF8Unsafe(), &db_);
-    ReportInitStatus(kInitStatusAfterRepairHistogramLabel, status);
-    if (status.ok())
+  if (RepairDatabase(db_path_str)) {
+    if (leveldb_env::OpenDB(options, db_path_str, &db_).ok()) {
       return opening_type;
+    }
   }
 
   // Delete the database and try to open it one last time.
   if (leveldb_chrome::DeleteDB(db_path_, options).ok()) {
-    status = leveldb_env::OpenDB(options, db_path_.AsUTF8Unsafe(), &db_);
-    ReportInitStatus(kInitStatusAfterDeleteHistogramLabel, status);
-    if (!status.ok())
+    if (!leveldb_env::OpenDB(options, db_path_str, &db_).ok()) {
       db_.reset();
+    }
   }
 
   return opening_type;
@@ -440,8 +404,8 @@
   if (status.ok()) {
     OpenOrCreateDatabaseImpl();
   } else {
-    LOG(WARNING) << "Failed to destroy the site characteristics database: "
-                 << status.ToString();
+    DLOG(WARNING) << "Failed to destroy the site characteristics database: "
+                  << status.ToString();
   }
 }
 
@@ -524,12 +488,16 @@
 
 void LevelDBSiteDataStore::SetInitializationCallbackForTesting(
     base::OnceClosure callback) {
+#if !BUILDFLAG(IS_ANDROID)
+  // This testing function cannot be optimized out by linker for unknown reason.
+  // Manually exclude it on Android to reduce APK size.
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   blocking_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&LevelDBSiteDataStore::AsyncHelper::
                                     SetInitializationCallbackForTesting,
                                 base::Unretained(async_helper_.get()),
                                 std::move(callback)));
+#endif  // !BUILDFLAG(IS_ANDROID)
 }
 
 void LevelDBSiteDataStore::DatabaseIsInitializedForTesting(
@@ -559,10 +527,14 @@
 
 // static
 base::ScopedClosureRunner LevelDBSiteDataStore::UseInMemoryDBForTesting() {
+#if !BUILDFLAG(IS_ANDROID)
   g_use_in_memory_db_for_testing.store(true, std::memory_order_relaxed);
   return base::ScopedClosureRunner(base::BindOnce([] {
     g_use_in_memory_db_for_testing.store(false, std::memory_order_relaxed);
   }));
+#else
+  return base::ScopedClosureRunner();
+#endif  // !BUILDFLAG(IS_ANDROID)
 }
 
 }  // namespace performance_manager
diff --git a/components/performance_manager/persistence/site_data/leveldb_site_data_store_unittest.cc b/components/performance_manager/persistence/site_data/leveldb_site_data_store_unittest.cc
index 725b791..424dca1 100644
--- a/components/performance_manager/persistence/site_data/leveldb_site_data_store_unittest.cc
+++ b/components/performance_manager/persistence/site_data/leveldb_site_data_store_unittest.cc
@@ -14,7 +14,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
-#include "base/test/metrics/histogram_tester.h"
 #include "base/test/test_file_util.h"
 #include "build/build_config.h"
 #include "components/performance_manager/persistence/site_data/site_data.pb.h"
@@ -238,18 +237,9 @@
 
   EXPECT_TRUE(leveldb_chrome::CorruptClosedDBForTesting(GetDBPath()));
 
-  base::HistogramTester histogram_tester;
-  histogram_tester.ExpectTotalCount("PerformanceManager.SiteDB.DatabaseInit",
-                                    0);
-  // Open the corrupt DB and ensure that the appropriate histograms gets
-  // updated.
+  // Open the corrupt DB.
   OpenDB();
   EXPECT_TRUE(DbIsInitialized());
-  histogram_tester.ExpectUniqueSample("PerformanceManager.SiteDB.DatabaseInit",
-                                      1 /* kInitStatusCorruption */, 1);
-  histogram_tester.ExpectUniqueSample(
-      "PerformanceManager.SiteDB.DatabaseInitAfterRepair",
-      0 /* kInitStatusOk */, 1);
 
   // TODO(sebmarchand): try to induce an I/O error by deleting one of the
   // manifest files.
diff --git a/components/performance_manager/persistence/site_data/site_data_impl.cc b/components/performance_manager/persistence/site_data/site_data_impl.cc
index 43a3f3e..6ef61cf 100644
--- a/components/performance_manager/persistence/site_data/site_data_impl.cc
+++ b/components/performance_manager/persistence/site_data/site_data_impl.cc
@@ -8,7 +8,6 @@
 
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
-#include "base/metrics/histogram_functions.h"
 #include "base/strings/strcat.h"
 
 namespace performance_manager {
@@ -126,21 +125,18 @@
 void SiteDataImpl::NotifyUpdatesFaviconInBackground() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   NotifyFeatureUsage(
-      site_characteristics_.mutable_updates_favicon_in_background(),
-      "FaviconUpdateInBackground");
+      site_characteristics_.mutable_updates_favicon_in_background());
 }
 
 void SiteDataImpl::NotifyUpdatesTitleInBackground() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   NotifyFeatureUsage(
-      site_characteristics_.mutable_updates_title_in_background(),
-      "TitleUpdateInBackground");
+      site_characteristics_.mutable_updates_title_in_background());
 }
 
 void SiteDataImpl::NotifyUsesAudioInBackground() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  NotifyFeatureUsage(site_characteristics_.mutable_uses_audio_in_background(),
-                     "AudioUsageInBackground");
+  NotifyFeatureUsage(site_characteristics_.mutable_uses_audio_in_background());
 }
 
 void SiteDataImpl::NotifyLoadTimePerformanceMeasurement(
@@ -210,9 +206,6 @@
       // SiteDataImpl is only created from SiteDataCacheImpl, not from the
       // NonRecordingSiteDataCache that's used for OTR profiles, so this should
       // always be logged.
-      base::UmaHistogramBoolean(
-          "PerformanceManager.SiteDB.WriteScheduled.WriteSiteDataIntoStore",
-          true);
       data_store_->WriteSiteDataIntoStore(origin_, FlushStateToProto());
     }
   }
@@ -286,10 +279,6 @@
     const SiteDataFeatureProto& feature_proto) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  base::UmaHistogramBoolean(
-      "PerformanceManager.SiteDB.ReadHasCompletedBeforeQuery",
-      fully_initialized_);
-
   // Checks if this feature has already been observed.
   // TODO(sebmarchand): Check the timestamp and reset features that haven't been
   // observed in a long time, https://crbug.com/826446.
@@ -302,24 +291,11 @@
   return SiteFeatureUsage::kSiteFeatureUsageUnknown;
 }
 
-void SiteDataImpl::NotifyFeatureUsage(SiteDataFeatureProto* feature_proto,
-                                      const char* feature_name) {
+void SiteDataImpl::NotifyFeatureUsage(SiteDataFeatureProto* feature_proto) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(IsLoaded());
   DCHECK_GT(loaded_tabs_in_background_count_, 0U);
 
-  // Report the observation time if this is the first time this feature is
-  // observed.
-  if (feature_proto->observation_duration() != 0) {
-    base::UmaHistogramCustomTimes(
-        base::StrCat(
-            {"PerformanceManager.SiteDB.ObservationTimeBeforeFirstUse.",
-             feature_name}),
-        InternalRepresentationToTimeDelta(
-            feature_proto->observation_duration()),
-        base::Seconds(1), base::Days(1), 100);
-  }
-
   feature_proto->Clear();
   feature_proto->set_use_timestamp(
       TimeDeltaToInternalRepresentation(GetTickDeltaSinceEpoch()));
diff --git a/components/performance_manager/persistence/site_data/site_data_impl.h b/components/performance_manager/persistence/site_data/site_data_impl.h
index 8d88f28..de40c9bb 100644
--- a/components/performance_manager/persistence/site_data/site_data_impl.h
+++ b/components/performance_manager/persistence/site_data/site_data_impl.h
@@ -225,8 +225,7 @@
 
   // Helper function to update a given |SiteDataFeatureProto| when a
   // feature gets used.
-  void NotifyFeatureUsage(SiteDataFeatureProto* feature_proto,
-                          const char* feature_name);
+  void NotifyFeatureUsage(SiteDataFeatureProto* feature_proto);
 
   bool IsLoaded() const {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/components/permissions/android/BUILD.gn b/components/permissions/android/BUILD.gn
index 4ad3db9..65b8e1a 100644
--- a/components/permissions/android/BUILD.gn
+++ b/components/permissions/android/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/config/android/rules.gni")
-import("//build/config/locales.gni")
 import("//third_party/jni_zero/jni_zero.gni")
 import("//tools/grit/grit_rule.gni")
 
@@ -120,11 +119,6 @@
 
 java_strings_grd("permissions_strings_grd") {
   grd_file = "permissions_android_strings.grd"
-  outputs =
-      [ "values/permissions_android_strings.xml" ] +
-      process_file_template(
-          android_bundle_locales_as_resources,
-          [ "values-{{source_name_part}}/permissions_android_strings.xml" ])
 }
 
 android_library("content_settings_enums_java") {
diff --git a/components/plus_addresses/resources/strings/BUILD.gn b/components/plus_addresses/resources/strings/BUILD.gn
index 7abfa69..413fbc52 100644
--- a/components/plus_addresses/resources/strings/BUILD.gn
+++ b/components/plus_addresses/resources/strings/BUILD.gn
@@ -8,11 +8,6 @@
 
 if (is_android) {
   import("//build/config/android/rules.gni")
-  android_strings_java_resources =
-      [ "java/res/values/plus_addresses_strings.xml" ] +
-      process_file_template(
-          android_bundle_locales_as_resources,
-          [ "java/res/values-{{source_name_part}}/plus_addresses_strings.xml" ])
 }
 
 grit("strings") {
@@ -27,16 +22,12 @@
                 all_chrome_locales,
                 [ "plus_addresses_strings_{{source_name_part}}.pak" ])
   if (is_android) {
-    outputs += android_strings_java_resources
+    create_android_resources = true
   }
 }
 
 if (is_android) {
   java_strings_grd_prebuilt("strings_grd") {
-    grit_output_dir =
-        "$root_gen_dir/components/plus_addresses/resources/strings/java/res"
-    generated_files =
-        rebase_path(android_strings_java_resources, "java/res", ".")
-    deps = [ ":strings" ]
+    generating_target = ":strings"
   }
 }
diff --git a/components/policy/core/common/cloud/cloud_policy_client.cc b/components/policy/core/common/cloud/cloud_policy_client.cc
index 0032446..ba599893 100644
--- a/components/policy/core/common/cloud/cloud_policy_client.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client.cc
@@ -9,7 +9,6 @@
 #include <variant>
 
 #include "base/check.h"
-#include "base/check_is_test.h"
 #include "base/containers/contains.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
@@ -1134,10 +1133,6 @@
   // Unsigned commands and NONE signature are not supported.
   DCHECK_NE(signature_type, em::PolicyFetchRequest::NONE);
 
-  if (reason == RemoteCommandsFetchReason::kTest) {
-    CHECK_IS_TEST();
-  }
-
   auto params = DMServerJobConfiguration::CreateParams::WithClient(
       DeviceManagementService::JobConfiguration::TYPE_REMOTE_COMMANDS, this);
   params.auth_data = DMAuth::FromDMToken(dm_token_);
diff --git a/components/policy/resources/templates/policies.yaml b/components/policy/resources/templates/policies.yaml
index 0a51219..2b7334f3 100644
--- a/components/policy/resources/templates/policies.yaml
+++ b/components/policy/resources/templates/policies.yaml
@@ -1359,6 +1359,7 @@
   1358: TabGroupSharingSettings
   1359: NTPFooterManagementNoticeEnabled
   1360: NTPFooterThemeAttributionEnabled
+  1361: ClearWindowNameForNewBrowsingContextGroup
 
 atomic_groups:
   1: Homepage
diff --git a/components/policy/resources/templates/policy_definitions/ContentSettings/ClearWindowNameForNewBrowsingContextGroup.yaml b/components/policy/resources/templates/policy_definitions/ContentSettings/ClearWindowNameForNewBrowsingContextGroup.yaml
new file mode 100644
index 0000000..31688cb
--- /dev/null
+++ b/components/policy/resources/templates/policy_definitions/ContentSettings/ClearWindowNameForNewBrowsingContextGroup.yaml
@@ -0,0 +1,58 @@
+caption: Choose whether to allow clearing window.name for cross-site top-level
+  navigations resulting in a new browsing context group.
+
+default: true
+
+desc: |-
+  This policy controls whether window.name can be cleared for cross-site
+  top-level navigations which result in a new browsing context group being
+  created when the ClearCrossSiteCrossBrowsingContextGroupWindowName variation is
+  enabled.
+
+  The ClearCrossSiteCrossBrowsingContextGroupWindowName variation controls whether
+  window.name will be cleared for cross-site top-level navigations. Examples of
+  such navigations include a user navigating to a new site via the omnibox or
+  clicking on a link to a new site when the link uses "target='_blank'
+  rel='noopener'". Clearing window.name in these cases prevents information from
+  potentially leaking between sites via the window.name property, improving user
+  privacy. ClearWindowNameForNewBrowsingContextGroup policy is in place to
+  restore the previous behavior. When the
+  ClearCrossSiteCrossBrowsingContextGroupWindowName variation is enabled
+  window.name will be cleared for qualifying navigations if this policy is set
+  to Enabled or not set. If it is disabled, window.name will not be cleared.
+
+  If you must use the policy to disable window.name clearing on qualifying
+  navigations, please file a bug on
+  <ph name="BUG_URL">$1<ex>https://crbug.com/new?component=1456652&amp;template=1937639&amp;cc=ladan@chromium.org,miketaylr@chromium.org&amp;noWizard=true</ex></ph>
+  explaining your use case. The policy is scheduled to
+  be offered through <ph name="PRODUCT_NAME">$2<ex>Google Chrome</ex></ph>
+  version 142.
+
+example_value: false
+
+features:
+  dynamic_refresh: true
+  per_profile: true
+
+future_on:
+ - chrome.*
+ - chrome_os
+ - android
+
+items:
+- caption: Clear window.name when the navigation is top-level, cross-site and swaps BrowsingContextGroup.
+  value: true
+- caption: Do not clear window.name when the navigation is top-level, cross-site and swaps BrowsingContextGroup.
+  value: false
+
+owners:
+- ladan@chromium.org
+- potassium-katabolism@google.com
+
+schema:
+  type: boolean
+
+tags: []
+
+type: main
+
diff --git a/components/policy/test/data/pref_mapping/ClearWindowNameForNewBrowsingContextGroup.json b/components/policy/test/data/pref_mapping/ClearWindowNameForNewBrowsingContextGroup.json
new file mode 100644
index 0000000..8a2dbc8a
--- /dev/null
+++ b/components/policy/test/data/pref_mapping/ClearWindowNameForNewBrowsingContextGroup.json
@@ -0,0 +1,20 @@
+[
+  {
+    "os": [
+      "win",
+      "linux",
+      "mac",
+      "chromeos",
+      "android",
+      "fuchsia"
+    ],
+    "simple_policy_pref_mapping_test": {
+      "pref_name": "profile.content_settings.clear_window_name_for_new_browsing_context_group",
+      "default_value": true,
+      "values_to_test": [
+        true,
+        false
+      ]
+    }
+  }
+]
diff --git a/components/reading_list/core/reading_list_local_data_batch_uploader.cc b/components/reading_list/core/reading_list_local_data_batch_uploader.cc
index fde1e3dd..fea4527e 100644
--- a/components/reading_list/core/reading_list_local_data_batch_uploader.cc
+++ b/components/reading_list/core/reading_list_local_data_batch_uploader.cc
@@ -57,6 +57,23 @@
   dual_reading_list_model_->MarkAllForUploadToSyncServerIfNeeded();
 }
 
+void ReadingListLocalDataBatchUploader::TriggerLocalDataMigrationForItems(
+    std::vector<syncer::LocalDataItemModel::DataId> items) {
+  CHECK(base::FeatureList::IsEnabled(
+      syncer::kSyncReadingListBatchUploadSelectedItems));
+
+  if (!CanUpload()) {
+    return;
+  }
+
+  dual_reading_list_model_->MarkEntriesForUploadToSyncServerIfNeeded(
+      base::MakeFlatSet<GURL>(
+          items, /*comp=*/{},
+          /*proj=*/[](const syncer::LocalDataItemModel::DataId& id) {
+            return std::get<GURL>(id);
+          }));
+}
+
 bool ReadingListLocalDataBatchUploader::CanUpload() const {
   // TODO(crbug.com/354146311): Check GetAccountModelIfSyncing() isn't null
   return dual_reading_list_model_ && dual_reading_list_model_->loaded();
diff --git a/components/reading_list/core/reading_list_local_data_batch_uploader.h b/components/reading_list/core/reading_list_local_data_batch_uploader.h
index 14c04f4..b2c8dd4 100644
--- a/components/reading_list/core/reading_list_local_data_batch_uploader.h
+++ b/components/reading_list/core/reading_list_local_data_batch_uploader.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_READING_LIST_CORE_READING_LIST_LOCAL_DATA_BATCH_UPLOADER_H_
 #define COMPONENTS_READING_LIST_CORE_READING_LIST_LOCAL_DATA_BATCH_UPLOADER_H_
 
+#include <vector>
+
 #include "base/memory/raw_ptr.h"
 #include "components/sync/service/data_type_local_data_batch_uploader.h"
 
@@ -33,6 +35,8 @@
   void GetLocalDataDescription(
       base::OnceCallback<void(syncer::LocalDataDescription)> callback) override;
   void TriggerLocalDataMigration() override;
+  void TriggerLocalDataMigrationForItems(
+      std::vector<syncer::LocalDataItemModel::DataId> items) override;
 
  private:
   bool CanUpload() const;
diff --git a/components/reading_list/core/reading_list_local_data_batch_uploader_unittest.cc b/components/reading_list/core/reading_list_local_data_batch_uploader_unittest.cc
index a3fec011..1dab608 100644
--- a/components/reading_list/core/reading_list_local_data_batch_uploader_unittest.cc
+++ b/components/reading_list/core/reading_list_local_data_batch_uploader_unittest.cc
@@ -156,6 +156,8 @@
   ReadingListLocalDataBatchUploader uploader(nullptr);
 
   uploader.TriggerLocalDataMigration();
+  uploader.TriggerLocalDataMigrationForItems(
+      {syncer::LocalDataItemModel::DataId(GURL("https://local.com"))});
 
   // Should not crash;
 }
@@ -164,6 +166,8 @@
   ReadingListLocalDataBatchUploader uploader(dual_reading_list_model());
 
   uploader.TriggerLocalDataMigration();
+  uploader.TriggerLocalDataMigrationForItems(
+      {syncer::LocalDataItemModel::DataId(GURL("https://local.com"))});
 
   // Should not crash;
 }
@@ -191,5 +195,50 @@
       DualReadingListModel::StorageStateForTesting::kExistsInAccountModelOnly);
 }
 
+TEST_F(ReadingListLocalDataBatchUploaderTest, OnlySelectedItemsGetUploaded) {
+  LoadModel();
+  dual_reading_list_model()->GetLocalOrSyncableModel()->AddOrReplaceEntry(
+      GURL("https://local1.com"), "local1", reading_list::ADDED_VIA_CURRENT_APP,
+      /*estimated_read_time=*/base::TimeDelta());
+  dual_reading_list_model()->GetLocalOrSyncableModel()->AddOrReplaceEntry(
+      GURL("https://local2.com"), "local2", reading_list::ADDED_VIA_CURRENT_APP,
+      /*estimated_read_time=*/base::TimeDelta());
+  dual_reading_list_model()->GetAccountModelIfSyncing()->AddOrReplaceEntry(
+      GURL("https://account.com"), "account",
+      reading_list::ADDED_VIA_CURRENT_APP,
+      /*estimated_read_time=*/base::TimeDelta());
+  ReadingListLocalDataBatchUploader uploader(dual_reading_list_model());
+
+  ASSERT_EQ(dual_reading_list_model()->GetStorageStateForURLForTesting(
+                GURL("https://local1.com")),
+            DualReadingListModel::StorageStateForTesting::
+                kExistsInLocalOrSyncableModelOnly);
+  ASSERT_EQ(dual_reading_list_model()->GetStorageStateForURLForTesting(
+                GURL("https://local2.com")),
+            DualReadingListModel::StorageStateForTesting::
+                kExistsInLocalOrSyncableModelOnly);
+  ASSERT_EQ(
+      dual_reading_list_model()->GetStorageStateForURLForTesting(
+          GURL("https://account.com")),
+      DualReadingListModel::StorageStateForTesting::kExistsInAccountModelOnly);
+
+  uploader.TriggerLocalDataMigrationForItems(
+      {syncer::LocalDataItemModel::DataId(GURL("https://local2.com"))});
+
+  EXPECT_EQ(dual_reading_list_model()->GetStorageStateForURLForTesting(
+                GURL("https://local1.com")),
+            DualReadingListModel::StorageStateForTesting::
+                kExistsInLocalOrSyncableModelOnly);
+
+  EXPECT_EQ(
+      dual_reading_list_model()->GetStorageStateForURLForTesting(
+          GURL("https://local2.com")),
+      DualReadingListModel::StorageStateForTesting::kExistsInAccountModelOnly);
+  EXPECT_EQ(
+      dual_reading_list_model()->GetStorageStateForURLForTesting(
+          GURL("https://account.com")),
+      DualReadingListModel::StorageStateForTesting::kExistsInAccountModelOnly);
+}
+
 }  // namespace
 }  // namespace reading_list
diff --git a/components/resources/default_100_percent/search_engine_choice/DIR_METADATA b/components/resources/default_100_percent/search_engine_choice/DIR_METADATA
deleted file mode 100644
index c8383f40..0000000
--- a/components/resources/default_100_percent/search_engine_choice/DIR_METADATA
+++ /dev/null
@@ -1 +0,0 @@
-mixins: "//chrome/browser/search_engine_choice/COMMON_METADATA"
diff --git a/components/resources/default_100_percent/search_engine_choice/OWNERS b/components/resources/default_100_percent/search_engine_choice/OWNERS
deleted file mode 100644
index c7aa42b..0000000
--- a/components/resources/default_100_percent/search_engine_choice/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
diff --git a/components/resources/default_100_percent/search_engine_choice/bing_com.png b/components/resources/default_100_percent/search_engine_choice/bing_com.png
deleted file mode 100644
index f24e65c..0000000
--- a/components/resources/default_100_percent/search_engine_choice/bing_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_100_percent/search_engine_choice/duckduckgo_com.png b/components/resources/default_100_percent/search_engine_choice/duckduckgo_com.png
deleted file mode 100644
index eed2cf1..0000000
--- a/components/resources/default_100_percent/search_engine_choice/duckduckgo_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_100_percent/search_engine_choice/ecosia_org.png b/components/resources/default_100_percent/search_engine_choice/ecosia_org.png
deleted file mode 100644
index 0bf2216..0000000
--- a/components/resources/default_100_percent/search_engine_choice/ecosia_org.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_100_percent/search_engine_choice/lilo_org.png b/components/resources/default_100_percent/search_engine_choice/lilo_org.png
deleted file mode 100644
index 9129fa2..0000000
--- a/components/resources/default_100_percent/search_engine_choice/lilo_org.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_100_percent/search_engine_choice/mail_ru.png b/components/resources/default_100_percent/search_engine_choice/mail_ru.png
deleted file mode 100644
index 2808a4a..0000000
--- a/components/resources/default_100_percent/search_engine_choice/mail_ru.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_100_percent/search_engine_choice/privacywall_org.png b/components/resources/default_100_percent/search_engine_choice/privacywall_org.png
deleted file mode 100644
index 08e56bb..0000000
--- a/components/resources/default_100_percent/search_engine_choice/privacywall_org.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_100_percent/search_engine_choice/qwant_com.png b/components/resources/default_100_percent/search_engine_choice/qwant_com.png
deleted file mode 100644
index 6866e8e..0000000
--- a/components/resources/default_100_percent/search_engine_choice/qwant_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_100_percent/search_engine_choice/search_brave_com.png b/components/resources/default_100_percent/search_engine_choice/search_brave_com.png
deleted file mode 100644
index 5c1ec391..0000000
--- a/components/resources/default_100_percent/search_engine_choice/search_brave_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_100_percent/search_engine_choice/seznam.png b/components/resources/default_100_percent/search_engine_choice/seznam.png
deleted file mode 100644
index eeed14b..0000000
--- a/components/resources/default_100_percent/search_engine_choice/seznam.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_100_percent/search_engine_choice/startpage_com.png b/components/resources/default_100_percent/search_engine_choice/startpage_com.png
deleted file mode 100644
index b138da6..0000000
--- a/components/resources/default_100_percent/search_engine_choice/startpage_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_100_percent/search_engine_choice/yahoo_com.png b/components/resources/default_100_percent/search_engine_choice/yahoo_com.png
deleted file mode 100644
index 890a748b..0000000
--- a/components/resources/default_100_percent/search_engine_choice/yahoo_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_100_percent/search_engine_choice/yep_com.png b/components/resources/default_100_percent/search_engine_choice/yep_com.png
deleted file mode 100644
index 21780fff..0000000
--- a/components/resources/default_100_percent/search_engine_choice/yep_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/search_engine_choice/OWNERS b/components/resources/default_200_percent/search_engine_choice/OWNERS
deleted file mode 100644
index c7aa42b..0000000
--- a/components/resources/default_200_percent/search_engine_choice/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
diff --git a/components/resources/default_200_percent/search_engine_choice/bing_com.png b/components/resources/default_200_percent/search_engine_choice/bing_com.png
deleted file mode 100644
index 93a7d0c9..0000000
--- a/components/resources/default_200_percent/search_engine_choice/bing_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/search_engine_choice/duckduckgo_com.png b/components/resources/default_200_percent/search_engine_choice/duckduckgo_com.png
deleted file mode 100644
index 613f6b8..0000000
--- a/components/resources/default_200_percent/search_engine_choice/duckduckgo_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/search_engine_choice/ecosia_org.png b/components/resources/default_200_percent/search_engine_choice/ecosia_org.png
deleted file mode 100644
index f4d7ae7..0000000
--- a/components/resources/default_200_percent/search_engine_choice/ecosia_org.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/search_engine_choice/lilo_org.png b/components/resources/default_200_percent/search_engine_choice/lilo_org.png
deleted file mode 100644
index beb4fc7..0000000
--- a/components/resources/default_200_percent/search_engine_choice/lilo_org.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/search_engine_choice/mail_ru.png b/components/resources/default_200_percent/search_engine_choice/mail_ru.png
deleted file mode 100644
index 55335fb..0000000
--- a/components/resources/default_200_percent/search_engine_choice/mail_ru.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/search_engine_choice/privacywall_org.png b/components/resources/default_200_percent/search_engine_choice/privacywall_org.png
deleted file mode 100644
index 982a7dd4..0000000
--- a/components/resources/default_200_percent/search_engine_choice/privacywall_org.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/search_engine_choice/qwant_com.png b/components/resources/default_200_percent/search_engine_choice/qwant_com.png
deleted file mode 100644
index 20de57e..0000000
--- a/components/resources/default_200_percent/search_engine_choice/qwant_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/search_engine_choice/search_brave_com.png b/components/resources/default_200_percent/search_engine_choice/search_brave_com.png
deleted file mode 100644
index 415aa70..0000000
--- a/components/resources/default_200_percent/search_engine_choice/search_brave_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/search_engine_choice/seznam.png b/components/resources/default_200_percent/search_engine_choice/seznam.png
deleted file mode 100644
index 8e02cc5..0000000
--- a/components/resources/default_200_percent/search_engine_choice/seznam.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/search_engine_choice/startpage_com.png b/components/resources/default_200_percent/search_engine_choice/startpage_com.png
deleted file mode 100644
index 0fc22c9..0000000
--- a/components/resources/default_200_percent/search_engine_choice/startpage_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/search_engine_choice/yahoo_com.png b/components/resources/default_200_percent/search_engine_choice/yahoo_com.png
deleted file mode 100644
index 4603863..0000000
--- a/components/resources/default_200_percent/search_engine_choice/yahoo_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_200_percent/search_engine_choice/yep_com.png b/components/resources/default_200_percent/search_engine_choice/yep_com.png
deleted file mode 100644
index 7d80a1e..0000000
--- a/components/resources/default_200_percent/search_engine_choice/yep_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/search_engine_choice/OWNERS b/components/resources/default_300_percent/search_engine_choice/OWNERS
deleted file mode 100644
index c7aa42b..0000000
--- a/components/resources/default_300_percent/search_engine_choice/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
diff --git a/components/resources/default_300_percent/search_engine_choice/bing_com.png b/components/resources/default_300_percent/search_engine_choice/bing_com.png
deleted file mode 100644
index 8cd95cd..0000000
--- a/components/resources/default_300_percent/search_engine_choice/bing_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/search_engine_choice/duckduckgo_com.png b/components/resources/default_300_percent/search_engine_choice/duckduckgo_com.png
deleted file mode 100644
index 133f75d..0000000
--- a/components/resources/default_300_percent/search_engine_choice/duckduckgo_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/search_engine_choice/ecosia_org.png b/components/resources/default_300_percent/search_engine_choice/ecosia_org.png
deleted file mode 100644
index 6d0d85f..0000000
--- a/components/resources/default_300_percent/search_engine_choice/ecosia_org.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/search_engine_choice/lilo_org.png b/components/resources/default_300_percent/search_engine_choice/lilo_org.png
deleted file mode 100644
index 106d3d1..0000000
--- a/components/resources/default_300_percent/search_engine_choice/lilo_org.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/search_engine_choice/mail_ru.png b/components/resources/default_300_percent/search_engine_choice/mail_ru.png
deleted file mode 100644
index 3ce376e2..0000000
--- a/components/resources/default_300_percent/search_engine_choice/mail_ru.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/search_engine_choice/privacywall_org.png b/components/resources/default_300_percent/search_engine_choice/privacywall_org.png
deleted file mode 100644
index 44eef1cd..0000000
--- a/components/resources/default_300_percent/search_engine_choice/privacywall_org.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/search_engine_choice/qwant_com.png b/components/resources/default_300_percent/search_engine_choice/qwant_com.png
deleted file mode 100644
index 954ee33..0000000
--- a/components/resources/default_300_percent/search_engine_choice/qwant_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/search_engine_choice/search_brave_com.png b/components/resources/default_300_percent/search_engine_choice/search_brave_com.png
deleted file mode 100644
index 8a305df..0000000
--- a/components/resources/default_300_percent/search_engine_choice/search_brave_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/search_engine_choice/seznam.png b/components/resources/default_300_percent/search_engine_choice/seznam.png
deleted file mode 100644
index 1491f2e..0000000
--- a/components/resources/default_300_percent/search_engine_choice/seznam.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/search_engine_choice/startpage_com.png b/components/resources/default_300_percent/search_engine_choice/startpage_com.png
deleted file mode 100644
index a3bf360..0000000
--- a/components/resources/default_300_percent/search_engine_choice/startpage_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/search_engine_choice/yahoo_com.png b/components/resources/default_300_percent/search_engine_choice/yahoo_com.png
deleted file mode 100644
index 339eaa3..0000000
--- a/components/resources/default_300_percent/search_engine_choice/yahoo_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/default_300_percent/search_engine_choice/yep_com.png b/components/resources/default_300_percent/search_engine_choice/yep_com.png
deleted file mode 100644
index 17a6a39..0000000
--- a/components/resources/default_300_percent/search_engine_choice/yep_com.png
+++ /dev/null
Binary files differ
diff --git a/components/resources/search_engine_choice_scaled_resources.grdp b/components/resources/search_engine_choice_scaled_resources.grdp
index 656fbe83..44e287f 100644
--- a/components/resources/search_engine_choice_scaled_resources.grdp
+++ b/components/resources/search_engine_choice_scaled_resources.grdp
@@ -1,52 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- This file is generated using generate_search_engine_icons.py -->
-<!-- Don't modify it manually -->
 <grit-part>
   <if expr="_google_chrome">
     <structure type="chrome_scaled_image" name="IDR_GOOGLE_COM_PNG" file="google_chrome/google_search_logo.png" />
     <structure type="chrome_scaled_image" name="IDR_SEARCH_ENGINE_GOOGLE_IMAGE" file="google_chrome/google_search_logo.png" />
   </if>
-  <structure type="chrome_scaled_image" name="IDR_AR_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_AT_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_AU_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_BING_COM_PNG" file="search_engine_choice/bing_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_BR_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_CA_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_CH_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_CL_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_CO_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_DE_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_DK_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_DUCKDUCKGO_COM_PNG" file="search_engine_choice/duckduckgo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_ECOSIA_ORG_PNG" file="search_engine_choice/ecosia_org.png" />
-  <structure type="chrome_scaled_image" name="IDR_EMEA_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_ES_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_FI_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_FR_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_HK_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_ID_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_IN_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_IT_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_LILO_ORG_PNG" file="search_engine_choice/lilo_org.png" />
-  <structure type="chrome_scaled_image" name="IDR_MAIL_RU_PNG" file="search_engine_choice/mail_ru.png" />
-  <structure type="chrome_scaled_image" name="IDR_MALAYSIA_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_MX_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_NL_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_NZ_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_PE_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_PH_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_PRIVACYWALL_ORG_PNG" file="search_engine_choice/privacywall_org.png" />
-  <structure type="chrome_scaled_image" name="IDR_QWANT_COM_PNG" file="search_engine_choice/qwant_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_SEARCH_BRAVE_COM_PNG" file="search_engine_choice/search_brave_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_SEZNAM_PNG" file="search_engine_choice/seznam.png" />
-  <structure type="chrome_scaled_image" name="IDR_SE_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_SG_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_STARTPAGE_COM_PNG" file="search_engine_choice/startpage_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_TH_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_TR_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_TW_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_UK_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_YAHOO_COM_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_YAHOO_CO_JP_PNG" file="search_engine_choice/yahoo_com.png" />
-  <structure type="chrome_scaled_image" name="IDR_YEP_COM_PNG" file="search_engine_choice/yep_com.png" />
 </grit-part>
diff --git a/components/saved_tab_groups/public/features.cc b/components/saved_tab_groups/public/features.cc
index ddc25b1a..af87369 100644
--- a/components/saved_tab_groups/public/features.cc
+++ b/components/saved_tab_groups/public/features.cc
@@ -82,6 +82,11 @@
              "EnableOriginatingSavedGroupCleanUp",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// Swaps the click actions for the tab group header.
+BASE_FEATURE(kLeftClickOpensTabGroupBubble,
+             "LeftClickOpensTabGroupBubble",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 bool IsTabGroupSyncServiceDesktopMigrationEnabled() {
   return (base::FeatureList::IsEnabled(kTabGroupSyncServiceDesktopMigration) ||
           data_sharing::features::IsDataSharingFunctionalityEnabled());
diff --git a/components/saved_tab_groups/public/features.h b/components/saved_tab_groups/public/features.h
index a16d239..53e4b843 100644
--- a/components/saved_tab_groups/public/features.h
+++ b/components/saved_tab_groups/public/features.h
@@ -32,6 +32,8 @@
 
 BASE_DECLARE_FEATURE(kEnableOriginatingSavedGroupCleanUp);
 
+BASE_DECLARE_FEATURE(kLeftClickOpensTabGroupBubble);
+
 extern bool IsTabGroupSyncServiceDesktopMigrationEnabled();
 
 extern bool IsTabGroupSyncDelegateAndroidEnabled();
diff --git a/components/search_engines/BUILD.gn b/components/search_engines/BUILD.gn
index 06d2976b..3afab246 100644
--- a/components/search_engines/BUILD.gn
+++ b/components/search_engines/BUILD.gn
@@ -130,7 +130,6 @@
   if (!is_android) {
     sources += [ "search_engine_choice/generated_marketing_snippets.cc" ]
     deps += [
-      ":generate_search_engine_icons",
       "//components/resources:components_scaled_resources_grit",
       "//third_party/search_engines_data:search_engines_scaled_resources",
       "//ui/resources",
@@ -296,32 +295,3 @@
 
   deps = [ "//base" ]
 }
-
-if (!is_android) {
-  action("generate_search_engine_icons") {
-    script = "//tools/search_engine_choice/generate_search_engine_icons.py"
-
-    inputs = [
-      "//tools/search_engine_choice/generate_search_engine_icons_config.json",
-      "//tools/json_comment_eater/json_comment_eater.py",
-      "//tools/search_engine_choice/generate_search_engine_icons.py",
-      "//tools/search_engine_choice/search_engine_icons_utils.py",
-      "//third_party/search_engines_data/resources/definitions/prepopulated_engines.json",
-      "//third_party/search_engines_data/resources/definitions/regional_settings.json",
-    ]
-
-    outputs = [
-      "$root_gen_dir/chrome/browser/ui/webui/search_engine_choice/generated_icon_utils-inc.cc",
-      "$target_gen_dir/generated_search_engine_resource_ids-inc.cc",
-    ]
-
-    args = [
-      rebase_path("//", root_build_dir),
-      rebase_path("$target_gen_dir/generated_search_engine_resource_ids-inc.cc",
-                  root_build_dir),
-      rebase_path(
-          "$target_gen_dir/../../chrome/browser/ui/webui/search_engine_choice/generated_icon_utils-inc.cc",
-          root_build_dir),
-    ]
-  }
-}
diff --git a/components/search_engines/search_engine_choice/search_engine_choice_service_unittest.cc b/components/search_engines/search_engine_choice/search_engine_choice_service_unittest.cc
index f220928..767d71a 100644
--- a/components/search_engines/search_engine_choice/search_engine_choice_service_unittest.cc
+++ b/components/search_engines/search_engine_choice/search_engine_choice_service_unittest.cc
@@ -1188,34 +1188,4 @@
                          SearchEngineChoiceUtilsParamTest,
                          ::testing::ValuesIn(kRepromptTestParams));
 
-#if !BUILDFLAG(IS_ANDROID)
-
-class SearchEngineChoiceUtilsResourceIdsTest : public ::testing::Test {
- protected:
-  SearchEnginesTestEnvironment search_engine_test_environment_;
-};
-
-// Verifies that all prepopulated search engines associated with EEA countries
-// have an icon.
-TEST_F(SearchEngineChoiceUtilsResourceIdsTest, GetIconResourceId) {
-  // Make sure the country is not forced.
-  ASSERT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kSearchEngineChoiceCountry));
-
-  for (CountryId country_id : regional_capabilities::kEeaChoiceCountriesIds) {
-    search_engine_test_environment_.pref_service().SetInteger(
-        country_codes::kCountryIDAtInstall, country_id.Serialize());
-    std::vector<std::unique_ptr<TemplateURLData>> urls =
-        search_engine_test_environment_.prepopulate_data_resolver()
-            .GetPrepopulatedEngines();
-    for (const std::unique_ptr<TemplateURLData>& url : urls) {
-      EXPECT_GE(search_engines::GetIconResourceId(url->keyword()), 0)
-          << "Missing icon for " << url->keyword() << ". Try re-running "
-          << "`tools/search_engine_choice/generate_search_engine_icons.py`.";
-    }
-  }
-}
-
-#endif  // !BUILDFLAG(IS_ANDROID)
-
 }  // namespace search_engines
diff --git a/components/search_engines/search_engine_choice/search_engine_choice_utils.cc b/components/search_engines/search_engine_choice/search_engine_choice_utils.cc
index c02d3ff..42a7f07 100644
--- a/components/search_engines/search_engine_choice/search_engine_choice_utils.cc
+++ b/components/search_engines/search_engine_choice/search_engine_choice_utils.cc
@@ -49,11 +49,6 @@
 namespace search_engines {
 
 namespace {
-#if !BUILDFLAG(IS_ANDROID)
-// Defines `kSearchEngineResourceIdMap`.
-#include "components/search_engines/generated_search_engine_resource_ids-inc.cc"
-#endif
-
 // Serialization keys for `ChoiceScreenDisplayState`.
 constexpr char kDisplayStateCountryIdKey[] = "country_id";
 constexpr char kDisplayStateSearchEnginesKey[] = "search_engines";
@@ -333,15 +328,6 @@
              : l10n_util::GetStringUTF16(snippet_resource_id);
 }
 
-int GetIconResourceId(const std::u16string& engine_keyword) {
-  // `kSearchEngineResourceIdMap` is defined in
-  // `components/search_engines/generated_search_engine_resource_ids-inc.cc`
-  const base::fixed_flat_map<std::u16string_view, int,
-                             kSearchEngineResourceIdMap.size()>::const_iterator
-      iterator = kSearchEngineResourceIdMap.find(engine_keyword);
-  return iterator == kSearchEngineResourceIdMap.cend() ? -1 : iterator->second;
-}
-
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 }  // namespace search_engines
diff --git a/components/search_engines/search_engine_choice/search_engine_choice_utils.h b/components/search_engines/search_engine_choice/search_engine_choice_utils.h
index d24c003..d084ff5a 100644
--- a/components/search_engines/search_engine_choice/search_engine_choice_utils.h
+++ b/components/search_engines/search_engine_choice/search_engine_choice_utils.h
@@ -329,13 +329,6 @@
 std::u16string GetMarketingSnippetString(
     const TemplateURLData& template_url_data);
 
-// Returns the resource ID for the icon associated with `engine_keyword`, or -1
-// if not found. All search engines prepopulated in EEA countries are guaranteed
-// to have an icon.
-// The function definition is generated by `generate_search_engine_icons.py`in
-// `generated_search_engine_resource_ids.cc`.
-int GetIconResourceId(const std::u16string& engine_keyword);
-
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 }  // namespace search_engines
diff --git a/components/signin/core/browser/cookie_settings_util.cc b/components/signin/core/browser/cookie_settings_util.cc
index 6c9bb21..45544d0 100644
--- a/components/signin/core/browser/cookie_settings_util.cc
+++ b/components/signin/core/browser/cookie_settings_util.cc
@@ -4,6 +4,8 @@
 
 #include "components/signin/core/browser/cookie_settings_util.h"
 
+#include <optional>
+
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "net/cookies/cookie_constants.h"
@@ -29,10 +31,12 @@
   return cookie_settings &&
          cookie_settings->IsFullCookieAccessAllowed(
              gaia_url, net::SiteForCookies::FromUrl(gaia_url),
-             url::Origin::Create(gaia_url), net::CookieSettingOverrides()) &&
+             url::Origin::Create(gaia_url), net::CookieSettingOverrides(),
+             /*cookie_partition_key=*/std::nullopt) &&
          cookie_settings->IsFullCookieAccessAllowed(
              google_url, net::SiteForCookies::FromUrl(google_url),
-             url::Origin::Create(google_url), net::CookieSettingOverrides());
+             url::Origin::Create(google_url), net::CookieSettingOverrides(),
+             /*cookie_partition_key=*/std::nullopt);
 #endif
 }
 
diff --git a/components/signin/public/base/signin_switches.cc b/components/signin/public/base/signin_switches.cc
index 3163a62..d23fefb4 100644
--- a/components/signin/public/base/signin_switches.cc
+++ b/components/signin/public/base/signin_switches.cc
@@ -156,11 +156,11 @@
 
 BASE_FEATURE(kEnablePreferencesAccountStorage,
              "EnablePreferencesAccountStorage",
-#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)
-             base::FEATURE_ENABLED_BY_DEFAULT
-#else
+#if BUILDFLAG(IS_CHROMEOS)
              base::FEATURE_DISABLED_BY_DEFAULT
-#endif
+#else
+             base::FEATURE_ENABLED_BY_DEFAULT
+#endif  // BUILDFLAG(IS_CHROMEOS)
 );
 
 // This feature disables all extended sync promos.
diff --git a/components/strings/BUILD.gn b/components/strings/BUILD.gn
index 3119b6b..dd44c6a 100644
--- a/components/strings/BUILD.gn
+++ b/components/strings/BUILD.gn
@@ -13,11 +13,6 @@
 
 if (is_android) {
   import("//build/config/android/rules.gni")
-  android_components_strings_java_resources =
-      [ "java/res/values/components_strings.xml" ] +
-      process_file_template(
-          android_bundle_locales_as_resources,
-          [ "java/res/values-{{source_name_part}}/components_strings.xml" ])
 }
 
 group("strings") {
@@ -50,16 +45,13 @@
   }
 
   if (is_android) {
-    outputs += android_components_strings_java_resources
+    create_android_resources = true
   }
 }
 
 if (is_android) {
   java_strings_grd_prebuilt("components_strings_grd") {
-    grit_output_dir = "$root_gen_dir/components/strings/java/res"
-    generated_files =
-        rebase_path(android_components_strings_java_resources, "java/res", ".")
-    deps = [ ":components_strings" ]
+    generating_target = ":components_strings"
   }
 }
 
@@ -71,14 +63,6 @@
   }
 }
 
-if (is_android) {
-  android_components_locale_settings_java_resources =
-      [ "java/res/values/components_locale_settings.xml" ]
-  android_components_locale_settings_java_resources += process_file_template(
-          android_bundle_locales_as_resources,
-          [ "java/res/values-{{source_name_part}}/components_locale_settings.xml" ])
-}
-
 grit("components_locale_settings") {
   source = "../components_locale_settings.grd"
   outputs = [ "grit/components_locale_settings.h" ]
@@ -87,7 +71,7 @@
   }
 
   if (is_android) {
-    outputs += android_components_locale_settings_java_resources
+    create_android_resources = true
   }
 }
 
@@ -101,23 +85,10 @@
 
 if (is_android) {
   java_strings_grd_prebuilt("components_locale_settings_grd") {
-    grit_output_dir = "$root_gen_dir/components/strings/java/res"
-    generated_files =
-        rebase_path(android_components_locale_settings_java_resources,
-                    "java/res",
-                    ".")
-    deps = [ ":components_locale_settings" ]
+    generating_target = ":components_locale_settings"
   }
 }
 
-if (is_android) {
-  android_privacy_sandbox_strings_java_resources =
-      [ "java/res/values/privacy_sandbox_strings.xml" ]
-  android_privacy_sandbox_strings_java_resources += process_file_template(
-          android_bundle_locales_as_resources,
-          [ "java/res/values-{{source_name_part}}/privacy_sandbox_strings.xml" ])
-}
-
 grit("privacy_sandbox_strings") {
   source = "../privacy_sandbox_strings.grd"
   outputs = [ "grit/privacy_sandbox_strings.h" ]
@@ -126,17 +97,12 @@
   }
 
   if (is_android) {
-    outputs += android_privacy_sandbox_strings_java_resources
+    create_android_resources = true
   }
 }
 
 if (is_android) {
   java_strings_grd_prebuilt("privacy_sandbox_strings_grd") {
-    grit_output_dir = "$root_gen_dir/components/strings/java/res"
-    generated_files =
-        rebase_path(android_privacy_sandbox_strings_java_resources,
-                    "java/res",
-                    ".")
-    deps = [ ":privacy_sandbox_strings" ]
+    generating_target = ":privacy_sandbox_strings"
   }
 }
diff --git a/components/supervised_user/core/browser/BUILD.gn b/components/supervised_user/core/browser/BUILD.gn
index f2cd0bc..39f118e 100644
--- a/components/supervised_user/core/browser/BUILD.gn
+++ b/components/supervised_user/core/browser/BUILD.gn
@@ -18,6 +18,20 @@
   ]
 }
 
+source_set("test_support") {
+  testonly = true
+  sources = [ "supervised_user_sync_data_fake.h" ]
+  deps = [
+    ":browser",
+    "//base",
+    "//base/test:test_support",
+    "//components/prefs",
+    "//components/prefs:test_support",
+    "//components/supervised_user/core/common:common",
+    "//components/sync_preferences:test_support",
+  ]
+}
+
 static_library("mocked") {
   testonly = true
   sources = [
@@ -180,6 +194,7 @@
     ":browser",
     ":fetcher",
     ":fetcher_config",
+    ":test_support",
     "//base",
     "//base/test:test_support",
     "//components/content_settings/core/browser:test_support",
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 b8c7a80..f08c86c 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
@@ -15,6 +15,7 @@
 #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_sync_data_fake.h"
 #include "components/supervised_user/core/browser/supervised_user_url_filter.h"
 #include "components/supervised_user/core/browser/supervised_user_utils.h"
 #include "components/supervised_user/core/common/pref_names.h"
@@ -42,10 +43,8 @@
     RegisterProfilePrefs(pref_service_.registry());
     SupervisedUserMetricsService::RegisterProfilePrefs(
         pref_service_.registry());
-
+    supervised_user_sync_data_fake_.Init(pref_service_);
     settings_service_.Init(pref_service_.user_prefs_store());
-
-    EnableParentalControls(pref_service_);
     supervised_user_service_ = std::make_unique<SupervisedUserService>(
         identity_test_env_.identity_manager(),
         test_url_loader_factory_.GetSafeWeakWrapper(), pref_service_,
@@ -92,6 +91,7 @@
 
   SupervisedUserSettingsService settings_service_;
   syncer::MockSyncService sync_service_;
+  SupervisedUserSyncDataFake supervised_user_sync_data_fake_;
 
   std::unique_ptr<SupervisedUserMetricsService>
       supervised_user_metrics_service_;
@@ -99,6 +99,7 @@
 
 // Tests that the recorded day is updated after more than one day passes.
 TEST_F(SupervisedUserMetricsServiceTest, NewDayAfterMultipleDays) {
+  EnableParentalControls(pref_service_);
   CreateMetricsService();
 
   task_environment_.FastForwardBy(base::Days(1) + base::Hours(1));
@@ -109,6 +110,7 @@
 
 // Tests that the recorded day is updated after metrics service is created.
 TEST_F(SupervisedUserMetricsServiceTest, NewDayAfterServiceCreation) {
+  EnableParentalControls(pref_service_);
   CreateMetricsService();
 
   task_environment_.FastForwardBy(base::Hours(1));
@@ -151,6 +153,7 @@
 TEST_F(SupervisedUserMetricsServiceTest, RecordDefaultMetrics) {
   // If the parent has not changed their configuration the supervised user
   // should be subject to default mature sites blocking.
+  EnableParentalControls(pref_service_);
   CreateMetricsService();
   histogram_tester_.ExpectUniqueSample(
       SupervisedUserURLFilter::GetWebFilterTypeHistogramNameForTest(),
diff --git a/components/supervised_user/core/browser/supervised_user_pref_store.cc b/components/supervised_user/core/browser/supervised_user_pref_store.cc
index bd83c7aa..400bbce 100644
--- a/components/supervised_user/core/browser/supervised_user_pref_store.cc
+++ b/components/supervised_user/core/browser/supervised_user_pref_store.cc
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/command_line.h"
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/values.h"
@@ -31,6 +32,7 @@
 #include "components/sync/service/sync_prefs.h"
 #include "extensions/buildflags/buildflags.h"
 
+namespace supervised_user {
 namespace {
 
 struct SupervisedUserSettingsPrefMappingEntry {
@@ -71,6 +73,20 @@
 
 }  // namespace
 
+void SetSupervisedUserPrefStoreDefaults(PrefValueMap& pref_values) {
+  pref_values.SetInteger(
+      prefs::kDefaultSupervisedUserFilteringBehavior,
+      static_cast<int>(supervised_user::FilteringBehavior::kAllow));
+
+  pref_values.SetBoolean(policy::policy_prefs::kHideWebStoreIcon, false);
+  pref_values.SetBoolean(feed::prefs::kEnableSnippets, false);
+
+  if (base::FeatureList::IsEnabled(kAlignSafeSitesValueWithBrowserDefault)) {
+    pref_values.SetBoolean(prefs::kSupervisedUserSafeSites, true);
+  }
+}
+}  // namespace supervised_user
+
 SupervisedUserPrefStore::SupervisedUserPrefStore() = default;
 
 SupervisedUserPrefStore::SupervisedUserPrefStore(
@@ -129,13 +145,7 @@
   std::unique_ptr<PrefValueMap> old_prefs = std::move(prefs_);
   prefs_ = std::make_unique<PrefValueMap>();
   if (!settings.empty()) {
-    // Set hardcoded prefs and defaults.
-    prefs_->SetInteger(
-        prefs::kDefaultSupervisedUserFilteringBehavior,
-        static_cast<int>(supervised_user::FilteringBehavior::kAllow));
-
-    prefs_->SetBoolean(policy::policy_prefs::kHideWebStoreIcon, false);
-    prefs_->SetBoolean(feed::prefs::kEnableSnippets, false);
+    supervised_user::SetSupervisedUserPrefStoreDefaults(*prefs_.get());
 
 #if BUILDFLAG(IS_ANDROID)
     syncer::SyncPrefs::SetTypeDisabledByCustodian(
@@ -143,7 +153,8 @@
 #endif
 
     // Copy supervised user settings to prefs.
-    for (const auto& entry : kSupervisedUserSettingsPrefMapping) {
+    for (const auto& entry :
+         supervised_user::kSupervisedUserSettingsPrefMapping) {
       const base::Value* value = settings.Find(entry.settings_name);
       if (value) {
         prefs_->SetValue(entry.pref_name, value->Clone());
diff --git a/components/supervised_user/core/browser/supervised_user_pref_store.h b/components/supervised_user/core/browser/supervised_user_pref_store.h
index 6973705..7cb5931 100644
--- a/components/supervised_user/core/browser/supervised_user_pref_store.h
+++ b/components/supervised_user/core/browser/supervised_user_pref_store.h
@@ -12,6 +12,7 @@
 #include "base/observer_list.h"
 #include "base/values.h"
 #include "components/prefs/pref_store.h"
+#include "components/prefs/pref_value_map.h"
 #include "components/supervised_user/core/common/supervised_users.h"
 
 namespace base {
@@ -22,6 +23,13 @@
 
 namespace supervised_user {
 class SupervisedUserSettingsService;
+
+// Writes default values to `pref_values` as used within the
+// SupervisedUserPrefStore. In this context "default" doesn't indicate the
+// bottom pref store in hierarchy which yields fallback values for the
+// PrefService, but rather preset values within a single PrefStore.
+void SetSupervisedUserPrefStoreDefaults(PrefValueMap& pref_values);
+
 }  // namespace supervised_user
 
 // A PrefStore that gets its values from supervised user settings via the
diff --git a/components/supervised_user/core/browser/supervised_user_preferences.cc b/components/supervised_user/core/browser/supervised_user_preferences.cc
index fd6da30..b055bf6 100644
--- a/components/supervised_user/core/browser/supervised_user_preferences.cc
+++ b/components/supervised_user/core/browser/supervised_user_preferences.cc
@@ -6,6 +6,7 @@
 
 #include <vector>
 
+#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/notreached.h"
 #include "components/google/core/common/google_util.h"
@@ -135,12 +136,16 @@
 }
 
 void RegisterProfilePrefs(PrefRegistrySimple* registry) {
+  // The default pref store should hold values that configure default browsing
+  // behavior.
   registry->RegisterStringPref(prefs::kSupervisedUserId, std::string());
   registry->RegisterDictionaryPref(prefs::kSupervisedUserManualHosts);
   registry->RegisterDictionaryPref(prefs::kSupervisedUserManualURLs);
   registry->RegisterIntegerPref(prefs::kDefaultSupervisedUserFilteringBehavior,
                                 static_cast<int>(FilteringBehavior::kAllow));
-  registry->RegisterBooleanPref(prefs::kSupervisedUserSafeSites, true);
+  registry->RegisterBooleanPref(
+      prefs::kSupervisedUserSafeSites,
+      !base::FeatureList::IsEnabled(kAlignSafeSitesValueWithBrowserDefault));
   for (const char* pref : kCustodianInfoPrefs) {
     registry->RegisterStringPref(pref, std::string());
   }
@@ -181,6 +186,11 @@
 #endif
 
 bool IsSafeSitesEnabled(const PrefService& pref_service) {
+  if (base::FeatureList::IsEnabled(kDecoupleSafeSitesFromMainSwitch) &&
+      base::FeatureList::IsEnabled(kAlignSafeSitesValueWithBrowserDefault)) {
+    return pref_service.GetBoolean(prefs::kSupervisedUserSafeSites);
+  }
+
   return supervised_user::IsSubjectToParentalControls(pref_service) &&
          pref_service.GetBoolean(prefs::kSupervisedUserSafeSites);
 }
diff --git a/components/supervised_user/core/browser/supervised_user_preferences_unittest.cc b/components/supervised_user/core/browser/supervised_user_preferences_unittest.cc
index 8bb530f..077a9c6 100644
--- a/components/supervised_user/core/browser/supervised_user_preferences_unittest.cc
+++ b/components/supervised_user/core/browser/supervised_user_preferences_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
+#include "components/supervised_user/core/browser/supervised_user_sync_data_fake.h"
 #include "components/supervised_user/core/browser/supervised_user_utils.h"
 #include "components/supervised_user/core/common/pref_names.h"
 #include "components/supervised_user/core/common/supervised_user_constants.h"
@@ -25,60 +26,64 @@
  public:
   void SetUp() override {
     auto* registry = pref_service_.registry();
-    supervised_user::RegisterProfilePrefs(registry);
+    RegisterProfilePrefs(registry);
+    supervised_user_sync_data_fake_.Init(pref_service_);
   }
 
  protected:
   TestingPrefServiceSimple pref_service_;
+  SupervisedUserSyncDataFake supervised_user_sync_data_fake_;
 };
 
-TEST_F(SupervisedUserPreferencesTest, RegisterProfilePrefs) {
+TEST_F(SupervisedUserPreferencesTest, RegisterProfilePrefsAndCheckDefaults) {
   // Checks the preference registration from the Setup.
   EXPECT_EQ(
       pref_service_.GetInteger(prefs::kDefaultSupervisedUserFilteringBehavior),
-      static_cast<int>(supervised_user::FilteringBehavior::kAllow));
-  EXPECT_EQ(pref_service_.GetBoolean(prefs::kSupervisedUserSafeSites), true);
-  EXPECT_FALSE(supervised_user::IsSubjectToParentalControls(pref_service_));
+      static_cast<int>(FilteringBehavior::kAllow));
+
+  // This is browser's default kSupervisedUserSafeSites setting (for
+  // unsupervised user).
+  EXPECT_EQ(pref_service_.GetBoolean(prefs::kSupervisedUserSafeSites), false);
+  EXPECT_FALSE(IsSubjectToParentalControls(pref_service_));
   // TODO(b/306376651): When we migrate more preference reading methods in this
   // library, add more test cases for their correct default values.
 }
 
 TEST_F(SupervisedUserPreferencesTest, ToggleParentalControlsSetsUserId) {
-  supervised_user::EnableParentalControls(pref_service_);
+  EnableParentalControls(pref_service_);
   EXPECT_EQ(pref_service_.GetString(prefs::kSupervisedUserId),
-            supervised_user::kChildAccountSUID);
+            kChildAccountSUID);
 
-  supervised_user::DisableParentalControls(pref_service_);
+  DisableParentalControls(pref_service_);
   EXPECT_EQ(pref_service_.GetString(prefs::kSupervisedUserId), std::string());
 }
 
 #if BUILDFLAG(IS_CHROMEOS)
 TEST_F(SupervisedUserPreferencesTest, ToggleParentalControlsSetsChildStatus) {
-  supervised_user::EnableParentalControls(pref_service_);
-  EXPECT_TRUE(supervised_user::IsChildAccountStatusKnown(pref_service_));
+  EnableParentalControls(pref_service_);
+  EXPECT_TRUE(IsChildAccountStatusKnown(pref_service_));
 
-  supervised_user::DisableParentalControls(pref_service_);
-  EXPECT_TRUE(supervised_user::IsChildAccountStatusKnown(pref_service_));
+  DisableParentalControls(pref_service_);
+  EXPECT_TRUE(IsChildAccountStatusKnown(pref_service_));
 }
 #endif
 
 TEST_F(SupervisedUserPreferencesTest, StartFetchingFamilyInfo) {
   kidsmanagement::ListMembersResponse list_family_members_response;
-  supervised_user::SetFamilyMemberAttributesForTesting(
+  SetFamilyMemberAttributesForTesting(
       list_family_members_response.add_members(),
       kidsmanagement::HEAD_OF_HOUSEHOLD, "username_hoh");
-  supervised_user::SetFamilyMemberAttributesForTesting(
+  SetFamilyMemberAttributesForTesting(
       list_family_members_response.add_members(), kidsmanagement::PARENT,
       "username_parent");
-  supervised_user::SetFamilyMemberAttributesForTesting(
+  SetFamilyMemberAttributesForTesting(
       list_family_members_response.add_members(), kidsmanagement::CHILD,
       "username_child");
-  supervised_user::SetFamilyMemberAttributesForTesting(
+  SetFamilyMemberAttributesForTesting(
       list_family_members_response.add_members(), kidsmanagement::MEMBER,
       "username_member");
 
-  supervised_user::RegisterFamilyPrefs(pref_service_,
-                                       list_family_members_response);
+  RegisterFamilyPrefs(pref_service_, list_family_members_response);
 
   EXPECT_EQ("username_hoh",
             pref_service_.GetString(prefs::kSupervisedUserCustodianName));
@@ -89,26 +94,24 @@
 TEST_F(SupervisedUserPreferencesTest, FieldsAreClearedForNonChildAccounts) {
   {
     kidsmanagement::ListMembersResponse list_family_members_response;
-    supervised_user::SetFamilyMemberAttributesForTesting(
+    SetFamilyMemberAttributesForTesting(
         list_family_members_response.add_members(),
         kidsmanagement::HEAD_OF_HOUSEHOLD, "username_hoh");
-    supervised_user::SetFamilyMemberAttributesForTesting(
+    SetFamilyMemberAttributesForTesting(
         list_family_members_response.add_members(), kidsmanagement::PARENT,
         "username_parent");
 
-    supervised_user::RegisterFamilyPrefs(pref_service_,
-                                         list_family_members_response);
+    RegisterFamilyPrefs(pref_service_, list_family_members_response);
 
-    for (const char* property : supervised_user::kCustodianInfoPrefs) {
+    for (const char* property : kCustodianInfoPrefs) {
       EXPECT_THAT(pref_service_.GetString(property), Not(IsEmpty()));
     }
   }
 
   {
     kidsmanagement::ListMembersResponse list_family_members_response;
-    supervised_user::RegisterFamilyPrefs(pref_service_,
-                                         list_family_members_response);
-    for (const char* property : supervised_user::kCustodianInfoPrefs) {
+    RegisterFamilyPrefs(pref_service_, list_family_members_response);
+    for (const char* property : kCustodianInfoPrefs) {
       EXPECT_THAT(pref_service_.GetString(property), IsEmpty());
     }
   }
@@ -116,35 +119,26 @@
 
 TEST_F(SupervisedUserPreferencesTest, IsSafeSitesEnabledSupervisedUser) {
   // Enables parental controls with safe sites checks.
-  pref_service_.SetSupervisedUserPref(prefs::kSupervisedUserSafeSites,
-                                      base::Value(true));
-  pref_service_.SetString(prefs::kSupervisedUserId,
-                            supervised_user::kChildAccountSUID);
-
-  EXPECT_TRUE(supervised_user::IsSafeSitesEnabled(pref_service_));
+  EnableParentalControls(pref_service_);
+  EXPECT_TRUE(IsSafeSitesEnabled(pref_service_));
 }
 
-TEST_F(SupervisedUserPreferencesTest, IsSafeSitesEnabledNonSupervisedUser) {
-  // Requests safe sites check without parental controls.
+TEST_F(SupervisedUserPreferencesTest,
+       IsSafeSitesEnabledIndependentlyFromSupervision) {
+  // Default behavior.
+  ASSERT_FALSE(IsSubjectToParentalControls(pref_service_));
+  ASSERT_FALSE(IsSafeSitesEnabled(pref_service_));
+
   pref_service_.SetSupervisedUserPref(prefs::kSupervisedUserSafeSites,
                                       base::Value(true));
-  pref_service_.SetString(prefs::kSupervisedUserId, std::string());
-
-  EXPECT_FALSE(supervised_user::IsSafeSitesEnabled(pref_service_));
-}
-
-TEST_F(SupervisedUserPreferencesTest, IsSafeSitesDisabled) {
-  // Sanity check for disabled safe sites (also gated by kSupervisedUserId).
-  pref_service_.SetSupervisedUserPref(prefs::kSupervisedUserSafeSites,
-                                      base::Value(false));
-  EXPECT_FALSE(supervised_user::IsSafeSitesEnabled(pref_service_));
+  ASSERT_FALSE(IsSubjectToParentalControls(pref_service_));
+  EXPECT_TRUE(IsSafeSitesEnabled(pref_service_));
 }
 
 TEST_F(SupervisedUserPreferencesTest,
        IsSubjectToParentalControlsForSupervisedUser) {
   // Simply enables parental controls.
-  pref_service_.SetString(prefs::kSupervisedUserId,
-                          supervised_user::kChildAccountSUID);
+  EnableParentalControls(pref_service_);
   EXPECT_TRUE(supervised_user::IsSubjectToParentalControls(pref_service_));
 
   // Safe sites is enabled by default.
@@ -155,7 +149,7 @@
        IsSubjectToParentalControlsForNonSupervisedUser) {
   // Set non-supervised user preference.
   pref_service_.SetString(prefs::kSupervisedUserId, std::string());
-  EXPECT_FALSE(supervised_user::IsSubjectToParentalControls(pref_service_));
+  EXPECT_FALSE(IsSubjectToParentalControls(pref_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 0a829a72..b4c2a17 100644
--- a/components/supervised_user/core/browser/supervised_user_service_unittest.cc
+++ b/components/supervised_user/core/browser/supervised_user_service_unittest.cc
@@ -28,6 +28,7 @@
 #include "components/signin/public/identity_manager/identity_test_environment.h"
 #include "components/supervised_user/core/browser/supervised_user_preferences.h"
 #include "components/supervised_user/core/browser/supervised_user_settings_service.h"
+#include "components/supervised_user/core/browser/supervised_user_sync_data_fake.h"
 #include "components/supervised_user/core/browser/supervised_user_utils.h"
 #include "components/supervised_user/core/common/features.h"
 #include "components/supervised_user/core/common/pref_names.h"
@@ -41,19 +42,19 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace supervised_user {
-
 namespace {
 
 const char kExampleUrl0[] = "http://www.example0.com";
 const char kExampleUrl1[] = "http://www.example1.com/123";
 
-}  // namespace
 
 class SupervisedUserServiceTestBase : public ::testing::Test {
  public:
   explicit SupervisedUserServiceTestBase(bool is_supervised) {
     settings_service_.Init(syncable_pref_service_.user_prefs_store());
     supervised_user::RegisterProfilePrefs(syncable_pref_service_.registry());
+    supervised_user_sync_data_fake_.Init(syncable_pref_service_);
+
     if (is_supervised) {
       syncable_pref_service_.SetString(prefs::kSupervisedUserId,
                                        kChildAccountSUID);
@@ -84,6 +85,7 @@
 
   syncer::MockSyncService sync_service_;
   sync_preferences::TestingPrefServiceSyncable syncable_pref_service_;
+  SupervisedUserSyncDataFake supervised_user_sync_data_fake_;
   SupervisedUserSettingsService settings_service_;
 
   std::unique_ptr<SupervisedUserService> service_;
@@ -134,7 +136,8 @@
   syncable_pref_service_.SetSupervisedUserPref(prefs::kSupervisedUserSafeSites,
                                                base::Value(true));
 
-  // This should not increase since only changes from the default are recorded.
+  // This should not increase since setting user pref `kSupervisedUserSafeSites`
+  // true won't take precedence over SupervisedUserPrefStore.
   histogram_tester.ExpectUniqueSample(
       SupervisedUserURLFilter::GetWebFilterTypeHistogramNameForTest(),
       /*sample=*/
@@ -159,7 +162,6 @@
       /*sample=*/
       WebFilterType::kCertainSites,
       /*expected_count=*/1);
-
   histogram_tester.ExpectTotalCount(
       SupervisedUserURLFilter::GetWebFilterTypeHistogramNameForTest(),
       /*expected_count=*/2);
@@ -286,4 +288,5 @@
       /* SupervisedUserURLFilter::WARN */ base::Value(1)));
 }
 
+}  // namespace
 }  // namespace supervised_user
diff --git a/components/supervised_user/core/browser/supervised_user_sync_data_fake.h b/components/supervised_user/core/browser/supervised_user_sync_data_fake.h
new file mode 100644
index 0000000..e0c68694
--- /dev/null
+++ b/components/supervised_user/core/browser/supervised_user_sync_data_fake.h
@@ -0,0 +1,67 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SUPERVISED_USER_CORE_BROWSER_SUPERVISED_USER_SYNC_DATA_FAKE_H_
+#define COMPONENTS_SUPERVISED_USER_CORE_BROWSER_SUPERVISED_USER_SYNC_DATA_FAKE_H_
+
+#include "base/test/bind.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/supervised_user/core/browser/supervised_user_pref_store.h"
+#include "components/supervised_user/core/common/pref_names.h"
+
+// Note: for supervised user features, the prod managed pref store is setting
+// some initial values before consuming actual settings. These could be called
+// "pref store local defaults", but should not be confused with the default pref
+// store, which is the lowest one in hierarchy of pref stores.
+// This compilation unit provides a dummy that set these "local defaults", but
+// is not necessarily implementing the entirety of features.
+// https://www.chromium.org/developers/design-documents/preferences/#prefstores-and-precedences
+namespace supervised_user {
+
+// This class fakes the interaction between SupervisedUserService,
+// SupervisedUserSettingsService and SupervisedUserPrefStore that is enabling or
+// disabling supervised user preferences as if they were loaded from the family
+// link Chrome sync data service. Use in unit tests:
+//
+// class MyTest : public ::testing::Test {
+//  void SetUp() override {
+//    supervised_user::RegisterProfilePrefs(pref_service_.registry());
+//    fake_.Init(pref_service_);
+//  }
+//  ...
+//   TestingPrefServiceSimple pref_service_;
+//   SupervisedUserSyncDataFake fake_;
+// }
+class SupervisedUserSyncDataFake {
+ public:
+  // Must be initialized after pref_service_ registers prefs. Supports any
+  // flavor of testing pref service that can alter managed user pref store (has
+  // SetSupervisedUserPref interface). `pref_service_` must outlive
+  // `SupervisedUserSyncDataFake` instances.
+  template <typename TestingPrefService>
+  void Init(TestingPrefService& pref_service_) {
+    registrar_.Init(&pref_service_);
+    registrar_.Add(prefs::kSupervisedUserId,
+                   base::BindLambdaForTesting([&pref_service_]() {
+                     PrefValueMap value_map;
+                     SetSupervisedUserPrefStoreDefaults(value_map);
+
+                     if (IsSubjectToParentalControls(pref_service_)) {
+                       for (const auto& [k, v] : value_map) {
+                         pref_service_.SetSupervisedUserPref(k, v.Clone());
+                       }
+                     } else {
+                       for (const auto& [k, v] : value_map) {
+                         pref_service_.RemoveSupervisedUserPref(k);
+                       }
+                     }
+                   }));
+  }
+
+ private:
+  PrefChangeRegistrar registrar_;
+};
+}  // namespace supervised_user
+
+#endif  // COMPONENTS_SUPERVISED_USER_CORE_BROWSER_SUPERVISED_USER_SYNC_DATA_FAKE_H_
diff --git a/components/supervised_user/core/browser/supervised_user_url_filter_unittest.cc b/components/supervised_user/core/browser/supervised_user_url_filter_unittest.cc
index 638f3b52..3392905 100644
--- a/components/supervised_user/core/browser/supervised_user_url_filter_unittest.cc
+++ b/components/supervised_user/core/browser/supervised_user_url_filter_unittest.cc
@@ -17,6 +17,7 @@
 #include "components/prefs/testing_pref_service.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_sync_data_fake.h"
 #include "components/supervised_user/core/browser/supervised_user_utils.h"
 #include "components/supervised_user/core/common/pref_names.h"
 #include "components/supervised_user/test_support/supervised_user_url_filter_test_utils.h"
@@ -34,6 +35,7 @@
   SupervisedUserURLFilterTest() {
     PrefRegistrySimple* registry = pref_service_.registry();
     RegisterProfilePrefs(registry);
+    supervised_user_sync_data_fake_.Init(pref_service_);
     filter_.SetURLCheckerClient(
         std::make_unique<safe_search_api::FakeURLCheckerClient>());
     filter_.SetDefaultFilteringBehavior(FilteringBehavior::kBlock);
@@ -75,6 +77,9 @@
 
   base::test::TaskEnvironment task_environment_;
   TestingPrefServiceSimple pref_service_;
+  // This makes pref service behave as if SupervisedUserSettingsService and
+  // SupervisedUserPrefStore were in action.
+  SupervisedUserSyncDataFake supervised_user_sync_data_fake_;
   SupervisedUserURLFilter filter_ =
       SupervisedUserURLFilter(pref_service_,
                               std::make_unique<FakeURLFilterDelegate>());
@@ -519,6 +524,8 @@
   SupervisedUserURLFilteringWithConflictsTest() {
     PrefRegistrySimple* registry = pref_service_.registry();
     RegisterProfilePrefs(registry);
+    supervised_user_sync_data_fake_.Init(pref_service_);
+
     filter_.SetURLCheckerClient(
         std::make_unique<safe_search_api::FakeURLCheckerClient>());
     filter_.SetDefaultFilteringBehavior(FilteringBehavior::kBlock);
@@ -533,6 +540,9 @@
 
   base::test::TaskEnvironment task_environment_;
   TestingPrefServiceSimple pref_service_;
+  // This makes pref service behave as if SupervisedUserSettingsService and
+  // SupervisedUserPrefStore were in action.
+  SupervisedUserSyncDataFake supervised_user_sync_data_fake_;
   SupervisedUserURLFilter filter_ =
       SupervisedUserURLFilter(pref_service_,
                               std::make_unique<FakeURLFilterDelegate>());
@@ -705,13 +715,18 @@
  protected:
   void EnableSafeSites() {
     RegisterProfilePrefs(pref_service_.registry());
-    pref_service_.SetString(prefs::kSupervisedUserId, kChildAccountSUID);
-    // No need to explicitly enable kSupervisedUserSafeSites, that's the default
-    // setting.
+    supervised_user_sync_data_fake_.Init(pref_service_);
+
+    // This call enables parental controls, and default settings of parental
+    // controls is safe sites on.
+    EnableParentalControls(pref_service_);
   }
 
   base::HistogramTester histogram_tester_;
   TestingPrefServiceSimple pref_service_;
+  // This makes pref service behave as if SupervisedUserSettingsService and
+  // SupervisedUserPrefStore were in action.
+  SupervisedUserSyncDataFake supervised_user_sync_data_fake_;
   SupervisedUserURLFilter filter_{pref_service_,
                                   std::make_unique<FakeURLFilterDelegate>()};
 };
diff --git a/components/supervised_user/core/common/features.cc b/components/supervised_user/core/common/features.cc
index 5f288d4..e4943b0 100644
--- a/components/supervised_user/core/common/features.cc
+++ b/components/supervised_user/core/common/features.cc
@@ -141,4 +141,11 @@
 #endif
 );
 
+BASE_FEATURE(kAlignSafeSitesValueWithBrowserDefault,
+             "AlignSafeSitesValueWithBrowserDefault",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kDecoupleSafeSitesFromMainSwitch,
+             "DecoupleSafeSitesFromMainSwitch",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 }  // namespace supervised_user
diff --git a/components/supervised_user/core/common/features.h b/components/supervised_user/core/common/features.h
index b57c6fa..71472f7 100644
--- a/components/supervised_user/core/common/features.h
+++ b/components/supervised_user/core/common/features.h
@@ -68,6 +68,14 @@
 // ClassifyUrl fetches.
 BASE_DECLARE_FEATURE(kWaitUntilAccessTokenAvailableForClassifyUrl);
 
+// Manages kSupervisedUserSafeSites exclusively within managed user pref store,
+// while keeping the default value neutral.
+BASE_DECLARE_FEATURE(kAlignSafeSitesValueWithBrowserDefault);
+
+// Allows reading SafeSites setting without extra supervised user guard. Can be
+// enabled iff kAlignSafeSitesValueWithBrowserDefault is also enabled.
+BASE_DECLARE_FEATURE(kDecoupleSafeSitesFromMainSwitch);
+
 // Returns whether the V3 version of the URL filter interstitial is
 // enabled.
 bool IsBlockInterstitialV3Enabled();
diff --git a/components/sync/base/features.cc b/components/sync/base/features.cc
index e7a964b4..a56547e 100644
--- a/components/sync/base/features.cc
+++ b/components/sync/base/features.cc
@@ -53,7 +53,12 @@
 
 BASE_FEATURE(kSeparateLocalAndAccountSearchEngines,
              "SeparateLocalAndAccountSearchEngines",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+#if BUILDFLAG(IS_CHROMEOS)
+             base::FEATURE_DISABLED_BY_DEFAULT
+#else
+             base::FEATURE_ENABLED_BY_DEFAULT
+#endif  // BUILDFLAG(IS_CHROMEOS)
+);
 
 BASE_FEATURE(kReplaceSyncPromosWithSignInPromos,
              "ReplaceSyncPromosWithSignInPromos",
@@ -119,11 +124,16 @@
 
 BASE_FEATURE(kSeparateLocalAndAccountThemes,
              "SeparateLocalAndAccountThemes",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+#if BUILDFLAG(IS_CHROMEOS)
+             base::FEATURE_DISABLED_BY_DEFAULT
+#else
+             base::FEATURE_ENABLED_BY_DEFAULT
+#endif  // BUILDFLAG(IS_CHROMEOS)
+);
 
 BASE_FEATURE(kThemesBatchUpload,
              "ThemesBatchUpload",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kSyncIncreaseNudgeDelayForSingleClient,
              "SyncIncreaseNudgeDelayForSingleClient",
diff --git a/components/sync/service/sync_service_utils.h b/components/sync/service/sync_service_utils.h
index 9881e68..382bdae5 100644
--- a/components/sync/service/sync_service_utils.h
+++ b/components/sync/service/sync_service_utils.h
@@ -58,7 +58,9 @@
   kPasswordManagerErrorMessage = 4,
   // Used on iOS only, from the account menu.
   kAccountMenu = 5,
-  kMaxValue = kAccountMenu
+  // From the Password Manager Settings (currently used only on iOS).
+  kPasswordManagerSettings = 6,
+  kMaxValue = kPasswordManagerSettings
 };
 // LINT.ThenChange(/tools/metrics/histograms/metadata/sync/enums.xml:TrustedVaultUserActionTrigger)
 
diff --git a/components/tabs/BUILD.gn b/components/tabs/BUILD.gn
index b04ad5b..36ac7829 100644
--- a/components/tabs/BUILD.gn
+++ b/components/tabs/BUILD.gn
@@ -4,6 +4,7 @@
 
 source_set("public") {
   sources = [
+    "public/pinned_tab_collection.h",
     "public/split_tab_id.h",
     "public/supports_handles.h",
     "public/tab_collection.h",
@@ -23,6 +24,7 @@
 
 source_set("tabs") {
   sources = [
+    "pinned_tab_collection.cc",
     "tab_collection.cc",
     "tab_collection_storage.cc",
   ]
diff --git a/chrome/browser/ui/tabs/pinned_tab_collection.cc b/components/tabs/pinned_tab_collection.cc
similarity index 87%
rename from chrome/browser/ui/tabs/pinned_tab_collection.cc
rename to components/tabs/pinned_tab_collection.cc
index 1c8532b..b5a88e3 100644
--- a/chrome/browser/ui/tabs/pinned_tab_collection.cc
+++ b/components/tabs/pinned_tab_collection.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/tabs/pinned_tab_collection.h"
+#include "components/tabs/public/pinned_tab_collection.h"
 
 namespace tabs {
 
diff --git a/chrome/browser/ui/tabs/pinned_tab_collection.h b/components/tabs/public/pinned_tab_collection.h
similarity index 73%
rename from chrome/browser/ui/tabs/pinned_tab_collection.h
rename to components/tabs/public/pinned_tab_collection.h
index b4f7df0..533286a4 100644
--- a/chrome/browser/ui/tabs/pinned_tab_collection.h
+++ b/components/tabs/public/pinned_tab_collection.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_TABS_PINNED_TAB_COLLECTION_H_
-#define CHROME_BROWSER_UI_TABS_PINNED_TAB_COLLECTION_H_
+#ifndef COMPONENTS_TABS_PUBLIC_PINNED_TAB_COLLECTION_H_
+#define COMPONENTS_TABS_PUBLIC_PINNED_TAB_COLLECTION_H_
 
 #include "components/tabs/public/tab_collection.h"
 
@@ -19,4 +19,4 @@
 
 }  // namespace tabs
 
-#endif  // CHROME_BROWSER_UI_TABS_PINNED_TAB_COLLECTION_H_
+#endif  // COMPONENTS_TABS_PUBLIC_PINNED_TAB_COLLECTION_H_
diff --git a/components/trusted_vault/icloud_keychain_recovery_factor.cc b/components/trusted_vault/icloud_keychain_recovery_factor.cc
index bb6667a8..05e674b8 100644
--- a/components/trusted_vault/icloud_keychain_recovery_factor.cc
+++ b/components/trusted_vault/icloud_keychain_recovery_factor.cc
@@ -108,7 +108,7 @@
     return;
   }
 
-  ongoing_request_for_recovery_ =
+  ongoing_download_registration_state_request_for_recovery_ =
       connection->DownloadAuthenticationFactorsRegistrationState(
           *primary_account_,
           base::BindOnce(&ICloudKeychainRecoveryFactor::
@@ -117,7 +117,7 @@
                          base::Unretained(this), connection, std::move(cb),
                          std::move(local_icloud_keys)),
           base::NullCallback());
-  CHECK(ongoing_request_for_recovery_);
+  CHECK(ongoing_download_registration_state_request_for_recovery_);
 }
 
 void ICloudKeychainRecoveryFactor::OnRecoveryFactorStateDownloadedForRecovery(
@@ -128,8 +128,8 @@
   // This method should be called only as a result of
   // `ongoing_request_for_recovery_` completion/failure, verify this condition
   // and destroy `ongoing_request_for_recovery_` as it's not  needed anymore.
-  CHECK(ongoing_request_for_recovery_);
-  ongoing_request_for_recovery_ = nullptr;
+  CHECK(ongoing_download_registration_state_request_for_recovery_);
+  ongoing_download_registration_state_request_for_recovery_ = nullptr;
 
   if (result.state ==
       DownloadAuthenticationFactorsRegistrationStateResult::State::kError) {
@@ -149,6 +149,7 @@
 
     const std::vector<uint8_t> public_key =
         recovery_icloud_key.public_key->ExportToBytes();
+
     const auto local_icloud_key_it = std::ranges::find_if(
         local_icloud_keys,
         [&public_key](const auto& key) { return key->id() == public_key; });
@@ -173,6 +174,7 @@
           std::ranges::max_element(recovery_icloud_key.member_keys, {},
                                    &MemberKeys::version)
               ->version;
+      MarkAsRegistered();
       std::move(cb).Run(RecoveryStatus::kSuccess, *new_vault_keys,
                         last_vault_key_version);
       return;
@@ -201,25 +203,229 @@
 }
 
 bool ICloudKeychainRecoveryFactor::IsRegistered() {
-  NOTIMPLEMENTED();
-  return false;
+  auto* per_user_vault = GetPrimaryAccountVault();
+  return per_user_vault->icloud_keychain_registration_info().registered();
 }
 
 void ICloudKeychainRecoveryFactor::MarkAsNotRegistered() {
-  NOTIMPLEMENTED();
+  auto* per_user_vault = GetPrimaryAccountVault();
+  per_user_vault->mutable_icloud_keychain_registration_info()->set_registered(
+      false);
+  storage_->WriteDataToDisk();
+}
+
+void ICloudKeychainRecoveryFactor::MarkAsRegistered() {
+  auto* per_user_vault = GetPrimaryAccountVault();
+  per_user_vault->mutable_icloud_keychain_registration_info()->set_registered(
+      true);
+  storage_->WriteDataToDisk();
 }
 
 void ICloudKeychainRecoveryFactor::ClearRegistrationAttemptInfo(
     const GaiaId& gaia_id) {
-  NOTIMPLEMENTED();
+  // TODO(crbug.com/409488087): Registration attempt information is currently
+  // shared with PhysicalDeviceRecoveryFactor, so there's nothing to do here.
 }
 
 TrustedVaultRecoveryFactorRegistrationStateForUMA
 ICloudKeychainRecoveryFactor::MaybeRegister(
     TrustedVaultThrottlingConnection* connection,
     RegisterCallback cb) {
-  NOTIMPLEMENTED();
-  return TrustedVaultRecoveryFactorRegistrationStateForUMA::kLocalKeysAreStale;
+  auto* per_user_vault = GetPrimaryAccountVault();
+
+  if (per_user_vault->icloud_keychain_registration_info().registered()) {
+    return TrustedVaultRecoveryFactorRegistrationStateForUMA::
+        kAlreadyRegisteredV1;
+  }
+
+  if (per_user_vault->local_device_registration_info()
+          .last_registration_returned_local_data_obsolete()) {
+    // Client already knows that existing vault keys (or their absence) isn't
+    // sufficient for registration. Fresh keys should be obtained first.
+    return TrustedVaultRecoveryFactorRegistrationStateForUMA::
+        kLocalKeysAreStale;
+  }
+
+  if (connection->AreRequestsThrottled(*primary_account_)) {
+    return TrustedVaultRecoveryFactorRegistrationStateForUMA::
+        kThrottledClientSide;
+  }
+
+  if (!StandaloneTrustedVaultStorage::HasNonConstantKey(*per_user_vault)) {
+    // Registration without non-constant keys isn't supported for iCloud
+    // Keychain.
+    return TrustedVaultRecoveryFactorRegistrationStateForUMA::
+        kRegistrationWithConstantKeyNotSupported;
+  }
+
+  // ICloudRecoveryKey::Retrieve() can't be cancelled, so we use a weak pointer
+  // for the callback.
+  ICloudRecoveryKey::Retrieve(
+      base::BindOnce(
+          &ICloudKeychainRecoveryFactor::OnICloudKeysRetrievedForRegistration,
+          weak_ptr_factory_.GetWeakPtr(), connection, std::move(cb)),
+      security_domain_id_, icloud_keychain_access_group_);
+
+  // We don't know yet whether there's an existing key pair in iCloud Keychain.
+  // However, if there is one that's not yet registered with the security
+  // domain, then we have to create a new key pair anyways. Thus, returning
+  // `kAttemptingRegistrationWithNewKeyPair` is the most appropriate status
+  // here.
+  return TrustedVaultRecoveryFactorRegistrationStateForUMA::
+      kAttemptingRegistrationWithNewKeyPair;
+}
+
+void ICloudKeychainRecoveryFactor::OnICloudKeysRetrievedForRegistration(
+    TrustedVaultThrottlingConnection* connection,
+    RegisterCallback cb,
+    std::vector<std::unique_ptr<ICloudRecoveryKey>> local_icloud_keys) {
+  CHECK(primary_account_);
+
+  if (local_icloud_keys.empty()) {
+    // No local iCloud Keychain key. We need to create a new one and register
+    // it.
+    ICloudRecoveryKey::Create(
+        base::BindOnce(
+            &ICloudKeychainRecoveryFactor::OnICloudKeyCreatedForRegistration,
+            weak_ptr_factory_.GetWeakPtr(), connection, std::move(cb)),
+        security_domain_id_, icloud_keychain_access_group_);
+    return;
+  }
+
+  ongoing_download_registration_state_request_for_registration_ =
+      connection->DownloadAuthenticationFactorsRegistrationState(
+          *primary_account_,
+          base::BindOnce(&ICloudKeychainRecoveryFactor::
+                             OnRecoveryFactorStateDownloadedForRegistration,
+                         // `this` outlives `ongoing_request_for_registration_`.
+                         base::Unretained(this), connection, std::move(cb),
+                         std::move(local_icloud_keys)),
+          base::NullCallback());
+  CHECK(ongoing_download_registration_state_request_for_registration_);
+}
+
+void ICloudKeychainRecoveryFactor::
+    OnRecoveryFactorStateDownloadedForRegistration(
+        TrustedVaultThrottlingConnection* connection,
+        RegisterCallback cb,
+        std::vector<std::unique_ptr<ICloudRecoveryKey>> local_icloud_keys,
+        DownloadAuthenticationFactorsRegistrationStateResult result) {
+  // This method should be called only as a result of
+  // `ongoing_request_for_registration_` completion/failure, verify this
+  // condition and destroy `ongoing_request_for_registration_` as it's not
+  // needed anymore.
+  CHECK(ongoing_download_registration_state_request_for_registration_);
+  ongoing_download_registration_state_request_for_registration_ = nullptr;
+
+  if (result.state ==
+      DownloadAuthenticationFactorsRegistrationStateResult::State::kError) {
+    connection->RecordFailedRequestForThrottling(*primary_account_);
+    FulfillRegistrationWithFailure(
+        TrustedVaultRegistrationStatus::kNetworkError, std::move(cb));
+    return;
+  }
+
+  for (const VaultMember& recovery_icloud_key : result.icloud_keys) {
+    std::vector<uint8_t> public_key =
+        recovery_icloud_key.public_key->ExportToBytes();
+    const auto local_icloud_key_it = std::ranges::find_if(
+        local_icloud_keys,
+        [&public_key](const auto& key) { return key->id() == public_key; });
+    if (local_icloud_key_it != local_icloud_keys.end()) {
+      MarkAsRegistered();
+      int last_vault_key_version =
+          std::ranges::max_element(recovery_icloud_key.member_keys, {},
+                                   &MemberKeys::version)
+              ->version;
+      base::BindPostTaskToCurrentDefault(
+          base::BindOnce(std::move(cb),
+                         TrustedVaultRegistrationStatus::kAlreadyRegistered,
+                         last_vault_key_version, /*had_local_keys=*/true))
+          .Run();
+      return;
+    }
+  }
+
+  // None of the retrieved iCloud Keychain keys is in the security domain. We
+  // need to create a new one and register it.
+  ICloudRecoveryKey::Create(
+      base::BindOnce(
+          &ICloudKeychainRecoveryFactor::OnICloudKeyCreatedForRegistration,
+          weak_ptr_factory_.GetWeakPtr(), connection, std::move(cb)),
+      security_domain_id_, icloud_keychain_access_group_);
+}
+
+void ICloudKeychainRecoveryFactor::OnICloudKeyCreatedForRegistration(
+    TrustedVaultThrottlingConnection* connection,
+    RegisterCallback cb,
+    std::unique_ptr<ICloudRecoveryKey> local_icloud_key) {
+  if (!local_icloud_key) {
+    FulfillRegistrationWithFailure(TrustedVaultRegistrationStatus::kOtherError,
+                                   std::move(cb));
+    return;
+  }
+
+  auto* per_user_vault = GetPrimaryAccountVault();
+
+  ongoing_registration_request_ = connection->RegisterAuthenticationFactor(
+      *primary_account_,
+      GetTrustedVaultKeysWithVersions(
+          StandaloneTrustedVaultStorage::GetAllVaultKeys(*per_user_vault),
+          per_user_vault->last_vault_key_version()),
+      local_icloud_key->key()->public_key(), ICloudKeychain(),
+      base::BindOnce(&ICloudKeychainRecoveryFactor::OnRegistered,
+                     base::Unretained(this), std::move(cb)));
+  CHECK(ongoing_registration_request_);
+}
+
+void ICloudKeychainRecoveryFactor::OnRegistered(
+    RegisterCallback cb,
+    TrustedVaultRegistrationStatus status,
+    int key_version) {
+  // This method should be called only as a result of
+  // `ongoing_registration_request_` completion/failure, verify this
+  // condition and destroy `ongoing_registration_request_` as it's not
+  // needed anymore.
+  CHECK(ongoing_registration_request_);
+  ongoing_registration_request_ = nullptr;
+
+  auto* per_user_vault = GetPrimaryAccountVault();
+  switch (status) {
+    case TrustedVaultRegistrationStatus::kSuccess:
+    case TrustedVaultRegistrationStatus::kAlreadyRegistered:
+      // kAlreadyRegistered handled as success, because it only means that
+      // client doesn't fully handled successful device registration before.
+      per_user_vault->mutable_icloud_keychain_registration_info()
+          ->set_registered(true);
+      per_user_vault->mutable_local_device_registration_info()
+          ->clear_last_registration_returned_local_data_obsolete();
+      storage_->WriteDataToDisk();
+      break;
+    case TrustedVaultRegistrationStatus::kLocalDataObsolete:
+      per_user_vault->mutable_local_device_registration_info()
+          ->set_last_registration_returned_local_data_obsolete(true);
+      storage_->WriteDataToDisk();
+      break;
+    case TrustedVaultRegistrationStatus::kTransientAccessTokenFetchError:
+    case TrustedVaultRegistrationStatus::kPersistentAccessTokenFetchError:
+    case TrustedVaultRegistrationStatus::
+        kPrimaryAccountChangeAccessTokenFetchError:
+    case TrustedVaultRegistrationStatus::kNetworkError:
+    case TrustedVaultRegistrationStatus::kOtherError:
+      break;
+  }
+
+  std::move(cb).Run(status,
+                    /*key_version=*/key_version,
+                    /*had_local_keys=*/true);
+}
+
+void ICloudKeychainRecoveryFactor::FulfillRegistrationWithFailure(
+    TrustedVaultRegistrationStatus status,
+    RegisterCallback cb) {
+  std::move(cb).Run(status,
+                    /*key_version=*/0,
+                    /*had_local_keys=*/true);
 }
 
 trusted_vault_pb::LocalTrustedVaultPerUser*
diff --git a/components/trusted_vault/icloud_keychain_recovery_factor.h b/components/trusted_vault/icloud_keychain_recovery_factor.h
index 349deba2..9ba0a8e 100644
--- a/components/trusted_vault/icloud_keychain_recovery_factor.h
+++ b/components/trusted_vault/icloud_keychain_recovery_factor.h
@@ -67,6 +67,27 @@
       TrustedVaultDownloadKeysStatusForUMA status_for_uma,
       AttemptRecoveryCallback cb);
 
+  void MarkAsRegistered();
+
+  void OnICloudKeysRetrievedForRegistration(
+      TrustedVaultThrottlingConnection* connection,
+      RegisterCallback cb,
+      std::vector<std::unique_ptr<ICloudRecoveryKey>> local_icloud_keys);
+  void OnRecoveryFactorStateDownloadedForRegistration(
+      TrustedVaultThrottlingConnection* connection,
+      RegisterCallback cb,
+      std::vector<std::unique_ptr<ICloudRecoveryKey>> local_icloud_keys,
+      DownloadAuthenticationFactorsRegistrationStateResult result);
+  void OnICloudKeyCreatedForRegistration(
+      TrustedVaultThrottlingConnection* connection,
+      RegisterCallback cb,
+      std::unique_ptr<ICloudRecoveryKey> local_icloud_key);
+  void OnRegistered(RegisterCallback cb,
+                    TrustedVaultRegistrationStatus status,
+                    int key_version);
+  void FulfillRegistrationWithFailure(TrustedVaultRegistrationStatus status,
+                                      RegisterCallback cb);
+
   const std::string icloud_keychain_access_group_;
   const SecurityDomainId security_domain_id_;
   const raw_ptr<StandaloneTrustedVaultStorage> storage_;
@@ -74,7 +95,13 @@
 
   // Destroying this will cancel the ongoing request.
   std::unique_ptr<TrustedVaultConnection::Request>
-      ongoing_request_for_recovery_;
+      ongoing_download_registration_state_request_for_recovery_;
+  // Destroying this will cancel the ongoing request.
+  std::unique_ptr<TrustedVaultConnection::Request>
+      ongoing_download_registration_state_request_for_registration_;
+  // Destroying this will cancel the ongoing request.
+  std::unique_ptr<TrustedVaultConnection::Request>
+      ongoing_registration_request_;
 
   base::WeakPtrFactory<ICloudKeychainRecoveryFactor> weak_ptr_factory_{this};
 };
diff --git a/components/trusted_vault/icloud_keychain_recovery_factor_unittest.mm b/components/trusted_vault/icloud_keychain_recovery_factor_unittest.mm
index de59a7f..17667975 100644
--- a/components/trusted_vault/icloud_keychain_recovery_factor_unittest.mm
+++ b/components/trusted_vault/icloud_keychain_recovery_factor_unittest.mm
@@ -17,10 +17,12 @@
 #include "base/test/task_environment.h"
 #include "components/trusted_vault/icloud_recovery_key_mac.h"
 #include "components/trusted_vault/local_recovery_factor.h"
+#include "components/trusted_vault/proto/local_trusted_vault.pb.h"
 #include "components/trusted_vault/proto_string_bytes_conversion.h"
 #include "components/trusted_vault/securebox.h"
 #include "components/trusted_vault/test/fake_file_access.h"
 #include "components/trusted_vault/test/mock_trusted_vault_throttling_connection.h"
+#include "components/trusted_vault/trusted_vault_connection.h"
 #include "components/trusted_vault/trusted_vault_crypto.h"
 #include "components/trusted_vault/trusted_vault_server_constants.h"
 #include "crypto/apple_keychain_v2.h"
@@ -32,8 +34,11 @@
 namespace trusted_vault {
 
 using testing::_;
+using testing::Eq;
 using testing::NiceMock;
+using testing::NotNull;
 using testing::Return;
+using testing::SizeIs;
 
 namespace {
 
@@ -48,6 +53,17 @@
     {4, 5, 6}};
 constexpr int kLastKeyVersion = 123;
 
+MATCHER_P(MatchTrustedVaultKeyAndVersions, expected, "") {
+  const auto* trusted_vault_keys =
+      std::get_if<std::vector<TrustedVaultKeyAndVersion>>(&arg);
+  if (!trusted_vault_keys) {
+    *result_listener << "does not hold a vector of TrustedVaultKeyAndVersion";
+    return false;
+  }
+  return testing::ExplainMatchResult(*trusted_vault_keys, expected,
+                                     result_listener);
+}
+
 DownloadAuthenticationFactorsRegistrationStateResult
 CreateDownloadAuthenticationFactorsRegistrationStateResult(
     DownloadAuthenticationFactorsRegistrationStateResult::State state,
@@ -91,6 +107,10 @@
   ~ICloudKeychainRecoveryFactorTest() override = default;
 
   void ResetRecoveryFactor(const std::optional<CoreAccountInfo> account_info) {
+    // Destroy `recovery_factor_`, otherwise it would hold a reference to
+    // `storage_` which is destroyed before `recovery_factor_` below.
+    recovery_factor_ = nullptr;
+
     std::unique_ptr<FakeFileAccess> file_access =
         std::make_unique<FakeFileAccess>();
     if (file_access_) {
@@ -165,6 +185,21 @@
     return new_key;
   }
 
+  std::vector<std::unique_ptr<ICloudRecoveryKey>> RetrieveICloudKeys(
+      const trusted_vault::SecurityDomainId security_domain_id) {
+    std::vector<std::unique_ptr<ICloudRecoveryKey>> keys;
+    base::RunLoop run_loop;
+    ICloudRecoveryKey::Retrieve(
+        base::BindLambdaForTesting(
+            [&](std::vector<std::unique_ptr<ICloudRecoveryKey>> ret) {
+              keys = std::move(ret);
+              run_loop.Quit();
+            }),
+        security_domain_id, kKeychainAccessGroup);
+    run_loop.Run();
+    return keys;
+  }
+
   void AttemptRecoveryAndExpectDownloadRegistrationState(
       DownloadAuthenticationFactorsRegistrationStateResult&&
           download_registration_state_result,
@@ -210,6 +245,192 @@
     run_loop.Run();
   }
 
+  TrustedVaultConnection::DownloadAuthenticationFactorsRegistrationStateCallback
+  MaybeRegisterAndExpectDownloadRegistrationState(
+      LocalRecoveryFactor::RegisterCallback registration_callback) {
+    TrustedVaultConnection::
+        DownloadAuthenticationFactorsRegistrationStateCallback
+            download_state_callback;
+
+    // A dedicated run loop is required for fetching keys from the iCloud
+    // Keychain.
+    base::RunLoop fetch_icloud_key_run_loop;
+    EXPECT_CALL(*connection(), DownloadAuthenticationFactorsRegistrationState(
+                                   account_info(), _, _))
+        .WillOnce([&](const CoreAccountInfo& account_info,
+                      TrustedVaultConnection::
+                          DownloadAuthenticationFactorsRegistrationStateCallback
+                              callback,
+                      base::RepeatingClosure keep_alive_callback) {
+          download_state_callback = std::move(callback);
+          // Note: Quitting the iCloud Keychain run loop here is ok-ish,
+          // because DownloadAuthenticationFactorsRegistrationState is
+          // expected to be called after fetching iCloud Keychain keys.
+          fetch_icloud_key_run_loop.Quit();
+          return std::make_unique<TrustedVaultConnection::Request>();
+        });
+    TrustedVaultDeviceRegistrationStateForUMA status =
+        recovery_factor()->MaybeRegister(connection(),
+                                         std::move(registration_callback));
+    CHECK(status == TrustedVaultDeviceRegistrationStateForUMA::
+                        kAttemptingRegistrationWithNewKeyPair);
+    fetch_icloud_key_run_loop.Run();
+
+    CHECK(!download_state_callback.is_null());
+    return download_state_callback;
+  }
+
+  void MaybeRegisterAndExpectDownloadRegistrationState(
+      DownloadAuthenticationFactorsRegistrationStateResult&&
+          download_registration_state_result,
+      LocalRecoveryFactor::RegisterCallback registration_callback) {
+    base::RunLoop run_loop;
+    TrustedVaultConnection::
+        DownloadAuthenticationFactorsRegistrationStateCallback
+            download_state_callback =
+                MaybeRegisterAndExpectDownloadRegistrationState(
+                    std::move(registration_callback)
+                        .Then(run_loop.QuitClosure()));
+    std::move(download_state_callback)
+        .Run(std::move(download_registration_state_result));
+    run_loop.Run();
+  }
+
+  std::unique_ptr<SecureBoxPublicKey>
+  MaybeRegisterAndExpectDownloadRegistrationStateAndRegisterAuthenticationFactor(
+      std::vector<VaultMember>&& vault_members,
+      TrustedVaultRegistrationStatus registration_status,
+      int registration_key_version,
+      LocalRecoveryFactor::RegisterCallback registration_callback) {
+    base::RunLoop run_loop;
+    TrustedVaultConnection::
+        DownloadAuthenticationFactorsRegistrationStateCallback
+            download_state_callback =
+                MaybeRegisterAndExpectDownloadRegistrationState(
+                    std::move(registration_callback)
+                        .Then(run_loop.QuitClosure()));
+
+    TrustedVaultConnection::RegisterAuthenticationFactorCallback
+        register_authentication_factor_callback;
+    std::unique_ptr<SecureBoxPublicKey> registered_public_key;
+
+    {
+      // A dedicated run loop is required for creating the key in the iCloud
+      // Keychain.
+      base::RunLoop create_icloud_key_run_loop;
+      EXPECT_CALL(
+          *connection(),
+          RegisterAuthenticationFactor(
+              Eq(account_info()),
+              MatchTrustedVaultKeyAndVersions(
+                  GetTrustedVaultKeysWithVersions(kVaultKeys, kLastKeyVersion)),
+              _,
+              Eq(AuthenticationFactorTypeAndRegistrationParams(
+                  ICloudKeychain())),
+              _))
+          .WillOnce(
+              [&](const CoreAccountInfo&,
+                  const MemberKeysSource& member_keys_source,
+                  const SecureBoxPublicKey& public_key,
+                  AuthenticationFactorTypeAndRegistrationParams,
+                  TrustedVaultConnection::RegisterAuthenticationFactorCallback
+                      callback) {
+                register_authentication_factor_callback = std::move(callback);
+                registered_public_key = SecureBoxPublicKey::CreateByImport(
+                    public_key.ExportToBytes());
+                // Note: Quitting the iCloud Keychain run loop here is ok-ish,
+                // because RegisterAuthenticationFactor is called directly after
+                // creating the iCloud Keychain key.
+                create_icloud_key_run_loop.Quit();
+                return std::make_unique<TrustedVaultConnection::Request>();
+              });
+
+      std::move(download_state_callback)
+          .Run(CreateDownloadAuthenticationFactorsRegistrationStateResult(
+              DownloadAuthenticationFactorsRegistrationStateResult::State::
+                  kRecoverable,
+              std::move(vault_members)));
+
+      create_icloud_key_run_loop.Run();
+
+      CHECK(!register_authentication_factor_callback.is_null());
+    }
+
+    std::move(register_authentication_factor_callback)
+        .Run(registration_status, registration_key_version);
+    run_loop.Run();
+
+    return registered_public_key;
+  }
+
+  std::unique_ptr<SecureBoxPublicKey>
+  MaybeRegisterAndExpectRegisterAuthenticationFactor(
+      std::vector<VaultMember>&& vault_members,
+      TrustedVaultRegistrationStatus registration_status,
+      int registration_key_version,
+      LocalRecoveryFactor::RegisterCallback registration_callback) {
+    base::RunLoop run_loop;
+    TrustedVaultConnection::RegisterAuthenticationFactorCallback
+        register_authentication_factor_callback;
+    std::unique_ptr<SecureBoxPublicKey> registered_public_key;
+
+    {
+      // A dedicated run loop is required for fetching keys from the iCloud
+      // Keychain and creating a new key.
+      base::RunLoop fetch_and_create_icloud_key_run_loop;
+      EXPECT_CALL(
+          *connection(),
+          RegisterAuthenticationFactor(
+              Eq(account_info()),
+              MatchTrustedVaultKeyAndVersions(
+                  GetTrustedVaultKeysWithVersions(kVaultKeys, kLastKeyVersion)),
+              _,
+              Eq(AuthenticationFactorTypeAndRegistrationParams(
+                  ICloudKeychain())),
+              _))
+          .WillOnce(
+              [&](const CoreAccountInfo&,
+                  const MemberKeysSource& member_keys_source,
+                  const SecureBoxPublicKey& public_key,
+                  AuthenticationFactorTypeAndRegistrationParams,
+                  TrustedVaultConnection::RegisterAuthenticationFactorCallback
+                      callback) {
+                register_authentication_factor_callback = std::move(callback);
+                registered_public_key = SecureBoxPublicKey::CreateByImport(
+                    public_key.ExportToBytes());
+                // Note: Quitting the iCloud Keychain run loop here is ok-ish,
+                // because RegisterAuthenticationFactor is called directly after
+                // creating the iCloud Keychain key.
+                fetch_and_create_icloud_key_run_loop.Quit();
+                return std::make_unique<TrustedVaultConnection::Request>();
+              });
+
+      TrustedVaultDeviceRegistrationStateForUMA status =
+          recovery_factor()->MaybeRegister(
+              connection(),
+              std::move(registration_callback).Then(run_loop.QuitClosure()));
+      CHECK_EQ(status, TrustedVaultDeviceRegistrationStateForUMA::
+                           kAttemptingRegistrationWithNewKeyPair);
+      fetch_and_create_icloud_key_run_loop.Run();
+
+      CHECK(!register_authentication_factor_callback.is_null());
+    }
+
+    std::move(register_authentication_factor_callback)
+        .Run(registration_status, registration_key_version);
+    run_loop.Run();
+
+    return registered_public_key;
+  }
+
+  trusted_vault_pb::ICloudKeychainRegistrationInfo* GetICloudRegistrationInfo(
+      CoreAccountInfo account_info) {
+    trusted_vault_pb::LocalTrustedVaultPerUser* per_user_vault =
+        storage_->FindUserVault(account_info.gaia);
+    CHECK(per_user_vault);
+    return per_user_vault->mutable_icloud_keychain_registration_info();
+  }
+
  private:
   std::unique_ptr<StandaloneTrustedVaultStorage> storage_ = nullptr;
   raw_ptr<FakeFileAccess> file_access_ = nullptr;
@@ -303,7 +524,8 @@
       /*expected_bucket_count=*/1);
 }
 
-TEST_F(ICloudKeychainRecoveryFactorTest, ShouldFailWithNetworkError) {
+TEST_F(ICloudKeychainRecoveryFactorTest,
+       AttemptRecoveryShouldFailWithNetworkError) {
   CreateICloudKey(SecurityDomainId::kChromeSync);
 
   base::HistogramTester histogram_tester;
@@ -474,6 +696,207 @@
       /*expected_bucket_count=*/1);
 }
 
+TEST_F(ICloudKeychainRecoveryFactorTest,
+       ShouldNotRegisterWhenAlreadyRegistered) {
+  GetICloudRegistrationInfo(account_info())->set_registered(true);
+
+  base::MockCallback<LocalRecoveryFactor::RegisterCallback> register_callback;
+  EXPECT_CALL(register_callback, Run).Times(0);
+
+  TrustedVaultDeviceRegistrationStateForUMA status =
+      recovery_factor()->MaybeRegister(connection(), register_callback.Get());
+  EXPECT_THAT(
+      status,
+      Eq(TrustedVaultDeviceRegistrationStateForUMA::kAlreadyRegisteredV1));
+}
+
+TEST_F(ICloudKeychainRecoveryFactorTest,
+       ShouldNotRegisterWhenLocalDataObsolete) {
+  trusted_vault_pb::LocalTrustedVaultPerUser* per_user_vault =
+      storage()->FindUserVault(account_info().gaia);
+  ASSERT_THAT(per_user_vault, NotNull());
+  per_user_vault->mutable_local_device_registration_info()
+      ->set_last_registration_returned_local_data_obsolete(true);
+
+  base::MockCallback<LocalRecoveryFactor::RegisterCallback> register_callback;
+  EXPECT_CALL(register_callback, Run).Times(0);
+
+  TrustedVaultDeviceRegistrationStateForUMA status =
+      recovery_factor()->MaybeRegister(connection(), register_callback.Get());
+  EXPECT_THAT(
+      status,
+      Eq(TrustedVaultDeviceRegistrationStateForUMA::kLocalKeysAreStale));
+}
+
+TEST_F(ICloudKeychainRecoveryFactorTest, ShouldNotRegisterWhenThrottled) {
+  EXPECT_CALL(*connection(), AreRequestsThrottled).WillOnce(Return(true));
+
+  base::MockCallback<LocalRecoveryFactor::RegisterCallback> register_callback;
+  EXPECT_CALL(register_callback, Run).Times(0);
+
+  TrustedVaultDeviceRegistrationStateForUMA status =
+      recovery_factor()->MaybeRegister(connection(), register_callback.Get());
+  EXPECT_THAT(
+      status,
+      Eq(TrustedVaultDeviceRegistrationStateForUMA::kThrottledClientSide));
+}
+
+TEST_F(ICloudKeychainRecoveryFactorTest, ShouldNotRegisterWithoutKeys) {
+  base::MockCallback<LocalRecoveryFactor::RegisterCallback> register_callback;
+  EXPECT_CALL(register_callback, Run).Times(0);
+
+  TrustedVaultDeviceRegistrationStateForUMA status =
+      recovery_factor()->MaybeRegister(connection(), register_callback.Get());
+  EXPECT_THAT(status, Eq(TrustedVaultDeviceRegistrationStateForUMA::
+                             kRegistrationWithConstantKeyNotSupported));
+}
+
+TEST_F(ICloudKeychainRecoveryFactorTest, ShouldNotRegisterWithConstantKeys) {
+  StoreKeys(account_info(), {GetConstantTrustedVaultKey()}, kLastKeyVersion);
+
+  base::MockCallback<LocalRecoveryFactor::RegisterCallback> register_callback;
+  EXPECT_CALL(register_callback, Run).Times(0);
+
+  TrustedVaultDeviceRegistrationStateForUMA status =
+      recovery_factor()->MaybeRegister(connection(), register_callback.Get());
+  EXPECT_THAT(status, Eq(TrustedVaultDeviceRegistrationStateForUMA::
+                             kRegistrationWithConstantKeyNotSupported));
+}
+
+TEST_F(
+    ICloudKeychainRecoveryFactorTest,
+    RegistrationShouldFailWithNetworkErrorWhenDownloadRegistrationStateFails) {
+  StoreKeys(account_info(), kVaultKeys, kLastKeyVersion);
+  CreateICloudKey(SecurityDomainId::kChromeSync);
+
+  base::MockCallback<LocalRecoveryFactor::RegisterCallback>
+      registration_callback;
+  EXPECT_CALL(*connection(), RecordFailedRequestForThrottling);
+  EXPECT_CALL(registration_callback,
+              Run(TrustedVaultRegistrationStatus::kNetworkError, _, _));
+
+  // Mimic failed key downloading, it should record a failed request for
+  // throttling.
+  MaybeRegisterAndExpectDownloadRegistrationState(
+      CreateDownloadAuthenticationFactorsRegistrationStateResult(
+          DownloadAuthenticationFactorsRegistrationStateResult::State::kError,
+          std::vector<VaultMember>()),
+      registration_callback.Get());
+}
+
+TEST_F(ICloudKeychainRecoveryFactorTest,
+       RegistrationShouldDetectAlreadyRegisteredKey) {
+  StoreKeys(account_info(), kVaultKeys, kLastKeyVersion);
+  std::unique_ptr<ICloudRecoveryKey> icloud_key =
+      CreateICloudKey(SecurityDomainId::kChromeSync);
+
+  base::MockCallback<LocalRecoveryFactor::RegisterCallback>
+      registration_callback;
+  EXPECT_CALL(registration_callback,
+              Run(TrustedVaultRegistrationStatus::kAlreadyRegistered,
+                  kLastKeyVersion, _));
+
+  std::vector<VaultMember> vault_members;
+  vault_members.emplace_back(CreateVaultMember(icloud_key->key()->public_key(),
+                                               kVaultKeys, kLastKeyVersion));
+  MaybeRegisterAndExpectDownloadRegistrationState(
+      CreateDownloadAuthenticationFactorsRegistrationStateResult(
+          DownloadAuthenticationFactorsRegistrationStateResult::State::
+              kRecoverable,
+          std::move(vault_members)),
+      registration_callback.Get());
+
+  EXPECT_TRUE(recovery_factor()->IsRegistered());
+}
+
+TEST_F(ICloudKeychainRecoveryFactorTest,
+       RegistrationShouldHandleLocalDataObsolete) {
+  StoreKeys(account_info(), kVaultKeys, kLastKeyVersion);
+
+  base::MockCallback<LocalRecoveryFactor::RegisterCallback>
+      registration_callback;
+
+  EXPECT_CALL(registration_callback,
+              Run(TrustedVaultRegistrationStatus::kLocalDataObsolete, _, _));
+  MaybeRegisterAndExpectRegisterAuthenticationFactor(
+      std::vector<VaultMember>(),
+      TrustedVaultRegistrationStatus::kLocalDataObsolete,
+      /*registration_key_version=*/0, registration_callback.Get());
+
+  trusted_vault_pb::LocalTrustedVaultPerUser* per_user_vault =
+      storage()->FindUserVault(account_info().gaia);
+  ASSERT_THAT(per_user_vault, NotNull());
+  EXPECT_TRUE(per_user_vault->local_device_registration_info()
+                  .last_registration_returned_local_data_obsolete());
+}
+
+TEST_F(ICloudKeychainRecoveryFactorTest, RegistrationShouldSucceed) {
+  StoreKeys(account_info(), kVaultKeys, kLastKeyVersion);
+
+  base::MockCallback<LocalRecoveryFactor::RegisterCallback>
+      registration_callback;
+  EXPECT_CALL(
+      registration_callback,
+      Run(TrustedVaultRegistrationStatus::kSuccess, kLastKeyVersion, _));
+
+  std::unique_ptr<SecureBoxPublicKey> registered_public_key =
+      MaybeRegisterAndExpectRegisterAuthenticationFactor(
+          std::vector<VaultMember>(), TrustedVaultRegistrationStatus::kSuccess,
+          kLastKeyVersion, registration_callback.Get());
+
+  EXPECT_TRUE(recovery_factor()->IsRegistered());
+  std::vector<std::unique_ptr<ICloudRecoveryKey>> icloud_keys =
+      RetrieveICloudKeys(SecurityDomainId::kChromeSync);
+  ASSERT_THAT(icloud_keys, SizeIs(1));
+  EXPECT_EQ(icloud_keys[0]->key()->public_key().ExportToBytes(),
+            registered_public_key->ExportToBytes());
+}
+
+TEST_F(ICloudKeychainRecoveryFactorTest,
+       RegistrationShouldSucceedWithUnrelatedKeys) {
+  // Create some unrelated iCloud Keychain keys.
+  CreateICloudKey(SecurityDomainId::kChromeSync);
+  CreateICloudKey(SecurityDomainId::kPasskeys);
+  StoreKeys(account_info(), kVaultKeys, kLastKeyVersion);
+
+  base::MockCallback<LocalRecoveryFactor::RegisterCallback>
+      registration_callback;
+
+  EXPECT_CALL(
+      registration_callback,
+      Run(TrustedVaultRegistrationStatus::kSuccess, kLastKeyVersion, _));
+
+  std::vector<VaultMember> vault_members;
+  // Return an unrelated iCloud Keychain member.
+  vault_members.emplace_back(
+      CreateVaultMember(SecureBoxKeyPair::GenerateRandom()->public_key(),
+                        kVaultKeys, kLastKeyVersion));
+  MaybeRegisterAndExpectDownloadRegistrationStateAndRegisterAuthenticationFactor(
+      std::move(vault_members), TrustedVaultRegistrationStatus::kSuccess,
+      kLastKeyVersion, registration_callback.Get());
+
+  EXPECT_TRUE(recovery_factor()->IsRegistered());
+  std::vector<std::unique_ptr<ICloudRecoveryKey>> icloud_keys =
+      RetrieveICloudKeys(SecurityDomainId::kChromeSync);
+  // A new key should have been created, in addition to the existing one.
+  ASSERT_THAT(icloud_keys, SizeIs(2));
+}
+
+TEST_F(ICloudKeychainRecoveryFactorTest,
+       MarkAsNotRegisteredShouldClearRegistrationData) {
+  GetICloudRegistrationInfo(account_info())->set_registered(true);
+
+  EXPECT_TRUE(recovery_factor()->IsRegistered());
+
+  recovery_factor()->MarkAsNotRegistered();
+
+  // Now the device should no longer be registered.
+  EXPECT_FALSE(recovery_factor()->IsRegistered());
+  trusted_vault_pb::ICloudKeychainRegistrationInfo* registration_info =
+      GetICloudRegistrationInfo(account_info());
+  EXPECT_FALSE(registration_info->registered());
+}
+
 }  // namespace
 
 }  // namespace trusted_vault
diff --git a/components/trusted_vault/proto/local_trusted_vault.proto b/components/trusted_vault/proto/local_trusted_vault.proto
index 8b4599b..183669bc 100644
--- a/components/trusted_vault/proto/local_trusted_vault.proto
+++ b/components/trusted_vault/proto/local_trusted_vault.proto
@@ -48,9 +48,20 @@
   // the failure corresponds to a re-registration attempt. In this case, besides
   // key retrieval, it may be possible to resolve the error by attempting to
   // download new keys.
+  // TODO(crbug.com/409488087): Migrate this field to LocalTrustedVaultPerUser.
   optional bool last_registration_returned_local_data_obsolete = 4;
 }
 
+message ICloudKeychainRegistrationInfo {
+  // Indicates whether iCloud Keychain is registered, i.e. whether its public
+  // key is successfully submitted to the server.
+  // Note: The iCloud Keychain could have been registered successfully on
+  // another device already, which isn't reflected in this field initially.
+  // Attempting a registration will, however, detect that state an update this
+  // field.
+  optional bool registered = 1;
+}
+
 message LocalTrustedVaultDegradedRecoverabilityState {
   reserved 1;
 
@@ -82,6 +93,10 @@
   // Device key and corresponding registration metadata.
   optional LocalDeviceRegistrationInfo local_device_registration_info = 5;
 
+  // Registration metadata for iCloud Keychain member.
+  optional ICloudKeychainRegistrationInfo icloud_keychain_registration_info =
+      10;
+
   // The time (in milliseconds since UNIX epoch) at which last unsuccessful (due
   // to transient errors) request was sent to the vault service. Used for
   // throttling requests to the server.
diff --git a/components/trusted_vault/trusted_vault_histograms.h b/components/trusted_vault/trusted_vault_histograms.h
index 882de56..413573b 100644
--- a/components/trusted_vault/trusted_vault_histograms.h
+++ b/components/trusted_vault/trusted_vault_histograms.h
@@ -36,7 +36,8 @@
   // TrustedVaultRecoveryFactorRegistrationOutcomeForUMA.
   kDeprecatedAttemptingRegistrationWithPersistentAuthError = 5,
   kAlreadyRegisteredV1 = 6,
-  kMaxValue = kAlreadyRegisteredV1,
+  kRegistrationWithConstantKeyNotSupported = 7,
+  kMaxValue = kRegistrationWithConstantKeyNotSupported,
 };
 // TODO(crbug.com/369980730): this is used in internals, replace usages with the
 // version above and delete this alias.
diff --git a/components/url_pattern_index/ngram_extractor.h b/components/url_pattern_index/ngram_extractor.h
index 2780ab8..70bc73f 100644
--- a/components/url_pattern_index/ngram_extractor.h
+++ b/components/url_pattern_index/ngram_extractor.h
@@ -58,7 +58,6 @@
     }
 
     bool operator==(const Iterator& rhs) const { return head_ == rhs.head_; }
-    bool operator!=(const Iterator& rhs) const { return !operator==(rhs); }
 
     NGramType operator*() const { return ngram_; }
     NGramType* operator->() const { return &ngram_; }
diff --git a/components/url_pattern_index/string_splitter.h b/components/url_pattern_index/string_splitter.h
index 3f21fa6..ed2f590 100644
--- a/components/url_pattern_index/string_splitter.h
+++ b/components/url_pattern_index/string_splitter.h
@@ -43,8 +43,6 @@
       return current_.data() == rhs.current_.data();
     }
 
-    bool operator!=(const Iterator& rhs) const { return !operator==(rhs); }
-
     std::string_view operator*() const { return current_; }
     const std::string_view* operator->() const { return &current_; }
 
diff --git a/components/viz/common/hit_test/aggregated_hit_test_region.h b/components/viz/common/hit_test/aggregated_hit_test_region.h
index 0edade10..266f46b 100644
--- a/components/viz/common/hit_test/aggregated_hit_test_region.h
+++ b/components/viz/common/hit_test/aggregated_hit_test_region.h
@@ -45,6 +45,9 @@
               !!async_hit_test_reasons);
   }
 
+  friend bool operator==(const AggregatedHitTestRegion&,
+                         const AggregatedHitTestRegion&) = default;
+
   // The FrameSinkId corresponding to this region.  Events that match
   // are routed to this surface.
   FrameSinkId frame_sink_id;
@@ -64,17 +67,6 @@
   int32_t child_count = 0;
 
   gfx::Transform transform;
-
-  bool operator==(const AggregatedHitTestRegion& rhs) const {
-    return (frame_sink_id == rhs.frame_sink_id && flags == rhs.flags &&
-            async_hit_test_reasons == rhs.async_hit_test_reasons &&
-            rect == rhs.rect && child_count == rhs.child_count &&
-            transform == rhs.transform);
-  }
-
-  bool operator!=(const AggregatedHitTestRegion& other) const {
-    return !(*this == other);
-  }
 };
 
 }  // namespace viz
diff --git a/components/viz/common/quads/texture_draw_quad.cc b/components/viz/common/quads/texture_draw_quad.cc
index 7bbeca48..18f630c8 100644
--- a/components/viz/common/quads/texture_draw_quad.cc
+++ b/components/viz/common/quads/texture_draw_quad.cc
@@ -19,7 +19,7 @@
 
 TextureDrawQuad::TextureDrawQuad()
     : nearest_neighbor(false),
-      premultiplied_alpha(false),
+      premultiplied_alpha(true),
       secure_output_only(false),
       is_video_frame(false),
       is_stream_video(false),
diff --git a/components/viz/service/display/overlay_ca_unittest.cc b/components/viz/service/display/overlay_ca_unittest.cc
index a08354fe..64104156 100644
--- a/components/viz/service/display/overlay_ca_unittest.cc
+++ b/components/viz/service/display/overlay_ca_unittest.cc
@@ -123,7 +123,7 @@
     const gfx::Rect& rect,
     gfx::ProtectedVideoType protected_video_type) {
   bool needs_blending = false;
-  bool premultiplied_alpha = false;
+  bool premultiplied_alpha = true;
   bool nearest_neighbor = false;
   gfx::Size resource_size_in_pixels = rect.size();
   bool is_overlay_candidate = true;
@@ -359,7 +359,7 @@
     texture_video_quad->SetNew(pass->shared_quad_state_list.back(),
                                gfx::Rect(size), gfx::Rect(size),
                                /*needs_blending=*/false, resource_id,
-                               /*premultiplied_alpha=*/false, kUVTopLeft,
+                               /*premultiplied_alpha=*/true, kUVTopLeft,
                                kUVBottomRight, SkColors::kTransparent,
                                /*nearest_neighbor=*/false,
                                /*secure_output_only=*/false,
diff --git a/components/viz/service/display/surface_aggregator_perftest.cc b/components/viz/service/display/surface_aggregator_perftest.cc
index 32499c2..11abf0ff2 100644
--- a/components/viz/service/display/surface_aggregator_perftest.cc
+++ b/components/viz/service/display/surface_aggregator_perftest.cc
@@ -134,7 +134,7 @@
         gfx::Rect visible_rect =
             j % 2 == 0 ? gfx::Rect(0, 0, 1, 2) : gfx::Rect(0, 1, 1, 1);
         bool needs_blending = false;
-        bool premultiplied_alpha = false;
+        bool premultiplied_alpha = true;
         const gfx::PointF uv_top_left;
         const gfx::PointF uv_bottom_right;
         SkColor4f background_color = SkColors::kGreen;
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index c40fe82..e45a2cc 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -465,7 +465,7 @@
     const gfx::PointF kUVBottomRight(1.0f, 1.0f);
     quad->SetNew(shared_state, output_rect, output_rect,
                  false /*needs_blending*/, ResourceId(1),
-                 false /*premultiplied_alpha*/, kUVTopLeft, kUVBottomRight,
+                 true /*premultiplied_alpha*/, kUVTopLeft, kUVBottomRight,
                  SkColors::kTransparent, false /*nearest_neighbor*/,
                  false /*secure_output_only*/, gfx::ProtectedVideoType::kClear);
 
@@ -5839,7 +5839,7 @@
     const gfx::Rect rect;
     const gfx::Rect visible_rect;
     bool needs_blending = false;
-    bool premultiplied_alpha = false;
+    bool premultiplied_alpha = true;
     const gfx::PointF uv_top_left;
     const gfx::PointF uv_bottom_right;
     SkColor4f background_color = SkColors::kGreen;
@@ -7815,8 +7815,8 @@
     const gfx::PointF kUVBottomRight(1.0f, 1.0f);
     texure_quad->SetNew(
         sqs, quad_rects[i], quad_rects[i], false /*needs_blending*/,
-        ResourceId(1), false /*premultiplied_alpha*/, kUVTopLeft,
-        kUVBottomRight, SkColors::kTransparent, false /*nearest_neighbor*/,
+        ResourceId(1), true /*premultiplied_alpha*/, kUVTopLeft, kUVBottomRight,
+        SkColors::kTransparent, false /*nearest_neighbor*/,
         false /*secure_output_only*/, gfx::ProtectedVideoType::kClear);
 
     texure_quad->damage_rect = damage_rects[i];
diff --git a/components/webapps/browser/android/BUILD.gn b/components/webapps/browser/android/BUILD.gn
index 06dfdcc..915a015a 100644
--- a/components/webapps/browser/android/BUILD.gn
+++ b/components/webapps/browser/android/BUILD.gn
@@ -4,7 +4,6 @@
 
 import("//build/config/android/config.gni")
 import("//build/config/android/rules.gni")
-import("//build/config/locales.gni")
 import("//third_party/jni_zero/jni_zero.gni")
 import("//tools/grit/grit_rule.gni")
 
@@ -151,10 +150,6 @@
 
 java_strings_grd("webapps_strings_grd") {
   grd_file = "android_webapps_strings.grd"
-  outputs = [ "values/android_webapps_strings.xml" ] +
-            process_file_template(
-                android_bundle_locales_as_resources,
-                [ "values-{{source_name_part}}/android_webapps_strings.xml" ])
 }
 
 source_set("unit_tests") {
diff --git a/components/webauthn/android/internal_authenticator_android.cc b/components/webauthn/android/internal_authenticator_android.cc
index 445d884..fb23f08 100644
--- a/components/webauthn/android/internal_authenticator_android.cc
+++ b/components/webauthn/android/internal_authenticator_android.cc
@@ -231,18 +231,7 @@
 }
 
 JavaRef<jobject>& InternalAuthenticatorAndroid::GetJavaObject() {
-  if (java_internal_authenticator_ref_.is_null()) {
-    JNIEnv* env = AttachCurrentThread();
-    content::RenderFrameHost* render_frame_host = GetRenderFrameHost();
-    if (render_frame_host) {
-      java_internal_authenticator_ref_ = Java_InternalAuthenticator_create(
-          env, reinterpret_cast<intptr_t>(this),
-          render_frame_host->GetJavaRenderFrameHost());
-    } else {
-      java_internal_authenticator_ref_ = Java_InternalAuthenticator_create(
-          env, reinterpret_cast<intptr_t>(this));
-    }
-  }
+  CHECK(java_internal_authenticator_ref_);
   return java_internal_authenticator_ref_;
 }
 
diff --git a/content/browser/back_forward_cache_no_store_browsertest.cc b/content/browser/back_forward_cache_no_store_browsertest.cc
index 43b7b4f..9eec1c6e 100644
--- a/content/browser/back_forward_cache_no_store_browsertest.cc
+++ b/content/browser/back_forward_cache_no_store_browsertest.cc
@@ -1029,7 +1029,9 @@
       const GURL& url,
       const net::SiteForCookies& site_for_cookies,
       const url::Origin& top_frame_origin,
-      const net::CookieSettingOverrides overrides) override {
+      const net::CookieSettingOverrides overrides,
+      base::optional_ref<const net::CookiePartitionKey> cookie_partition_key)
+      override {
     return is_cookie_enabled_;
   }
 
diff --git a/content/browser/indexed_db/indexed_db_return_value.cc b/content/browser/indexed_db/indexed_db_return_value.cc
index ba9d830..9f95ac02 100644
--- a/content/browser/indexed_db/indexed_db_return_value.cc
+++ b/content/browser/indexed_db/indexed_db_return_value.cc
@@ -28,4 +28,8 @@
   return mojo_value;
 }
 
+IndexedDBReturnValue::IndexedDBReturnValue() = default;
+IndexedDBReturnValue::IndexedDBReturnValue(IndexedDBValue value)
+    : IndexedDBValue(std::move(value)) {}
+
 }  // namespace content::indexed_db
diff --git a/content/browser/indexed_db/indexed_db_return_value.h b/content/browser/indexed_db/indexed_db_return_value.h
index 440e37f..a70ff47 100644
--- a/content/browser/indexed_db/indexed_db_return_value.h
+++ b/content/browser/indexed_db/indexed_db_return_value.h
@@ -21,6 +21,9 @@
   static blink::mojom::IDBReturnValuePtr ConvertReturnValue(
       IndexedDBReturnValue* value);
 
+  IndexedDBReturnValue();
+  IndexedDBReturnValue(IndexedDBValue value);
+
   blink::IndexedDBKey
       primary_key;  // primary key (only when using key generator)
   blink::IndexedDBKeyPath key_path;
diff --git a/content/browser/indexed_db/indexed_db_value.cc b/content/browser/indexed_db/indexed_db_value.cc
index 9a6d962..f7854ef 100644
--- a/content/browser/indexed_db/indexed_db_value.cc
+++ b/content/browser/indexed_db/indexed_db_value.cc
@@ -22,6 +22,18 @@
 }
 
 IndexedDBValue::IndexedDBValue() = default;
+IndexedDBValue::~IndexedDBValue() = default;
+
+IndexedDBValue::IndexedDBValue(IndexedDBValue&& other) = default;
+IndexedDBValue& IndexedDBValue::operator=(IndexedDBValue&& other) = default;
+
+IndexedDBValue IndexedDBValue::Clone() const {
+  IndexedDBValue copy;
+  copy.bits = bits;
+  copy.external_objects = external_objects;
+  return copy;
+}
+
 IndexedDBValue::IndexedDBValue(
     const std::string& input_bits,
     const std::vector<IndexedDBExternalObject>& external_objects)
@@ -29,9 +41,5 @@
       external_objects(external_objects) {
   DCHECK(external_objects.empty() || input_bits.size());
 }
-IndexedDBValue::IndexedDBValue(const IndexedDBValue& other) = default;
-IndexedDBValue::~IndexedDBValue() = default;
-IndexedDBValue& IndexedDBValue::operator=(const IndexedDBValue& other) =
-    default;
 
 }  // namespace content::indexed_db
diff --git a/content/browser/indexed_db/indexed_db_value.h b/content/browser/indexed_db/indexed_db_value.h
index 1ff4f84..a3eb9e6 100644
--- a/content/browser/indexed_db/indexed_db_value.h
+++ b/content/browser/indexed_db/indexed_db_value.h
@@ -22,19 +22,23 @@
   static blink::mojom::IDBValuePtr ConvertAndEraseValue(IndexedDBValue* value);
 
   IndexedDBValue();
-  IndexedDBValue(const IndexedDBValue& other);
   ~IndexedDBValue();
-  IndexedDBValue& operator=(const IndexedDBValue& other);
 
-  // Only used for test.
+  // Move is allowed.
+  IndexedDBValue(IndexedDBValue&& other);
+  IndexedDBValue& operator=(IndexedDBValue&& other);
+
+  // Copy is a footgun.
+  IndexedDBValue(const IndexedDBValue& other) = delete;
+  IndexedDBValue& operator=(const IndexedDBValue& other) = delete;
+
+  // In rare cases, copy is acceptable.
+  IndexedDBValue Clone() const;
+
+  // Only used for tests.
   IndexedDBValue(const std::string& input_bits,
                  const std::vector<IndexedDBExternalObject>& external_objects);
 
-  void swap(IndexedDBValue& value) {
-    bits.swap(value.bits);
-    external_objects.swap(value.external_objects);
-  }
-
   bool empty() const { return bits.empty(); }
   void clear() {
     bits.clear();
diff --git a/content/browser/indexed_db/instance/cursor.cc b/content/browser/indexed_db/instance/cursor.cc
index 7272804..c3c1d67 100644
--- a/content/browser/indexed_db/instance/cursor.cc
+++ b/content/browser/indexed_db/instance/cursor.cc
@@ -311,10 +311,8 @@
         found_values.push_back(IndexedDBValue());
         break;
       case indexed_db::CursorType::kKeyAndValue: {
-        IndexedDBValue value;
-        value.swap(cursor_->GetValue());
-        size_estimate += value.SizeEstimate();
-        found_values.push_back(value);
+        found_values.push_back(std::move(cursor_->GetValue()));
+        size_estimate += found_values.back().SizeEstimate();
         break;
       }
       default:
diff --git a/content/browser/indexed_db/instance/database.cc b/content/browser/indexed_db/instance/database.cc
index f915130..4733fd1 100644
--- a/content/browser/indexed_db/instance/database.cc
+++ b/content/browser/indexed_db/instance/database.cc
@@ -80,8 +80,7 @@
     BucketContext& bucket_context,
     const IndexedDBObjectStoreMetadata& object_store_metadata,
     BackingStore::Cursor& cursor) {
-  IndexedDBReturnValue idb_return_value;
-  idb_return_value.swap(cursor.GetValue());
+  IndexedDBReturnValue idb_return_value(std::move(cursor.GetValue()));
 
   const bool is_generated_key =
       (!idb_return_value.empty() && object_store_metadata.auto_increment &&
@@ -106,49 +105,8 @@
   return blink::mojom::IDBError::New(code, base::UTF8ToUTF16(message));
 }
 
-std::unique_ptr<IndexedDBKey> GenerateKey(Transaction* transaction,
-                                          int64_t object_store_id) {
-  // Maximum integer uniquely representable as ECMAScript number.
-  const int64_t max_generator_value = 9007199254740992LL;
-  int64_t current_number;
-  Status s =
-      transaction->BackingStoreTransaction()->GetKeyGeneratorCurrentNumber(
-          object_store_id, &current_number);
-  if (!s.ok()) {
-    LOG(ERROR) << "Failed to GetKeyGeneratorCurrentNumber";
-    return std::make_unique<IndexedDBKey>();
-  }
-  if (current_number < 0 || current_number > max_generator_value) {
-    return std::make_unique<IndexedDBKey>();
-  }
-
-  return std::make_unique<IndexedDBKey>(current_number,
-                                        blink::mojom::IDBKeyType::Number);
-}
-
-// Called at the end of a "put" operation. The key is a number that was either
-// generated by the generator which now needs to be incremented (so
-// `check_current` is false) or was user-supplied so we only conditionally use
-// (and `check_current` is true).
-Status UpdateKeyGenerator(Transaction* transaction,
-                          int64_t object_store_id,
-                          const IndexedDBKey& key,
-                          bool check_current) {
-  DCHECK_EQ(blink::mojom::IDBKeyType::Number, key.type());
-  // Maximum integer uniquely representable as ECMAScript number.
-  const double max_generator_value = 9007199254740992.0;
-  int64_t value = base::saturated_cast<int64_t>(
-      floor(std::min(key.number(), max_generator_value)));
-  return transaction->BackingStoreTransaction()
-      ->MaybeUpdateKeyGeneratorCurrentNumber(object_store_id, value + 1,
-                                             check_current);
-}
-
 }  // namespace
 
-Database::PutOperationParams::PutOperationParams() = default;
-Database::PutOperationParams::~PutOperationParams() = default;
-
 Database::OpenCursorOperationParams::OpenCursorOperationParams() = default;
 Database::OpenCursorOperationParams::~OpenCursorOperationParams() = default;
 
@@ -757,133 +715,6 @@
   return s;
 }
 
-Status Database::PutOperation(std::unique_ptr<PutOperationParams> params,
-                              Transaction* transaction) {
-  TRACE_EVENT2("IndexedDB", "Database::PutOperation", "txn.id",
-               transaction->id(), "size", params->value.SizeEstimate());
-  DCHECK_NE(transaction->mode(), blink::mojom::IDBTransactionMode::ReadOnly);
-  bool key_was_generated = false;
-  Status s = Status::OK();
-  transaction->in_flight_memory() -= params->value.SizeEstimate();
-  DCHECK(transaction->in_flight_memory().IsValid());
-
-  if (!IsObjectStoreIdInMetadata(params->object_store_id)) {
-    std::move(params->callback)
-        .Run(blink::mojom::IDBTransactionPutResult::NewErrorResult(
-            CreateIDBErrorPtr(blink::mojom::IDBException::kUnknownError,
-                              "Bad request", transaction)));
-    return Status::InvalidArgument("Invalid object_store_id.");
-  }
-
-  const IndexedDBObjectStoreMetadata& object_store =
-      GetObjectStoreMetadata(params->object_store_id);
-  DCHECK(object_store.auto_increment || params->key->IsValid());
-
-  std::unique_ptr<IndexedDBKey> key;
-  if (params->put_mode != blink::mojom::IDBPutMode::CursorUpdate &&
-      object_store.auto_increment && !params->key->IsValid()) {
-    std::unique_ptr<IndexedDBKey> auto_inc_key =
-        GenerateKey(transaction, params->object_store_id);
-    key_was_generated = true;
-    if (!auto_inc_key->IsValid()) {
-      std::move(params->callback)
-          .Run(blink::mojom::IDBTransactionPutResult::NewErrorResult(
-              CreateIDBErrorPtr(blink::mojom::IDBException::kConstraintError,
-                                "Maximum key generator value reached.",
-                                transaction)));
-      return s;
-    }
-    key = std::move(auto_inc_key);
-  } else {
-    key = std::move(params->key);
-  }
-
-  if (!key->IsValid()) {
-    return Status::InvalidArgument("Invalid key");
-  }
-
-  BackingStore::RecordIdentifier record_identifier;
-  if (params->put_mode == blink::mojom::IDBPutMode::AddOnly) {
-    bool found = false;
-    Status found_status =
-        transaction->BackingStoreTransaction()->KeyExistsInObjectStore(
-            params->object_store_id, *key, &record_identifier, &found);
-    if (!found_status.ok()) {
-      return found_status;
-    }
-    if (found) {
-      std::move(params->callback)
-          .Run(blink::mojom::IDBTransactionPutResult::NewErrorResult(
-              CreateIDBErrorPtr(blink::mojom::IDBException::kConstraintError,
-                                "Key already exists in the object store.",
-                                transaction)));
-      return found_status;
-    }
-  }
-
-  std::vector<std::unique_ptr<IndexWriter>> index_writers;
-  std::string error_message;
-  bool obeys_constraints = false;
-  bool backing_store_success = MakeIndexWriters(
-      transaction, object_store, *key, key_was_generated, params->index_keys,
-      &index_writers, &error_message, &obeys_constraints);
-  if (!backing_store_success) {
-    std::move(params->callback)
-        .Run(blink::mojom::IDBTransactionPutResult::NewErrorResult(
-            CreateIDBErrorPtr(
-                blink::mojom::IDBException::kUnknownError,
-                "Internal error: backing store error updating index keys.",
-                transaction)));
-    return s;
-  }
-  if (!obeys_constraints) {
-    std::move(params->callback)
-        .Run(blink::mojom::IDBTransactionPutResult::NewErrorResult(
-            CreateIDBErrorPtr(blink::mojom::IDBException::kConstraintError,
-                              error_message, transaction)));
-    return s;
-  }
-
-  // Before this point, don't do any mutation. After this point, rollback the
-  // transaction in case of error.
-  s = transaction->BackingStoreTransaction()->PutRecord(
-      params->object_store_id, *key, &params->value, &record_identifier);
-  if (!s.ok()) {
-    return s;
-  }
-
-  {
-    TRACE_EVENT1("IndexedDB", "Database::PutOperation.UpdateIndexes", "txn.id",
-                 transaction->id());
-    for (const auto& writer : index_writers) {
-      writer->WriteIndexKeys(record_identifier,
-                             transaction->BackingStoreTransaction(),
-                             params->object_store_id);
-    }
-  }
-
-  if (object_store.auto_increment &&
-      params->put_mode != blink::mojom::IDBPutMode::CursorUpdate &&
-      key->type() == blink::mojom::IDBKeyType::Number) {
-    TRACE_EVENT1("IndexedDB", "Database::PutOperation.AutoIncrement", "txn.id",
-                 transaction->id());
-    s = UpdateKeyGenerator(transaction, params->object_store_id, *key,
-                           !key_was_generated);
-    if (!s.ok()) {
-      return s;
-    }
-  }
-  {
-    TRACE_EVENT1("IndexedDB", "Database::PutOperation.Callbacks", "txn.id",
-                 transaction->id());
-    std::move(params->callback)
-        .Run(blink::mojom::IDBTransactionPutResult::NewKey(*key));
-  }
-
-  bucket_context_->delegate().on_content_changed.Run(name_, object_store.name);
-  return s;
-}
-
 Status Database::SetIndexKeysOperation(
     int64_t object_store_id,
     std::unique_ptr<IndexedDBKey> primary_key,
diff --git a/content/browser/indexed_db/instance/database.h b/content/browser/indexed_db/instance/database.h
index 3699f5d..2e85ca56 100644
--- a/content/browser/indexed_db/instance/database.h
+++ b/content/browser/indexed_db/instance/database.h
@@ -44,7 +44,6 @@
 class Connection;
 class DatabaseCallbacks;
 class Transaction;
-struct IndexedDBValue;
 enum class CursorType;
 
 // This class maps to a single IDB database:
@@ -116,23 +115,6 @@
                       blink::mojom::IDBDatabase::GetCallback callback,
                       Transaction* transaction);
 
-  struct CONTENT_EXPORT PutOperationParams {
-    PutOperationParams();
-
-    PutOperationParams(const PutOperationParams&) = delete;
-    PutOperationParams& operator=(const PutOperationParams&) = delete;
-
-    ~PutOperationParams();
-    int64_t object_store_id;
-    IndexedDBValue value;
-    std::unique_ptr<blink::IndexedDBKey> key;
-    blink::mojom::IDBPutMode put_mode;
-    blink::mojom::IDBTransaction::PutCallback callback;
-    std::vector<blink::IndexedDBIndexKeys> index_keys;
-  };
-  Status PutOperation(std::unique_ptr<PutOperationParams> params,
-                      Transaction* transaction);
-
   Status SetIndexKeysOperation(
       int64_t object_store_id,
       std::unique_ptr<blink::IndexedDBKey> primary_key,
diff --git a/content/browser/indexed_db/instance/database_unittest.cc b/content/browser/indexed_db/instance/database_unittest.cc
index ffe7e31..dea67a1 100644
--- a/content/browser/indexed_db/instance/database_unittest.cc
+++ b/content/browser/indexed_db/instance/database_unittest.cc
@@ -64,6 +64,17 @@
 
 // Contains a record's keys and value that tests use to populate the database.
 struct TestIDBRecord {
+  TestIDBRecord(IndexedDBKey primary_key,
+                const IndexedDBValue& value,
+                std::optional<IndexedDBKey> index_key)
+      : primary_key(primary_key), value(value.Clone()), index_key(index_key) {}
+
+  TestIDBRecord(const TestIDBRecord& other) {
+    primary_key = other.primary_key;
+    value = other.value.Clone();
+    index_key = other.index_key;
+  }
+
   IndexedDBKey primary_key;
   IndexedDBValue value;
   // Optional. Tests may skip index creation.
@@ -693,8 +704,8 @@
   // After setup, calls `Database::GetAllOperation` with `get_all_parameters`.
   // Verifies that the results match `expected_results`.
   void TestGetAll(
-      const TestDatabaseParameters& database_parameters,
-      const TestGetAllParameters& get_all_parameters,
+      TestDatabaseParameters database_parameters,
+      TestGetAllParameters get_all_parameters,
       base::span<const blink::mojom::IDBRecordPtr> expected_results) {
     // Create the object store.
     ASSERT_EQ(0u, db_->metadata().object_stores.size());
@@ -737,16 +748,12 @@
 
       // Set in-flight memory to a reasonably large number to prevent underflow
       // in `PutOperation`
-      transaction_->in_flight_memory() += 1000;
+      transaction_->in_flight_memory_ += 1000;
 
-      auto put_params = std::make_unique<Database::PutOperationParams>();
-      put_params->object_store_id = store_id;
-      put_params->value = record.value;
-      put_params->key = std::make_unique<IndexedDBKey>(record.primary_key);
-      put_params->put_mode = blink::mojom::IDBPutMode::AddOnly;
-      put_params->callback = callback.Get();
-      put_params->index_keys = std::move(index_keys);
-      status = db_->PutOperation(std::move(put_params), transaction_);
+      status = transaction_->DoPut(
+          store_id, record.value.Clone(), record.primary_key,
+          blink::mojom::IDBPutMode::AddOnly, std::move(index_keys),
+          callback.Get(), transaction_);
       EXPECT_TRUE(status.ok()) << status.ToString();
     }
 
@@ -913,23 +920,16 @@
   EXPECT_TRUE(s.ok());
   EXPECT_EQ(1ULL, db_->metadata().object_stores.size());
 
-  IndexedDBValue value("value1", {});
-  std::unique_ptr<IndexedDBKey> key(std::make_unique<IndexedDBKey>("key"));
-  std::vector<IndexedDBIndexKeys> index_keys;
   base::MockCallback<blink::mojom::IDBTransaction::PutCallback> callback;
 
   // Set in-flight memory to a reasonably large number to prevent underflow in
   // `PutOperation`
-  transaction_->in_flight_memory() += 1000;
+  transaction_->in_flight_memory_ += 1000;
 
-  auto put_params = std::make_unique<Database::PutOperationParams>();
-  put_params->object_store_id = store_id;
-  put_params->value = value;
-  put_params->key = std::move(key);
-  put_params->put_mode = blink::mojom::IDBPutMode::AddOnly;
-  put_params->callback = callback.Get();
-  put_params->index_keys = index_keys;
-  s = db_->PutOperation(std::move(put_params), transaction_);
+  s = transaction_->DoPut(
+      store_id, IndexedDBValue("value1", {}), IndexedDBKey("key"),
+      blink::mojom::IDBPutMode::AddOnly, std::vector<IndexedDBIndexKeys>(),
+      callback.Get(), transaction_);
   EXPECT_TRUE(s.ok());
 
   s = transaction_->BackingStoreTransaction()->DeleteObjectStore(store_id);
@@ -1737,9 +1737,9 @@
     const std::string primary_key = base::StringPrintf("key%zu", i);
     const std::string value = base::StringPrintf("value%zu", i);
 
-    database_records.push_back({IndexedDBKey{primary_key},
-                                {value, /*external_objects=*/{}},
-                                /*index_key=*/std::nullopt});
+    database_records.emplace_back(IndexedDBKey{primary_key},
+                                  IndexedDBValue{value, {}},
+                                  /*index_key=*/std::nullopt);
 
     expected_results.emplace_back(
         blink::mojom::IDBRecord::New(IndexedDBKey{primary_key},
diff --git a/content/browser/indexed_db/instance/leveldb/backing_store.cc b/content/browser/indexed_db/instance/leveldb/backing_store.cc
index bfbed18a..63fd758 100644
--- a/content/browser/indexed_db/instance/leveldb/backing_store.cc
+++ b/content/browser/indexed_db/instance/leveldb/backing_store.cc
@@ -3845,7 +3845,7 @@
       std::unique_ptr<TransactionalLevelDBIterator> iterator)
       : BackingStore::Cursor(other, std::move(iterator)),
         primary_key_(std::make_unique<IndexedDBKey>(*other->primary_key_)),
-        current_value_(other->current_value_),
+        current_value_(other->current_value_.Clone()),
         primary_leveldb_key_(other->primary_leveldb_key_) {}
 
   std::unique_ptr<IndexedDBKey> primary_key_;
diff --git a/content/browser/indexed_db/instance/leveldb/backing_store_unittest.cc b/content/browser/indexed_db/instance/leveldb/backing_store_unittest.cc
index 3a0deb7..280e001 100644
--- a/content/browser/indexed_db/instance/leveldb/backing_store_unittest.cc
+++ b/content/browser/indexed_db/instance/leveldb/backing_store_unittest.cc
@@ -713,7 +713,7 @@
 TEST_F(BackingStoreTest, PutGetConsistency) {
   base::RunLoop loop;
   const IndexedDBKey key = key1_;
-  IndexedDBValue value = value1_;
+  IndexedDBValue& value = value1_;
   BackingStore::Database db(*backing_store(),
                             BackingStore::DatabaseMetadata{u"name"});
   db.metadata().id = 1;
@@ -843,7 +843,7 @@
     external_objects().push_back(CreateBlobInfo(base::UTF8ToUTF16(type), 1));
   }
 
-  std::vector<IndexedDBValue> values = {
+  std::array<IndexedDBValue, 4> values{
       IndexedDBValue("value0", {external_objects()[0]}),
       IndexedDBValue("value1", {external_objects()[1]}),
       IndexedDBValue("value2", {external_objects()[2]}),
@@ -910,7 +910,7 @@
     blob_context_->ClearWrites();
     file_system_access_context_->ClearWrites();
 
-    std::vector<IndexedDBValue> values = {
+    std::array<IndexedDBValue, 4> values{
         IndexedDBValue("value0", {external_objects[0]}),
         IndexedDBValue("value1", {external_objects[1]}),
         IndexedDBValue("value2", {external_objects[2]}),
@@ -993,7 +993,7 @@
     blob_context_->ClearWrites();
     file_system_access_context_->ClearWrites();
 
-    std::vector<IndexedDBValue> values = {
+    std::array<IndexedDBValue, 4> values{
         IndexedDBValue("value0", {external_objects[0]}),
         IndexedDBValue("value1", {external_objects[1]}),
         IndexedDBValue("value2", {external_objects[2]}),
@@ -1248,7 +1248,7 @@
 TEST_F(BackingStoreTest, HighIds) {
   IndexedDBKey key1 = key1_;
   IndexedDBKey key2 = key2_;
-  IndexedDBValue value1 = value1_;
+  IndexedDBValue& value1 = value1_;
 
   const int64_t high_database_id = 1ULL << 35;
   const int64_t high_object_store_id = 1ULL << 39;
@@ -1324,7 +1324,7 @@
 // Make sure that other invalid ids do not crash.
 TEST_F(BackingStoreTest, InvalidIds) {
   const IndexedDBKey key = key1_;
-  IndexedDBValue value = value1_;
+  IndexedDBValue& value = value1_;
 
   // valid ids for use when testing invalid ids
   const int64_t database_id = 1;
@@ -2113,7 +2113,7 @@
       external_objects.push_back(CreateBlobInfo(base::UTF8ToUTF16(type), 1));
     }
 
-    std::vector<IndexedDBValue> values = {
+    std::array<IndexedDBValue, 4> values{
         IndexedDBValue("value0", {external_objects[0]}),
         IndexedDBValue("value1", {external_objects[1]}),
         IndexedDBValue("value2", {external_objects[2]}),
diff --git a/content/browser/indexed_db/instance/transaction.cc b/content/browser/indexed_db/instance/transaction.cc
index 1d6bb089..96ef54b2 100644
--- a/content/browser/indexed_db/instance/transaction.cc
+++ b/content/browser/indexed_db/instance/transaction.cc
@@ -44,6 +44,7 @@
 #include "content/browser/indexed_db/instance/cursor.h"
 #include "content/browser/indexed_db/instance/database.h"
 #include "content/browser/indexed_db/instance/database_callbacks.h"
+#include "content/browser/indexed_db/instance/index_writer.h"
 #include "content/browser/indexed_db/instance/lock_request_data.h"
 #include "content/browser/indexed_db/status.h"
 #include "mojo/public/cpp/bindings/message.h"
@@ -476,28 +477,153 @@
   // Warm up the disk space cache.
   bucket_context()->CheckCanUseDiskSpace(preliminary_size_estimate_, {});
 
-  std::unique_ptr<Database::PutOperationParams> params(
-      std::make_unique<Database::PutOperationParams>());
-  IndexedDBValue& output_value = params->value;
+  IndexedDBValue value;
+  value.bits = std::move(input_value->bits);
+  value.external_objects = std::move(external_objects);
 
-  output_value.bits = std::move(input_value->bits);
-  swap(output_value.external_objects, external_objects);
-
-  blink::mojom::IDBTransaction::PutCallback aborting_callback =
+  blink::mojom::IDBTransaction::PutCallback wrapped_callback =
       CreateCallbackAbortOnDestruct<blink::mojom::IDBTransaction::PutCallback,
                                     blink::mojom::IDBTransactionPutResultPtr>(
           std::move(callback), AsWeakPtr());
 
-  params->object_store_id = object_store_id;
-  params->key = std::make_unique<blink::IndexedDBKey>(key);
-  params->put_mode = mode;
-  params->callback = std::move(aborting_callback);
-  params->index_keys = index_keys;
-  // This is decremented in Database::PutOperation.
-  in_flight_memory_ += output_value.SizeEstimate();
-  ScheduleTask(BindWeakOperation(&Database::PutOperation,
-                                 connection()->database()->AsWeakPtr(),
-                                 std::move(params)));
+  // This is decremented in DoPut.
+  in_flight_memory_ += value.SizeEstimate();
+  ScheduleTask(BindWeakOperation(&Transaction::DoPut, AsWeakPtr(),
+                                 object_store_id, std::move(value), key, mode,
+                                 index_keys, std::move(wrapped_callback)));
+}
+
+Status Transaction::DoPut(int64_t object_store_id,
+                          IndexedDBValue value,
+                          blink::IndexedDBKey key,
+                          blink::mojom::IDBPutMode put_mode,
+                          std::vector<blink::IndexedDBIndexKeys> index_keys,
+                          blink::mojom::IDBTransaction::PutCallback callback,
+                          Transaction* txn) {
+  DCHECK_EQ(this, txn);
+  TRACE_EVENT2("IndexedDB", "Database::PutOperation", "txn.id", id(), "size",
+               value.SizeEstimate());
+  DCHECK_NE(mode(), blink::mojom::IDBTransactionMode::ReadOnly);
+  bool key_was_generated = false;
+  in_flight_memory_ -= value.SizeEstimate();
+  DCHECK(in_flight_memory_.IsValid());
+
+  auto on_put_error = [&txn](blink::mojom::IDBTransaction::PutCallback callback,
+                             blink::mojom::IDBException code,
+                             const std::u16string& message) {
+    txn->IncrementNumErrorsSent();
+    std::move(callback).Run(
+        blink::mojom::IDBTransactionPutResult::NewErrorResult(
+            blink::mojom::IDBError::New(code, message)));
+  };
+
+  if (!connection()->database()->IsObjectStoreIdInMetadata(object_store_id)) {
+    on_put_error(std::move(callback), blink::mojom::IDBException::kUnknownError,
+                 u"Bad request");
+    return Status::InvalidArgument("Invalid object_store_id.");
+  }
+
+  const blink::IndexedDBObjectStoreMetadata& object_store =
+      connection()->database()->GetObjectStoreMetadata(object_store_id);
+  DCHECK(object_store.auto_increment || key.IsValid());
+  if (put_mode != blink::mojom::IDBPutMode::CursorUpdate &&
+      object_store.auto_increment && !key.IsValid()) {
+    blink::IndexedDBKey auto_inc_key =
+        GenerateAutoIncrementKey(object_store_id);
+    key_was_generated = true;
+    if (!auto_inc_key.IsValid()) {
+      on_put_error(std::move(callback),
+                   blink::mojom::IDBException::kConstraintError,
+                   u"Maximum key generator value reached.");
+      return Status::OK();
+    }
+    key = std::move(auto_inc_key);
+  }
+
+  if (!key.IsValid()) {
+    return Status::InvalidArgument("Invalid key");
+  }
+
+  BackingStore::RecordIdentifier record_identifier;
+  if (put_mode == blink::mojom::IDBPutMode::AddOnly) {
+    bool found = false;
+    Status found_status = BackingStoreTransaction()->KeyExistsInObjectStore(
+        object_store_id, key, &record_identifier, &found);
+    if (!found_status.ok()) {
+      return found_status;
+    }
+    if (found) {
+      on_put_error(std::move(callback),
+                   blink::mojom::IDBException::kConstraintError,
+                   u"Key already exists in the object store.");
+      return Status::OK();
+    }
+  }
+
+  std::vector<std::unique_ptr<IndexWriter>> index_writers;
+  std::string error_message;
+  bool obeys_constraints = false;
+  bool backing_store_success =
+      MakeIndexWriters(this, object_store, key, key_was_generated, index_keys,
+                       &index_writers, &error_message, &obeys_constraints);
+  if (!backing_store_success) {
+    on_put_error(std::move(callback), blink::mojom::IDBException::kUnknownError,
+                 u"Internal error: backing store error updating index keys.");
+    return Status::OK();
+  }
+  if (!obeys_constraints) {
+    on_put_error(std::move(callback),
+                 blink::mojom::IDBException::kConstraintError,
+                 base::UTF8ToUTF16(error_message));
+    return Status::OK();
+  }
+
+  // Before this point, don't do any mutation. After this point, rollback the
+  // transaction in case of error.
+  {
+    Status s = BackingStoreTransaction()->PutRecord(object_store_id, key,
+                                                    &value, &record_identifier);
+    if (!s.ok()) {
+      return s;
+    }
+  }
+
+  {
+    TRACE_EVENT1("IndexedDB", "Database::PutOperation.UpdateIndexes", "txn.id",
+                 id());
+    for (const auto& writer : index_writers) {
+      writer->WriteIndexKeys(record_identifier, BackingStoreTransaction(),
+                             object_store_id);
+    }
+  }
+
+  if (object_store.auto_increment &&
+      put_mode != blink::mojom::IDBPutMode::CursorUpdate &&
+      key.type() == blink::mojom::IDBKeyType::Number) {
+    TRACE_EVENT1("IndexedDB", "Database::PutOperation.AutoIncrement", "txn.id",
+                 id());
+    // Maximum integer uniquely representable as ECMAScript number.
+    const double max_generator_value = 9007199254740992.0;
+    int64_t new_max = 1 + base::saturated_cast<int64_t>(floor(
+                              std::min(key.number(), max_generator_value)));
+    // The key is a number that was either generated by the generator which now
+    // needs to be incremented (so `check_current` is false) or was
+    // user-supplied so we only conditionally use (and `check_current` is true).
+    Status s = BackingStoreTransaction()->MaybeUpdateKeyGeneratorCurrentNumber(
+        object_store_id, new_max, /*check_current=*/!key_was_generated);
+    if (!s.ok()) {
+      return s;
+    }
+  }
+  {
+    TRACE_EVENT1("IndexedDB", "Database::PutOperation.Callbacks", "txn.id",
+                 id());
+    std::move(callback).Run(blink::mojom::IDBTransactionPutResult::NewKey(key));
+  }
+
+  bucket_context()->delegate().on_content_changed.Run(
+      connection()->database()->name(), object_store.name);
+  return Status::OK();
 }
 
 void Transaction::Commit(int64_t num_errors_handled) {
@@ -964,4 +1090,22 @@
   lock_request_data->scheduling_priority = new_priority;
 }
 
+blink::IndexedDBKey Transaction::GenerateAutoIncrementKey(
+    int64_t object_store_id) {
+  // Maximum integer uniquely representable as ECMAScript number.
+  const int64_t max_generator_value = 9007199254740992LL;
+  int64_t current_number;
+  Status s = BackingStoreTransaction()->GetKeyGeneratorCurrentNumber(
+      object_store_id, &current_number);
+  if (!s.ok()) {
+    LOG(ERROR) << "Failed to GetKeyGeneratorCurrentNumber";
+    return {};
+  }
+  if (current_number < 0 || current_number > max_generator_value) {
+    return {};
+  }
+
+  return blink::IndexedDBKey(current_number, blink::mojom::IDBKeyType::Number);
+}
+
 }  // namespace content::indexed_db
diff --git a/content/browser/indexed_db/instance/transaction.h b/content/browser/indexed_db/instance/transaction.h
index 1b9995d..4d1180f 100644
--- a/content/browser/indexed_db/instance/transaction.h
+++ b/content/browser/indexed_db/instance/transaction.h
@@ -171,14 +171,13 @@
   const base::flat_set<PartitionedLockId> lock_ids() const { return lock_ids_; }
   PartitionedLockHolder* mutable_locks_receiver() { return &locks_receiver_; }
 
-  // in_flight_memory() is used to keep track of all memory scheduled to be
-  // written using ScheduleTask. This is reported to memory dumps.
-  base::CheckedNumeric<size_t>& in_flight_memory() { return in_flight_memory_; }
+  size_t in_flight_memory() const { return in_flight_memory_.ValueOrDie(); }
 
  private:
   friend class IndexedDBClassFactory;
   friend class Connection;
   friend class base::RefCounted<Transaction>;
+  friend class DatabaseOperationTest;
 
   FRIEND_TEST_ALL_PREFIXES(TransactionTestMode, AbortPreemptive);
   FRIEND_TEST_ALL_PREFIXES(TransactionTestMode, AbortTasks);
@@ -189,6 +188,7 @@
   FRIEND_TEST_ALL_PREFIXES(TransactionTest, Timeout);
   FRIEND_TEST_ALL_PREFIXES(TransactionTest, TimeoutPreemptive);
   FRIEND_TEST_ALL_PREFIXES(TransactionTest, TimeoutWithPriorities);
+  FRIEND_TEST_ALL_PREFIXES(DatabaseOperationTest, CreatePutDelete);
 
   // blink::mojom::IDBTransaction:
   void CreateObjectStore(int64_t object_store_id,
@@ -214,6 +214,14 @@
 
   Status DoPendingCommit();
 
+  Status DoPut(int64_t object_store_id,
+               IndexedDBValue value,
+               blink::IndexedDBKey key,
+               blink::mojom::IDBPutMode put_mode,
+               std::vector<blink::IndexedDBIndexKeys> index_keys,
+               blink::mojom::IDBTransaction::PutCallback callback,
+               Transaction* transaction);
+
   // Helper for posting a task to call Transaction::CommitPhaseTwo when
   // we know the transaction had no requests and therefore the commit must
   // succeed.
@@ -230,6 +238,10 @@
   void ResetTimeoutTimer();
   void SetState(State state);
 
+  // Generates a key for an auto_increment object store, or an invalid key if
+  // the backing store has a problem.
+  blink::IndexedDBKey GenerateAutoIncrementKey(int64_t object_store_id);
+
   const int64_t id_;
   const std::set<int64_t> object_store_ids_;
   const blink::mojom::IDBTransactionMode mode_;
diff --git a/content/browser/renderer_host/back_forward_cache_impl.cc b/content/browser/renderer_host/back_forward_cache_impl.cc
index 3fa50a4..f5ef69b0 100644
--- a/content/browser/renderer_host/back_forward_cache_impl.cc
+++ b/content/browser/renderer_host/back_forward_cache_impl.cc
@@ -959,7 +959,8 @@
                    *browser_context, rfh->GetLastCommittedURL(),
                    rfh->ComputeSiteForCookies(),
                    rfh->ComputeTopFrameOrigin(rfh->GetLastCommittedOrigin()),
-                   rfh->GetCookieSettingOverrides())) {
+                   rfh->GetCookieSettingOverrides(),
+                   rfh->GetStorageKey().ToCookiePartitionKey())) {
         result.No(
             BackForwardCacheMetrics::NotRestoredReason::kCacheControlNoStore);
         result.No(BackForwardCacheMetrics::NotRestoredReason::kCookieDisabled);
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index d5273fc..dafc516 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -8066,11 +8066,16 @@
   // knows locally about it because we sent an empty name at frame creation
   // time. The renderer has now committed the page and we can safely enforce the
   // empty name on the browser side.
+  BrowserContext* context =
+      frame_tree_node_->navigator().controller().GetBrowserContext();
   bool should_clear_browsing_instance_name =
       browsing_context_group_swap().ShouldClearWindowName() ||
       (commit_params().is_cross_site_cross_browsing_context_group &&
        base::FeatureList::IsEnabled(
-           features::kClearCrossSiteCrossBrowsingContextGroupWindowName));
+           features::kClearCrossSiteCrossBrowsingContextGroupWindowName) &&
+       GetContentClient()
+           ->browser()
+           ->IsClearWindowNameForNewBrowsingContextGroupAllowed(context));
 
   if (should_clear_browsing_instance_name) {
     std::string name, unique_name;
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 07cea50..4e4dd67 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -4318,6 +4318,8 @@
   // clear the frame name. This below informs the renderer at frame creation.
   NavigationRequest* navigation_request =
       frame_tree_node()->navigation_request();
+  BrowserContext* context =
+      frame_tree_node()->navigator().controller().GetBrowserContext();
 
   bool should_clear_browsing_instance_name =
       navigation_request &&
@@ -4326,7 +4328,10 @@
        (navigation_request->commit_params()
             .is_cross_site_cross_browsing_context_group &&
         base::FeatureList::IsEnabled(
-            features::kClearCrossSiteCrossBrowsingContextGroupWindowName)));
+            features::kClearCrossSiteCrossBrowsingContextGroupWindowName) &&
+        GetContentClient()
+            ->browser()
+            ->IsClearWindowNameForNewBrowsingContextGroupAllowed(context)));
 
   if (should_clear_browsing_instance_name) {
     params->replication_state->name = "";
diff --git a/content/browser/web_contents/web_contents_view_child_frame.cc b/content/browser/web_contents/web_contents_view_child_frame.cc
index b89d4621d..9c206f6 100644
--- a/content/browser/web_contents/web_contents_view_child_frame.cc
+++ b/content/browser/web_contents/web_contents_view_child_frame.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/notimplemented.h"
 #include "build/build_config.h"
 #include "content/browser/renderer_host/render_frame_proxy_host.h"
 #include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
@@ -160,15 +161,15 @@
 }
 
 void WebContentsViewChildFrame::RestoreFocus() {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
 void WebContentsViewChildFrame::Focus() {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
 void WebContentsViewChildFrame::StoreFocus() {
-  NOTREACHED();
+  NOTIMPLEMENTED();
 }
 
 void WebContentsViewChildFrame::FocusThroughTabTraversal(bool reverse) {
diff --git a/content/common/features.cc b/content/common/features.cc
index 4947a707..201f17f3 100644
--- a/content/common/features.cc
+++ b/content/common/features.cc
@@ -548,6 +548,11 @@
              "LimitCrossOriginNonActivatedPaintHolding",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// Kill switch for post OOP-C cleanup crbug.com/391648152
+BASE_FEATURE(kDisallowRasterInterfaceWithoutSkiaBackend,
+             "DisallowRasterInterfaceWithoutSkiaBackend",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 // Please keep features in alphabetical order.
 
 }  // namespace features
diff --git a/content/common/features.h b/content/common/features.h
index eaff791..4dc19d791 100644
--- a/content/common/features.h
+++ b/content/common/features.h
@@ -154,6 +154,8 @@
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebUIInProcessResourceLoading);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kLimitCrossOriginNonActivatedPaintHolding);
 
+CONTENT_EXPORT BASE_DECLARE_FEATURE(kDisallowRasterInterfaceWithoutSkiaBackend);
+
 // Please keep features in alphabetical order.
 
 }  // namespace features
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index d96d069..11c916e4 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -4,7 +4,6 @@
 
 import("//build/config/android/config.gni")
 import("//build/config/android/rules.gni")
-import("//build/config/locales.gni")
 import("//device/vr/buildflags/buildflags.gni")
 import("//third_party/jni_zero/jni_zero.gni")
 import("//tools/grit/grit_rule.gni")
@@ -452,10 +451,6 @@
 
 java_strings_grd("content_strings_grd") {
   grd_file = "java/strings/android_content_strings.grd"
-  outputs = [ "values/android_content_strings.xml" ] +
-            process_file_template(
-                android_bundle_locales_as_resources,
-                [ "values-{{source_name_part}}/android_content_strings.xml" ])
 }
 
 java_cpp_enum("content_public_android_java_enums_srcjar") {
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 3c8b332..4aa669a 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -1763,7 +1763,8 @@
     const GURL& url,
     const net::SiteForCookies& site_for_cookies,
     const url::Origin& top_frame_origin,
-    const net::CookieSettingOverrides overrides) {
+    const net::CookieSettingOverrides overrides,
+    base::optional_ref<const net::CookiePartitionKey> cookie_partition_key) {
   return true;
 }
 
@@ -1797,6 +1798,11 @@
   return true;
 }
 
+bool ContentBrowserClient::IsClearWindowNameForNewBrowsingContextGroupAllowed(
+    content::BrowserContext* browser_context) {
+  return true;
+}
+
 bool ContentBrowserClient::UseOutermostMainFrameOrEmbedderForSubCaptureTargets()
     const {
   return false;
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index afc93525..3e363b85 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -66,6 +66,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "net/base/schemeful_site.h"
+#include "net/cookies/cookie_partition_key.h"
 #include "net/cookies/cookie_setting_override.h"
 #include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom-forward.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
@@ -3005,7 +3006,8 @@
       const GURL& url,
       const net::SiteForCookies& site_for_cookies,
       const url::Origin& top_frame_origin,
-      const net::CookieSettingOverrides overrides);
+      const net::CookieSettingOverrides overrides,
+      base::optional_ref<const net::CookiePartitionKey> cookie_partition_key);
 
   // Callback will be called with either an error
   // (!=`FileSystemAccessStatus::kOk`) or a list of cloud file handles as
@@ -3036,6 +3038,11 @@
   virtual bool ShouldReduceAcceptLanguage(
       content::BrowserContext* browser_context);
 
+  // Checks whether window.name is allowed to be cleared for top-level
+  // cross-site navigations that create a new BrowsingContextGroup.
+  virtual bool IsClearWindowNameForNewBrowsingContextGroupAllowed(
+      content::BrowserContext* browser_context);
+
   // Set whether the browser is running in minimal mode (where most subsystems
   // are left uninitialized).
   virtual void SetIsMinimalMode(bool minimal) {}
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 0a5556c..4176c77 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1201,6 +1201,13 @@
     return nullptr;
   }
 
+  if (base::FeatureList::IsEnabled(
+          ::features::kDisallowRasterInterfaceWithoutSkiaBackend) &&
+      gpu_channel_host->gpu_info().skia_backend_type ==
+          gpu::SkiaBackendType::kNone) {
+    return nullptr;
+  }
+
   bool support_locking = false;
   bool support_raster_interface = true;
   // TODO(zmo): today if Skia backend is set, Chrome either runs in GPU
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 5747f94..64655ef 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -726,16 +726,26 @@
     gl_info->error_message = WebString::FromUTF8(error_message);
     return nullptr;
   }
+
+  if (base::FeatureList::IsEnabled(
+          ::features::kDisallowRasterInterfaceWithoutSkiaBackend) &&
+      web_attributes.enable_raster_interface &&
+      gpu_channel_host->gpu_info().skia_backend_type ==
+          gpu::SkiaBackendType::kNone) {
+    return nullptr;
+  }
+
   const auto& gpu_info = gpu_channel_host->gpu_info();
   Collect3DContextInformation(gl_info, gpu_info);
 
   gpu::ContextCreationAttribs attributes;
   attributes.bind_generates_resource = false;
   attributes.enable_raster_interface = web_attributes.enable_raster_interface;
-  // TODO(zmo): today if Skia backend is set, Chrome either runs in GPU
-  // acceleration mode, either on top of real GPU, or on top of SwiftShader
-  // (for testing). This may change in the future if we move Skia software
-  // rendering to be OOP as well.
+  // TODO(crbug.com/391648152, zmo): today if Skia backend is set, Chrome either
+  // runs in GPU acceleration mode, either on top of real GPU, or on top of
+  // SwiftShader (for testing). This may change in the future if we move Skia
+  // software rendering to be OOP as well. Clean this up once
+  // kDisallowRasterInterfaceWithoutSkiaBackend is rolled out safely.
   attributes.enable_gpu_rasterization =
       attributes.enable_raster_interface &&
       gpu_info.skia_backend_type != gpu::SkiaBackendType::kNone;
diff --git a/content/services/auction_worklet/public/cpp/BUILD.gn b/content/services/auction_worklet/public/cpp/BUILD.gn
index dd36d63..537fd86 100644
--- a/content/services/auction_worklet/public/cpp/BUILD.gn
+++ b/content/services/auction_worklet/public/cpp/BUILD.gn
@@ -4,6 +4,8 @@
     "auction_downloader.h",
     "auction_network_events_delegate.cc",
     "auction_network_events_delegate.h",
+    "creative_info.cc",
+    "creative_info.h",
     "private_aggregation_reporting.cc",
     "private_aggregation_reporting.h",
     "private_model_training_reporting.h",
diff --git a/content/services/auction_worklet/public/cpp/creative_info.cc b/content/services/auction_worklet/public/cpp/creative_info.cc
new file mode 100644
index 0000000..eac6168
--- /dev/null
+++ b/content/services/auction_worklet/public/cpp/creative_info.cc
@@ -0,0 +1,59 @@
+// 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 "content/services/auction_worklet/public/cpp/creative_info.h"
+
+#include <optional>
+#include <string>
+#include <tuple>
+
+#include "content/services/auction_worklet/public/mojom/seller_worklet.mojom.h"
+#include "third_party/blink/public/common/interest_group/ad_display_size.h"
+#include "url/origin.h"
+
+namespace auction_worklet {
+
+CreativeInfo::CreativeInfo() = default;
+CreativeInfo::CreativeInfo(blink::AdDescriptor ad_descriptor,
+                           std::string creative_scanning_metadata,
+                           std::optional<url::Origin> interest_group_owner,
+                           std::string buyer_and_seller_reporting_id)
+    : ad_descriptor(std::move(ad_descriptor)),
+      creative_scanning_metadata(std::move(creative_scanning_metadata)),
+      interest_group_owner(std::move(interest_group_owner)),
+      buyer_and_seller_reporting_id(std::move(buyer_and_seller_reporting_id)) {}
+
+CreativeInfo::CreativeInfo(
+    bool send_creative_scanning_metadata,
+    const mojom::CreativeInfoWithoutOwner& mojo_creative_info,
+    const url::Origin& in_interest_group_owner,
+    const std::optional<std::string>&
+        browser_signal_buyer_and_seller_reporting_id) {
+  ad_descriptor.url = mojo_creative_info.ad_descriptor.url;
+  if (send_creative_scanning_metadata) {
+    ad_descriptor.size = mojo_creative_info.ad_descriptor.size;
+    creative_scanning_metadata =
+        mojo_creative_info.creative_scanning_metadata.value_or(std::string());
+    interest_group_owner = in_interest_group_owner;
+    buyer_and_seller_reporting_id =
+        browser_signal_buyer_and_seller_reporting_id.value_or(std::string());
+  }
+}
+
+CreativeInfo::~CreativeInfo() = default;
+
+CreativeInfo::CreativeInfo(CreativeInfo&&) = default;
+CreativeInfo::CreativeInfo(const CreativeInfo&) = default;
+CreativeInfo& CreativeInfo::operator=(CreativeInfo&&) = default;
+CreativeInfo& CreativeInfo::operator=(const CreativeInfo&) = default;
+
+bool CreativeInfo::operator<(const CreativeInfo& other) const {
+  return std::tie(ad_descriptor, creative_scanning_metadata,
+                  interest_group_owner, buyer_and_seller_reporting_id) <
+         std::tie(other.ad_descriptor, other.creative_scanning_metadata,
+                  other.interest_group_owner,
+                  other.buyer_and_seller_reporting_id);
+}
+
+}  // namespace auction_worklet
diff --git a/content/services/auction_worklet/public/cpp/creative_info.h b/content/services/auction_worklet/public/cpp/creative_info.h
new file mode 100644
index 0000000..4629f4d1
--- /dev/null
+++ b/content/services/auction_worklet/public/cpp/creative_info.h
@@ -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.
+
+#ifndef CONTENT_SERVICES_AUCTION_WORKLET_PUBLIC_CPP_CREATIVE_INFO_H_
+#define CONTENT_SERVICES_AUCTION_WORKLET_PUBLIC_CPP_CREATIVE_INFO_H_
+
+#include <optional>
+#include <string>
+
+#include "content/common/content_export.h"
+#include "content/services/auction_worklet/public/mojom/seller_worklet.mojom-forward.h"
+#include "third_party/blink/public/common/interest_group/ad_display_size.h"
+#include "url/origin.h"
+
+namespace auction_worklet {
+
+// Info about a creative, either ad or component ad, that's sent to trusted
+// scoring signals server, corresponding to one chosen by a generateBid()
+// invocation. `buyer_and_seller_reporting_id` is only applicable, and only
+// sent, for ads - not for ad components - as ad components may not provide a
+// value for `buyer_and_seller_reporting_id` or any other reporting IDs.
+//
+// If operating with `send_creative_scanning_metadata` true, the same URL may
+// need to be repeated, in cases like it occurring in multiple interest groups
+// with the same ad creative but different scanning metadata.
+//
+// When `send_creative_scanning_metadata` is false, all fields other than
+// `ad_descriptor`'s `url` must be kept empty to avoid needlessly duplicating
+// URLs.
+struct CONTENT_EXPORT CreativeInfo {
+  CreativeInfo();
+  CreativeInfo(blink::AdDescriptor ad_descriptor,
+               std::string creative_scanning_metadata,
+               std::optional<url::Origin> interest_group_owner,
+               std::string buyer_and_seller_reporting_id);
+  CreativeInfo(bool send_creative_scanning_metadata,
+               const mojom::CreativeInfoWithoutOwner& mojo_creative_info,
+               const url::Origin& in_interest_group_owner,
+               const std::optional<std::string>&
+                   browser_signal_buyer_and_seller_reporting_id);
+  ~CreativeInfo();
+
+  CreativeInfo(CreativeInfo&&);
+  CreativeInfo(const CreativeInfo&);
+  CreativeInfo& operator=(CreativeInfo&&);
+  CreativeInfo& operator=(const CreativeInfo&);
+
+  bool operator<(const CreativeInfo& other) const;
+
+  // The ad and size selected by generateBid().
+  blink::AdDescriptor ad_descriptor;
+
+  // From `InterestGroup::Ad::creative_scanning_metadata`, with nullopt
+  // converted to empty string.
+  std::string creative_scanning_metadata;
+
+  // From `InterestGroup::owner`.
+  std::optional<url::Origin> interest_group_owner;
+
+  // From `InterestGroup::Ad::buyer_and_seller_reporting_id`, with nullopt
+  // converted to empty string.
+  std::string buyer_and_seller_reporting_id;
+};
+
+}  // namespace auction_worklet
+
+#endif  // CONTENT_SERVICES_AUCTION_WORKLET_PUBLIC_CPP_CREATIVE_INFO_H_
diff --git a/content/services/auction_worklet/public/mojom/seller_worklet.mojom b/content/services/auction_worklet/public/mojom/seller_worklet.mojom
index b46b824..66342d9 100644
--- a/content/services/auction_worklet/public/mojom/seller_worklet.mojom
+++ b/content/services/auction_worklet/public/mojom/seller_worklet.mojom
@@ -157,7 +157,7 @@
 // scored. This is partly passed to JS, and can also go to trusted seller
 // signals requests.
 //
-// Compare to TrustedSignals::CreativeInfo; the owner isn't included here since
+// Compare to CreativeInfo; the owner isn't included here since
 // it's the same for everything passed to ScoreAd
 // (browser_signal_interest_group_owner)
 struct CreativeInfoWithoutOwner {
diff --git a/content/services/auction_worklet/seller_worklet.cc b/content/services/auction_worklet/seller_worklet.cc
index daca69f..17449b33 100644
--- a/content/services/auction_worklet/seller_worklet.cc
+++ b/content/services/auction_worklet/seller_worklet.cc
@@ -41,6 +41,7 @@
 #include "content/services/auction_worklet/private_aggregation_bindings.h"
 #include "content/services/auction_worklet/public/cpp/auction_network_events_delegate.h"
 #include "content/services/auction_worklet/public/cpp/auction_worklet_features.h"
+#include "content/services/auction_worklet/public/cpp/creative_info.h"
 #include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
 #include "content/services/auction_worklet/public/mojom/in_progress_auction_download.mojom.h"
 #include "content/services/auction_worklet/public/mojom/seller_worklet.mojom.h"
@@ -2211,11 +2212,11 @@
       send_creative_scanning_metadata_.value_or(false) &&
       !trusted_signals_request_manager_->HasPublicKey();
 
-  TrustedSignals::CreativeInfo main_ad(
+  CreativeInfo main_ad(
       send_creative_scanning_metadata, *score_ad_task->ad,
       score_ad_task->browser_signal_interest_group_owner,
       score_ad_task->browser_signal_buyer_and_seller_reporting_id);
-  std::set<TrustedSignals::CreativeInfo> component_ads;
+  std::set<CreativeInfo> component_ads;
   for (const auto& component : score_ad_task->ad_components) {
     component_ads.emplace(
         send_creative_scanning_metadata, *component,
diff --git a/content/services/auction_worklet/trusted_signals.cc b/content/services/auction_worklet/trusted_signals.cc
index 2852ae8..e173d56 100644
--- a/content/services/auction_worklet/trusted_signals.cc
+++ b/content/services/auction_worklet/trusted_signals.cc
@@ -30,6 +30,7 @@
 #include "content/services/auction_worklet/auction_v8_helper.h"
 #include "content/services/auction_worklet/public/cpp/auction_downloader.h"
 #include "content/services/auction_worklet/public/cpp/auction_network_events_delegate.h"
+#include "content/services/auction_worklet/public/cpp/creative_info.h"
 #include "content/services/auction_worklet/public/mojom/seller_worklet.mojom.h"
 #include "gin/converter.h"
 #include "gin/dictionary.h"
@@ -90,8 +91,7 @@
 // AdDescriptors used have everything but the URL discarded, so we don't
 // needlessly duplicate creative URLs, which might cause compatibility
 // problems.
-bool ContainsNonUrlInfo(
-    const std::set<TrustedSignals::CreativeInfo>& creative_info_set) {
+bool ContainsNonUrlInfo(const std::set<CreativeInfo>& creative_info_set) {
   for (const auto& creative_info : creative_info_set) {
     if (creative_info.ad_descriptor.size.has_value() ||
         !creative_info.creative_scanning_metadata.empty() ||
@@ -385,52 +385,6 @@
 
 TrustedSignals::Result::~Result() = default;
 
-TrustedSignals::CreativeInfo::CreativeInfo() = default;
-TrustedSignals::CreativeInfo::CreativeInfo(
-    blink::AdDescriptor ad_descriptor,
-    std::string creative_scanning_metadata,
-    std::optional<url::Origin> interest_group_owner,
-    std::string buyer_and_seller_reporting_id)
-    : ad_descriptor(std::move(ad_descriptor)),
-      creative_scanning_metadata(std::move(creative_scanning_metadata)),
-      interest_group_owner(std::move(interest_group_owner)),
-      buyer_and_seller_reporting_id(std::move(buyer_and_seller_reporting_id)) {}
-
-TrustedSignals::CreativeInfo::CreativeInfo(
-    bool send_creative_scanning_metadata,
-    const mojom::CreativeInfoWithoutOwner& mojo_creative_info,
-    const url::Origin& in_interest_group_owner,
-    const std::optional<std::string>&
-        browser_signal_buyer_and_seller_reporting_id) {
-  ad_descriptor.url = mojo_creative_info.ad_descriptor.url;
-  if (send_creative_scanning_metadata) {
-    ad_descriptor.size = mojo_creative_info.ad_descriptor.size;
-    creative_scanning_metadata =
-        mojo_creative_info.creative_scanning_metadata.value_or(std::string());
-    interest_group_owner = in_interest_group_owner;
-    buyer_and_seller_reporting_id =
-        browser_signal_buyer_and_seller_reporting_id.value_or(std::string());
-  }
-}
-
-TrustedSignals::CreativeInfo::~CreativeInfo() = default;
-
-TrustedSignals::CreativeInfo::CreativeInfo(CreativeInfo&&) = default;
-TrustedSignals::CreativeInfo::CreativeInfo(const CreativeInfo&) = default;
-TrustedSignals::CreativeInfo& TrustedSignals::CreativeInfo::operator=(
-    CreativeInfo&&) = default;
-TrustedSignals::CreativeInfo& TrustedSignals::CreativeInfo::operator=(
-    const CreativeInfo&) = default;
-
-bool TrustedSignals::CreativeInfo::operator<(
-    const TrustedSignals::CreativeInfo& other) const {
-  return std::tie(ad_descriptor, creative_scanning_metadata,
-                  interest_group_owner, buyer_and_seller_reporting_id) <
-         std::tie(other.ad_descriptor, other.creative_scanning_metadata,
-                  other.interest_group_owner,
-                  other.buyer_and_seller_reporting_id);
-}
-
 void TrustedSignals::BuildTrustedBiddingSignalsURL(
     const std::string& hostname,
     const GURL& trusted_bidding_signals_url,
diff --git a/content/services/auction_worklet/trusted_signals.h b/content/services/auction_worklet/trusted_signals.h
index 5b84047..f7d3a05 100644
--- a/content/services/auction_worklet/trusted_signals.h
+++ b/content/services/auction_worklet/trusted_signals.h
@@ -20,6 +20,7 @@
 #include "base/time/time.h"
 #include "content/common/content_export.h"
 #include "content/services/auction_worklet/auction_v8_helper.h"
+#include "content/services/auction_worklet/public/cpp/creative_info.h"
 #include "content/services/auction_worklet/public/mojom/auction_network_events_handler.mojom.h"
 #include "content/services/auction_worklet/public/mojom/seller_worklet.mojom-forward.h"
 #include "net/http/http_response_headers.h"
@@ -147,55 +148,6 @@
     // Data version associated with the trusted signals.
     const std::optional<uint32_t> data_version_;
   };
-
-  // Info about a creative, either ad or component ad, that's sent to trusted
-  // scoring signals server, corresponding to one chosen by a generateBid()
-  // invocation. `buyer_and_seller_reporting_id` is only applicable, and only
-  // sent, for ads - not for ad components - as ad components may not provide a
-  // value for `buyer_and_seller_reporting_id` or any other reporting IDs.
-  //
-  // If operating with `send_creative_scanning_metadata` true, the same URL may
-  // need to be repeated, in cases like it occurring in multiple interest groups
-  // with the same ad creative but different scanning metadata.
-  //
-  // When `send_creative_scanning_metadata` is false, all fields other than
-  // `ad_descriptor`'s `url` must be kept empty to avoid needlessly duplicating
-  // URLs.
-  struct CONTENT_EXPORT CreativeInfo {
-    CreativeInfo();
-    CreativeInfo(blink::AdDescriptor ad_descriptor,
-                 std::string creative_scanning_metadata,
-                 std::optional<url::Origin> interest_group_owner,
-                 std::string buyer_and_seller_reporting_id);
-    CreativeInfo(bool send_creative_scanning_metadata,
-                 const mojom::CreativeInfoWithoutOwner& mojo_creative_info,
-                 const url::Origin& in_interest_group_owner,
-                 const std::optional<std::string>&
-                     browser_signal_buyer_and_seller_reporting_id);
-    ~CreativeInfo();
-
-    CreativeInfo(CreativeInfo&&);
-    CreativeInfo(const CreativeInfo&);
-    CreativeInfo& operator=(CreativeInfo&&);
-    CreativeInfo& operator=(const CreativeInfo&);
-
-    bool operator<(const CreativeInfo& other) const;
-
-    // The ad and size selected by generateBid().
-    blink::AdDescriptor ad_descriptor;
-
-    // From `InterestGroup::Ad::creative_scanning_metadata`, with nullopt
-    // converted to empty string.
-    std::string creative_scanning_metadata;
-
-    // From `InterestGroup::owner`.
-    std::optional<url::Origin> interest_group_owner;
-
-    // From `InterestGroup::Ad::buyer_and_seller_reporting_id`, with nullopt
-    // converted to empty string.
-    std::string buyer_and_seller_reporting_id;
-  };
-
   using LoadSignalsCallback =
       base::OnceCallback<void(scoped_refptr<Result> result,
                               std::optional<std::string> error_msg)>;
diff --git a/content/services/auction_worklet/trusted_signals_request_manager.cc b/content/services/auction_worklet/trusted_signals_request_manager.cc
index a972068..37cb532 100644
--- a/content/services/auction_worklet/trusted_signals_request_manager.cc
+++ b/content/services/auction_worklet/trusted_signals_request_manager.cc
@@ -31,6 +31,7 @@
 #include "content/services/auction_worklet/auction_v8_helper.h"
 #include "content/services/auction_worklet/public/cpp/auction_network_events_delegate.h"
 #include "content/services/auction_worklet/public/cpp/auction_worklet_features.h"
+#include "content/services/auction_worklet/public/cpp/creative_info.h"
 #include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
 #include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
 #include "content/services/auction_worklet/trusted_kvv2_signals.h"
@@ -54,11 +55,6 @@
 
   virtual ~TrustedSignalsUrlBuilder() = default;
 
-  // Try including a new request in the URL. Return false if the request would
-  // make the URL too big. If `split_fetch_` is false, this will always return
-  // true.
-  virtual bool TryToAddRequest(RequestImpl* request) = 0;
-
   // Reset the builder so that it can be used to build another URL.
   void Reset() {
     main_fragments_.clear();
@@ -68,17 +64,10 @@
     bidding_signals_keys_.clear();
     ads_.clear();
     ad_components_.clear();
-    merged_requests_.clear();
     length_limit_ = std::numeric_limits<size_t>::max();
     added_first_request_ = false;
   }
 
-  // Extract the requests that were included via `AddRequest`.
-  std::set<raw_ptr<RequestImpl, SetExperimental>, CompareRequestImpl>
-  TakeMergedRequests() {
-    return std::move(merged_requests_);
-  }
-
   // Extract the attributes needed to build and create trusted bidding signals.
   std::set<std::string> TakeInterestGroupNames() {
     // We should never try to build a bidding signals URL without
@@ -91,13 +80,13 @@
   }
 
   // Extract the attributes needed to build and create trusted scoring signals.
-  std::set<TrustedSignals::CreativeInfo> TakeAds() {
+  std::set<CreativeInfo> TakeAds() {
     // We should never try to build a scoring signals URL without any ads.
     DCHECK(ads_.size());
     return std::move(ads_);
   }
 
-  std::set<TrustedSignals::CreativeInfo> TakeAdComponents() {
+  std::set<CreativeInfo> TakeAdComponents() {
     return std::move(ad_components_);
   }
 
@@ -122,19 +111,19 @@
         split_fetch_(split_fetch) {}
 
   // This method should be called with `main_fragments_` and `aux_fragments_`
-  // updated to incorporate `request`, with `initial_num_main_fragments` and
-  // `initial_num_aux_fragments` giving the size of those two vectors before
-  // the incorporation.
+  // updated to incorporate a trusted signals request, with
+  // `initial_num_main_fragments` and `initial_num_aux_fragments` giving the
+  // size of those two vectors before the incorporation.
   //
   // If the resulting URL is within various size limits, updates the object's
   // size-tracking state and return true.
   //
   // If the resulting URL is too large, rolls back the changes to
   // `main_fragments_` and `aux_fragments_`, and returns false, denoting that
-  // `request` should not be included in this batch.
+  // the request should not be included in this batch.
   bool CommitOrRollback(size_t initial_num_main_fragments,
                         size_t initial_num_aux_fragments,
-                        RequestImpl* request) {
+                        size_t max_trusted_signals_url_length) {
     size_t attempted_len = length_thus_far_;
     for (size_t i = initial_num_main_fragments; i < main_fragments_.size();
          ++i) {
@@ -144,10 +133,8 @@
       attempted_len += aux_fragments_[i].text.length();
     }
 
-    size_t len_target =
-        std::min(length_limit_, request->max_trusted_signals_url_length_);
+    size_t len_target = std::min(length_limit_, max_trusted_signals_url_length);
     if (!split_fetch_ || !added_first_request_ || attempted_len <= len_target) {
-      merged_requests_.insert(request);
       length_limit_ = len_target;
       added_first_request_ = true;
       length_thus_far_ = attempted_len;
@@ -197,10 +184,11 @@
   // Whether the URL should be split based on length limits.
   const bool split_fetch_;
 
-  // Whether a request has been added to `merged_requests_` yet.
+  // True if we've incorporated a request with TryToAddRequest() and haven't
+  // Reset() since.
   bool added_first_request_ = false;
 
-  // The maximum allowed length of a URL with this group of `merged_requests_`.
+  // The maximum allowed length of a URL with this group of requests.
   size_t length_limit_ = std::numeric_limits<size_t>::max();
 
   // Parameters for building a bidding signals URL.
@@ -208,17 +196,14 @@
   std::set<std::string> bidding_signals_keys_;
 
   // Parameters for building a scoring signals URL.
-  std::set<TrustedSignals::CreativeInfo> ads_;
-  std::set<TrustedSignals::CreativeInfo> ad_components_;
+  std::set<CreativeInfo> ads_;
+  std::set<CreativeInfo> ad_components_;
 
   // Portions of incrementally composed URL, and how long the current
   // portion is.
   std::vector<TrustedSignals::UrlPiece> main_fragments_;
   std::vector<TrustedSignals::UrlPiece> aux_fragments_;
   size_t length_thus_far_ = 0;
-
-  std::set<raw_ptr<RequestImpl, SetExperimental>, CompareRequestImpl>
-      merged_requests_;
 };
 
 class TrustedSignalsRequestManager::TrustedBiddingSignalsUrlBuilder
@@ -244,13 +229,14 @@
 
   ~TrustedBiddingSignalsUrlBuilder() override = default;
 
-  // TrustedSignalsUrlBuilder implementation.
-  bool TryToAddRequest(RequestImpl* request) override {
+  bool TryToAddRequest(const std::string& interest_group_name,
+                       const std::set<std::string>& bidder_keys,
+                       size_t max_trusted_signals_url_length) {
     // Figure out which fields are new.
-    std::set<std::string> new_interest_group_names = AddAndReturnNew(
-        request->interest_group_name_.value(), interest_group_names_);
+    std::set<std::string> new_interest_group_names =
+        AddAndReturnNew(interest_group_name, interest_group_names_);
     std::set<std::string> new_keys =
-        AddAndReturnNew(*request->bidder_keys_, bidding_signals_keys_);
+        AddAndReturnNew(bidder_keys, bidding_signals_keys_);
 
     size_t initial_num_main_fragments = main_fragments_.size();
     size_t initial_num_aux_fragments = aux_fragments_.size();
@@ -260,7 +246,7 @@
         main_fragments_, aux_fragments_);
 
     if (!CommitOrRollback(initial_num_main_fragments, initial_num_aux_fragments,
-                          request)) {
+                          max_trusted_signals_url_length)) {
       for (const auto& key : new_interest_group_names) {
         interest_group_names_.erase(key);
       }
@@ -297,12 +283,12 @@
 
   ~TrustedScoringSignalsUrlBuilder() override = default;
 
-  // TrustedSignalsUrlBuilder implementation.
-  bool TryToAddRequest(RequestImpl* request) override {
-    std::set<TrustedSignals::CreativeInfo> new_ads =
-        AddAndReturnNew(*request->ad_, ads_);
-    std::set<TrustedSignals::CreativeInfo> new_ad_components =
-        AddAndReturnNew(request->ad_components_, ad_components_);
+  bool TryToAddRequest(const CreativeInfo& ad,
+                       const std::set<CreativeInfo>& ad_components,
+                       size_t max_trusted_signals_url_length) {
+    std::set<CreativeInfo> new_ads = AddAndReturnNew(ad, ads_);
+    std::set<CreativeInfo> new_ad_components =
+        AddAndReturnNew(ad_components, ad_components_);
 
     size_t initial_num_main_fragments = main_fragments_.size();
     size_t initial_num_aux_fragments = aux_fragments_.size();
@@ -313,7 +299,7 @@
         aux_fragments_);
 
     if (!CommitOrRollback(initial_num_main_fragments, initial_num_aux_fragments,
-                          request)) {
+                          max_trusted_signals_url_length)) {
       for (const auto& key : new_ads) {
         ads_.erase(key);
       }
@@ -387,8 +373,8 @@
 
 std::unique_ptr<TrustedSignalsRequestManager::Request>
 TrustedSignalsRequestManager::RequestScoringSignals(
-    TrustedSignals::CreativeInfo ad,
-    std::set<TrustedSignals::CreativeInfo> ad_components,
+    CreativeInfo ad,
+    std::set<CreativeInfo> ad_components,
     int32_t max_trusted_scoring_signals_url_length,
     LoadSignalsCallback load_signals_callback) {
   DCHECK_EQ(Type::kScoringSignals, type_);
@@ -421,8 +407,8 @@
 
 std::unique_ptr<TrustedSignalsRequestManager::Request>
 TrustedSignalsRequestManager::RequestKVv2ScoringSignals(
-    TrustedSignals::CreativeInfo ad,
-    std::set<TrustedSignals::CreativeInfo> ad_components,
+    CreativeInfo ad,
+    std::set<CreativeInfo> ad_components,
     const url::Origin& bidder_owner_origin,
     const url::Origin& bidder_joining_origin,
     LoadSignalsCallback load_signals_callback) {
@@ -435,10 +421,35 @@
   return request;
 }
 
+bool TrustedSignalsRequestManager::TryToAddRequest(
+    TrustedBiddingSignalsUrlBuilder& bidding_url_builder,
+    RequestSet& merged_requests,
+    RequestImpl* request) {
+  bool success = bidding_url_builder.TryToAddRequest(
+      *request->interest_group_name_, *request->bidder_keys_,
+      request->max_trusted_signals_url_length_);
+  if (success) {
+    merged_requests.insert(request);
+  }
+  return success;
+}
+
+bool TrustedSignalsRequestManager::TryToAddRequest(
+    TrustedScoringSignalsUrlBuilder& scoring_url_builder,
+    RequestSet& merged_requests,
+    RequestImpl* request) {
+  bool success = scoring_url_builder.TryToAddRequest(
+      *request->ad_, request->ad_components_,
+      request->max_trusted_signals_url_length_);
+  if (success) {
+    merged_requests.insert(request);
+  }
+  return success;
+}
+
 void TrustedSignalsRequestManager::IssueRequests(
-    TrustedSignalsUrlBuilder& url_builder) {
-  std::set<raw_ptr<RequestImpl, SetExperimental>, CompareRequestImpl>
-      merged_requests = url_builder.TakeMergedRequests();
+    TrustedSignalsUrlBuilder& url_builder,
+    RequestSet merged_requests) {
   DCHECK(!merged_requests.empty());
   BatchedTrustedSignalsRequest* batched_request =
       batched_requests_
@@ -473,6 +484,7 @@
         base::BindOnce(&TrustedSignalsRequestManager::OnSignalsLoaded,
                        base::Unretained(this), batched_request));
   }
+  url_builder.Reset();
 }
 
 void TrustedSignalsRequestManager::StartBatchedTrustedSignalsRequest() {
@@ -594,29 +606,38 @@
 
   base::ElapsedTimer compute_batch_cost;
 
-  std::unique_ptr<TrustedSignalsUrlBuilder> url_builder;
   bool split_fetch = base::FeatureList::IsEnabled(
       features::kFledgeSplitTrustedSignalsFetchingURL);
+
+  RequestSet merged_requests;
   if (type_ == Type::kBiddingSignals) {
-    url_builder = std::make_unique<TrustedBiddingSignalsUrlBuilder>(
+    TrustedBiddingSignalsUrlBuilder bidding_url_builder(
         top_level_origin_.host(), trusted_signals_url_, experiment_group_id_,
         trusted_bidding_signals_slot_size_param_, split_fetch);
+    for (auto& request : queued_requests_) {
+      if (!TryToAddRequest(bidding_url_builder, merged_requests, request)) {
+        // The url got too big so split out what we already have.
+        IssueRequests(bidding_url_builder, std::move(merged_requests));
+        merged_requests.clear();
+        TryToAddRequest(bidding_url_builder, merged_requests, request);
+      }
+    }
+    IssueRequests(bidding_url_builder, std::move(merged_requests));
   } else {
-    url_builder = std::make_unique<TrustedScoringSignalsUrlBuilder>(
+    TrustedScoringSignalsUrlBuilder scoring_url_builder(
         top_level_origin_.host(), trusted_signals_url_, experiment_group_id_,
         send_creative_scanning_metadata_, split_fetch);
-  }
-
-  for (auto& request : queued_requests_) {
-    if (!url_builder->TryToAddRequest(request)) {
-      // The url got too big so split out what we already have.
-      IssueRequests(*url_builder.get());
-      url_builder->Reset();
-      url_builder->TryToAddRequest(request);
+    for (auto& request : queued_requests_) {
+      if (!TryToAddRequest(scoring_url_builder, merged_requests, request)) {
+        // The url got too big so split out what we already have.
+        IssueRequests(scoring_url_builder, std::move(merged_requests));
+        merged_requests.clear();
+        TryToAddRequest(scoring_url_builder, merged_requests, request);
+      }
     }
+    IssueRequests(scoring_url_builder, std::move(merged_requests));
   }
 
-  IssueRequests(*url_builder.get());
   queued_requests_.clear();
 
   base::UmaHistogramMicrosecondsTimes(
@@ -653,8 +674,8 @@
 
 TrustedSignalsRequestManager::RequestImpl::RequestImpl(
     TrustedSignalsRequestManager* trusted_signals_request_manager,
-    TrustedSignals::CreativeInfo ad,
-    std::set<TrustedSignals::CreativeInfo> ad_components,
+    CreativeInfo ad,
+    std::set<CreativeInfo> ad_components,
     int32_t max_trusted_scoring_signals_url_length,
     LoadSignalsCallback load_signals_callback)
     : ad_(std::move(ad)),
@@ -686,8 +707,8 @@
 
 TrustedSignalsRequestManager::RequestImpl::RequestImpl(
     TrustedSignalsRequestManager* trusted_signals_request_manager,
-    TrustedSignals::CreativeInfo ad,
-    std::set<TrustedSignals::CreativeInfo> ad_components,
+    CreativeInfo ad,
+    std::set<CreativeInfo> ad_components,
     const url::Origin& bidder_owner_origin,
     const url::Origin& bidder_joining_origin,
     LoadSignalsCallback load_signals_callback)
diff --git a/content/services/auction_worklet/trusted_signals_request_manager.h b/content/services/auction_worklet/trusted_signals_request_manager.h
index ad69da8c..9b24379a 100644
--- a/content/services/auction_worklet/trusted_signals_request_manager.h
+++ b/content/services/auction_worklet/trusted_signals_request_manager.h
@@ -21,6 +21,7 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "content/common/content_export.h"
+#include "content/services/auction_worklet/public/cpp/creative_info.h"
 #include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
 #include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
 #include "content/services/auction_worklet/trusted_kvv2_signals.h"
@@ -101,7 +102,7 @@
   // whether additional fields like creative_scanning_metadata, size and owner
   // info are sent with trusted scoring requests. If this is off, this
   // information should not be included in the passed in
-  // TrustedSignals::CreativeInfo.
+  // CreativeInfo.
   //
   // TODO(crbug.com/40810962): Investigate improving the
   // `automatically_send_requests` logic.
@@ -143,8 +144,8 @@
   // the format matches the one accepted by ScoringSignals::Result, which
   // minimizes conversions.
   std::unique_ptr<Request> RequestScoringSignals(
-      TrustedSignals::CreativeInfo ad,
-      std::set<TrustedSignals::CreativeInfo> ad_components,
+      CreativeInfo ad,
+      std::set<CreativeInfo> ad_components,
       int32_t max_trusted_scoring_signals_url_length,
       LoadSignalsCallback load_signals_callback);
 
@@ -162,8 +163,8 @@
   // support. Requires `bidder_owner_origin` and `bidder_joining_origin` instead
   // of `max_trusted_scoring_signals_url_length`.
   std::unique_ptr<Request> RequestKVv2ScoringSignals(
-      TrustedSignals::CreativeInfo ad,
-      std::set<TrustedSignals::CreativeInfo> ad_components,
+      CreativeInfo ad,
+      std::set<CreativeInfo> ad_components,
       const url::Origin& bidder_owner_origin,
       const url::Origin& bidder_joining_origin,
       LoadSignalsCallback load_signals_callback);
@@ -192,8 +193,8 @@
     // Constructor for the BYOS version of trusted scoring signals, which builds
     // a GET request with a limit set by max_trusted_scoring_signals_url_length.
     RequestImpl(TrustedSignalsRequestManager* trusted_signals_request_manager,
-                TrustedSignals::CreativeInfo ad,
-                std::set<TrustedSignals::CreativeInfo> ad_components,
+                CreativeInfo ad,
+                std::set<CreativeInfo> ad_components,
                 int32_t max_trusted_scoring_signals_url_length,
                 LoadSignalsCallback load_signals_callback);
 
@@ -209,8 +210,8 @@
     // Constructor for trusted scoring signals with KVv2 support, which builds a
     // POST request.
     RequestImpl(TrustedSignalsRequestManager* trusted_signals_request_manager,
-                TrustedSignals::CreativeInfo ad,
-                std::set<TrustedSignals::CreativeInfo> ad_components,
+                CreativeInfo ad,
+                std::set<CreativeInfo> ad_components,
                 const url::Origin& bidder_owner_origin,
                 const url::Origin& bidder_joining_origin,
                 LoadSignalsCallback load_signals_callback);
@@ -235,8 +236,8 @@
     // Used for requests for scoring signals. `ad_` must be non-null for
     // scoring signals requests. `ad_` and `ad_components_` must be nullopt and
     // empty respectively for bidding signals requests.
-    std::optional<TrustedSignals::CreativeInfo> ad_;
-    std::set<TrustedSignals::CreativeInfo> ad_components_;
+    std::optional<CreativeInfo> ad_;
+    std::set<CreativeInfo> ad_components_;
     std::optional<url::Origin> bidder_owner_origin_;
     std::optional<url::Origin> bidder_joining_origin_;
 
@@ -269,6 +270,9 @@
     bool operator()(const RequestImpl* r1, const RequestImpl* r2) const;
   };
 
+  using RequestSet =
+      std::set<raw_ptr<RequestImpl, SetExperimental>, CompareRequestImpl>;
+
   // Manages building and loading trusted signals URLs.
   class TrustedSignalsUrlBuilder;
   class TrustedBiddingSignalsUrlBuilder;
@@ -290,8 +294,7 @@
     std::unique_ptr<TrustedKVv2Signals> trusted_kvv2_signals;
 
     // The batched Requests this is for.
-    std::set<raw_ptr<RequestImpl, SetExperimental>, CompareRequestImpl>
-        requests;
+    RequestSet requests;
   };
 
   // Adds `request` to `queued_requests_`, and starts `timer_` if needed.
@@ -315,7 +318,20 @@
   // request with it, cancelling the request if it's no longer needed.
   void OnRequestDestroyed(RequestImpl* request);
 
-  void IssueRequests(TrustedSignalsUrlBuilder& url_builder);
+  // Add `request` to `merged_requests` if the relevant
+  // TrustedBiddingSignalsUrlBuilder has room for it. Returns whether the
+  // request was successfully added.
+  bool TryToAddRequest(TrustedBiddingSignalsUrlBuilder& bidding_url_builder,
+                       RequestSet& merged_requests,
+                       RequestImpl* request);
+
+  bool TryToAddRequest(TrustedScoringSignalsUrlBuilder& scoring_url_builder,
+                       RequestSet& merged_requests,
+                       RequestImpl* request);
+
+  // Starts a batched request. Resets `url_builder`.
+  void IssueRequests(TrustedSignalsUrlBuilder& url_builder,
+                     RequestSet merged_requests);
 
   const Type type_;
   const raw_ptr<network::mojom::URLLoaderFactory> url_loader_factory_;
@@ -333,8 +349,7 @@
 
   // All live requests that haven't yet been assigned to a
   // BatchedTrustedSignalsRequest.
-  std::set<raw_ptr<RequestImpl, SetExperimental>, CompareRequestImpl>
-      queued_requests_;
+  RequestSet queued_requests_;
 
   std::set<std::unique_ptr<BatchedTrustedSignalsRequest>,
            base::UniquePtrComparator>
diff --git a/content/services/auction_worklet/trusted_signals_request_manager_unittest.cc b/content/services/auction_worklet/trusted_signals_request_manager_unittest.cc
index 9a324661..fd997a56 100644
--- a/content/services/auction_worklet/trusted_signals_request_manager_unittest.cc
+++ b/content/services/auction_worklet/trusted_signals_request_manager_unittest.cc
@@ -32,6 +32,7 @@
 #include "content/services/auction_worklet/auction_v8_helper.h"
 #include "content/services/auction_worklet/public/cpp/auction_worklet_features.h"
 #include "content/services/auction_worklet/public/cpp/cbor_test_util.h"
+#include "content/services/auction_worklet/public/cpp/creative_info.h"
 #include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom-forward.h"
 #include "content/services/auction_worklet/trusted_signals.h"
 #include "content/services/auction_worklet/worklet_test_util.h"
@@ -260,13 +261,13 @@
       kKeyId);
 }
 
-std::set<TrustedSignals::CreativeInfo> ToCreativeInfo(
+std::set<CreativeInfo> ToCreativeInfo(
     const std::vector<std::string>& render_urls) {
   return CreateCreativeInfoSet(render_urls);
 }
 
-TrustedSignals::CreativeInfo ToCreativeInfo(const GURL& url) {
-  TrustedSignals::CreativeInfo ad;
+CreativeInfo ToCreativeInfo(const GURL& url) {
+  CreativeInfo ad;
   ad.ad_descriptor.url = url;
   return ad;
 }
@@ -3895,8 +3896,8 @@
   }
 
  protected:
-  std::vector<TrustedSignals::CreativeInfo> ads_;
-  std::vector<TrustedSignals::CreativeInfo> ad_components_;
+  std::vector<CreativeInfo> ads_;
+  std::vector<CreativeInfo> ad_components_;
   url::Origin joining_origin_{url::Origin::Create(GURL(kJoiningOriginA))};
 };
 
diff --git a/content/services/auction_worklet/trusted_signals_unittest.cc b/content/services/auction_worklet/trusted_signals_unittest.cc
index 8305a0c5b..91d28bb 100644
--- a/content/services/auction_worklet/trusted_signals_unittest.cc
+++ b/content/services/auction_worklet/trusted_signals_unittest.cc
@@ -24,6 +24,7 @@
 #include "base/test/values_test_util.h"
 #include "content/common/features.h"
 #include "content/services/auction_worklet/auction_v8_helper.h"
+#include "content/services/auction_worklet/public/cpp/creative_info.h"
 #include "content/services/auction_worklet/worklet_test_util.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/http/http_status_code.h"
@@ -295,8 +296,8 @@
   }
 
   scoped_refptr<TrustedSignals::Result> FetchScoringSignals(
-      std::set<TrustedSignals::CreativeInfo> ads,
-      std::set<TrustedSignals::CreativeInfo> ad_components,
+      std::set<CreativeInfo> ads,
+      std::set<CreativeInfo> ad_components,
       const std::string& hostname,
       std::optional<uint16_t> experiment_group_id,
       bool send_creative_scanning_metadata = false) {
@@ -1457,7 +1458,7 @@
   for (const bool send_creative_scanning_metadata : {false, true}) {
     SCOPED_TRACE(send_creative_scanning_metadata);
 
-    auto test_data = std::to_array<TrustedSignals::CreativeInfo>(
+    auto test_data = std::to_array<CreativeInfo>(
         {{/*ad_descriptor=*/blink::AdDescriptor(
               GURL("https://creative1.test"),
               std::optional(
@@ -1505,7 +1506,7 @@
           std::optional(url::Origin::Create(GURL("https://bidder2.test"))),
           /*buyer_and_seller_reporting_id=*/"stool"}});
 
-    std::set<TrustedSignals::CreativeInfo> creative_set;
+    std::set<CreativeInfo> creative_set;
     for (auto& input : test_data) {
       if (!send_creative_scanning_metadata) {
         input.ad_descriptor.size = std::nullopt;
@@ -1518,8 +1519,8 @@
 
     // Minimal valid input for main ad, to use when mainly testing component-ad
     // specific query params..
-    std::set<TrustedSignals::CreativeInfo> single_ad_set;
-    TrustedSignals::CreativeInfo single_ad;
+    std::set<CreativeInfo> single_ad_set;
+    CreativeInfo single_ad;
     single_ad.ad_descriptor.url = GURL("https://product.test");
     if (send_creative_scanning_metadata) {
       single_ad.interest_group_owner =
@@ -1614,16 +1615,16 @@
 TEST_F(TrustedSignalsTest, BuildTrustedScoringSignalsURLNoSize) {
   for (const bool both_without_size : {false, true}) {
     SCOPED_TRACE(both_without_size);
-    std::set<TrustedSignals::CreativeInfo> input;
+    std::set<CreativeInfo> input;
 
-    input.insert(TrustedSignals::CreativeInfo(
+    input.insert(CreativeInfo(
         /*ad_descriptor=*/blink::AdDescriptor(GURL("https://c1.test"),
                                               /*size=*/std::nullopt),
         /*creative_scanning_metadata=*/"s1",
         /*interest_group_owner=*/url::Origin::Create(GURL("https://b1.test")),
         /*buyer_and_seller_reporting_id=*/"stool"));
 
-    input.insert(TrustedSignals::CreativeInfo(
+    input.insert(CreativeInfo(
         /*ad_descriptor=*/blink::AdDescriptor(
             GURL("https://c2.test"),
             /*size=*/both_without_size
@@ -1670,9 +1671,9 @@
 
 TEST_F(TrustedSignalsTest,
        BuildTrustedScoringSignalsURLEmptyCreativeScanMetadata) {
-  std::set<TrustedSignals::CreativeInfo> input;
+  std::set<CreativeInfo> input;
 
-  input.insert(TrustedSignals::CreativeInfo(
+  input.insert(CreativeInfo(
       /*ad_descriptor=*/blink::AdDescriptor(
           GURL("https://c1.test"),
           blink::AdSize(100, blink::AdSize::LengthUnit::kPixels, 50,
@@ -1681,7 +1682,7 @@
       /*interest_group_owner=*/url::Origin::Create(GURL("https://b1.test")),
       /*buyer_and_seller_reporting_id=*/"stool"));
 
-  input.insert(TrustedSignals::CreativeInfo(
+  input.insert(CreativeInfo(
       /*ad_descriptor=*/blink::AdDescriptor(
           GURL("https://c2.test"),
           blink::AdSize(100, blink::AdSize::LengthUnit::kPixels, 50,
@@ -1712,8 +1713,8 @@
 }
 
 TEST_F(TrustedSignalsTest, ScoringSignalsCreativeScanning) {
-  std::set<TrustedSignals::CreativeInfo> ads;
-  ads.insert(TrustedSignals::CreativeInfo(
+  std::set<CreativeInfo> ads;
+  ads.insert(CreativeInfo(
       /*ad_descriptor=*/blink::AdDescriptor(
           GURL("https://foo.test"),
           blink::AdSize(100, blink::AdSize::LengthUnit::kPixels, 50,
@@ -1722,7 +1723,7 @@
       /*interest_group_owner=*/url::Origin::Create(GURL("https://b1.test")),
       /*buyer_and_seller_reporting_id=*/"stool"));
 
-  ads.insert(TrustedSignals::CreativeInfo(
+  ads.insert(CreativeInfo(
       /*ad_descriptor=*/blink::AdDescriptor(
           GURL("https://foo.test"),
           blink::AdSize(100, blink::AdSize::LengthUnit::kPixels, 50,
@@ -1731,14 +1732,14 @@
       /*interest_group_owner=*/url::Origin::Create(GURL("https://b2.test")),
       /*buyer_and_seller_reporting_id=*/"sofa"));
 
-  ads.insert(TrustedSignals::CreativeInfo(
+  ads.insert(CreativeInfo(
       /*ad_descriptor=*/blink::AdDescriptor(GURL("https://bar.test")),
       /*creative_scanning_metadata=*/"s3",
       /*interest_group_owner=*/url::Origin::Create(GURL("https://b2.test")),
       /*buyer_and_seller_reporting_id=*/"chair"));
 
-  std::set<TrustedSignals::CreativeInfo> ad_components;
-  ad_components.insert(TrustedSignals::CreativeInfo(
+  std::set<CreativeInfo> ad_components;
+  ad_components.insert(CreativeInfo(
       /*ad_descriptor=*/blink::AdDescriptor(
           GURL("https://foosub.test"),
           blink::AdSize(30, blink::AdSize::LengthUnit::kPixels, 16,
@@ -1747,7 +1748,7 @@
       /*interest_group_owner=*/url::Origin::Create(GURL("https://b1.test")),
       /*buyer_and_seller_reporting_id=*/std::string()));
 
-  ad_components.insert(TrustedSignals::CreativeInfo(
+  ad_components.insert(CreativeInfo(
       /*ad_descriptor=*/blink::AdDescriptor(
           GURL("https://barsub.test"),
           blink::AdSize(60, blink::AdSize::LengthUnit::kPixels, 32,
@@ -2050,7 +2051,7 @@
   const GURL kBaseURL("https://tkv.com/");
   const uint16_t kExperiment = 123;
 
-  TrustedSignals::CreativeInfo m1(
+  CreativeInfo m1(
       /*ad_descriptor=*/blink::AdDescriptor(
           GURL("https://creative1.test"),
           std::optional(
@@ -2061,7 +2062,7 @@
       std::optional(url::Origin::Create(GURL("https://bidder1.test"))),
       /*buyer_and_seller_reporting_id=*/"chair");
 
-  TrustedSignals::CreativeInfo m2(
+  CreativeInfo m2(
       /*ad_descriptor=*/blink::AdDescriptor(
           GURL("https://creative1.test"),
           std::optional(
@@ -2072,14 +2073,14 @@
       std::optional(url::Origin::Create(GURL("https://bidder1.test"))),
       /*buyer_and_seller_reporting_id=*/"sofa");
 
-  TrustedSignals::CreativeInfo m3(
+  CreativeInfo m3(
       /*ad_descriptor=*/blink::AdDescriptor(GURL("https://creative2.test")),
       /*creative_scanning_metadata=*/"scan2",
       /*interest_group_owner=*/
       std::optional(url::Origin::Create(GURL("https://bidder2.test"))),
       /*buyer_and_seller_reporting_id=*/"stool");
 
-  TrustedSignals::CreativeInfo c1(
+  CreativeInfo c1(
       /*ad_descriptor=*/blink::AdDescriptor(
           GURL("https://c1.test"),
           std::optional(
@@ -2090,7 +2091,7 @@
       std::optional(url::Origin::Create(GURL("https://c.bidder1.test"))),
       /*buyer_and_seller_reporting_id=*/"c.chair");
 
-  TrustedSignals::CreativeInfo c2(
+  CreativeInfo c2(
       /*ad_descriptor=*/blink::AdDescriptor(
           GURL("https://c2.test"),
           std::optional(
@@ -2101,7 +2102,7 @@
       std::optional(url::Origin::Create(GURL("https://c.bidder1.test"))),
       /*buyer_and_seller_reporting_id=*/"c.sofa");
 
-  TrustedSignals::CreativeInfo c3(
+  CreativeInfo c3(
       /*ad_descriptor=*/blink::AdDescriptor(GURL("https://c3.test")),
       /*creative_scanning_metadata=*/"cs3",
       /*interest_group_owner=*/
diff --git a/content/services/auction_worklet/worklet_test_util.cc b/content/services/auction_worklet/worklet_test_util.cc
index 71b0d685..2355d89 100644
--- a/content/services/auction_worklet/worklet_test_util.cc
+++ b/content/services/auction_worklet/worklet_test_util.cc
@@ -16,6 +16,7 @@
 #include "content/public/test/shared_storage_test_utils.h"
 #include "content/services/auction_worklet/auction_v8_helper.h"
 #include "content/services/auction_worklet/public/cpp/auction_downloader.h"
+#include "content/services/auction_worklet/public/cpp/creative_info.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
@@ -273,11 +274,11 @@
   return observed_requests_;
 }
 
-std::set<TrustedSignals::CreativeInfo> CreateCreativeInfoSet(
+std::set<CreativeInfo> CreateCreativeInfoSet(
     const std::vector<std::string>& urls) {
-  std::set<TrustedSignals::CreativeInfo> result;
+  std::set<CreativeInfo> result;
   for (const auto& url : urls) {
-    TrustedSignals::CreativeInfo entry;
+    CreativeInfo entry;
     entry.ad_descriptor.url = GURL(url);
     result.insert(std::move(entry));
   }
diff --git a/content/services/auction_worklet/worklet_test_util.h b/content/services/auction_worklet/worklet_test_util.h
index 2e9ca48..c1ab954 100644
--- a/content/services/auction_worklet/worklet_test_util.h
+++ b/content/services/auction_worklet/worklet_test_util.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/types/optional_ref.h"
+#include "content/services/auction_worklet/public/cpp/creative_info.h"
 #include "content/services/auction_worklet/public/mojom/auction_network_events_handler.mojom.h"
 #include "content/services/auction_worklet/public/mojom/auction_shared_storage_host.mojom.h"
 #include "content/services/auction_worklet/public/mojom/seller_worklet.mojom.h"
@@ -189,7 +190,7 @@
 
 // A helper to make testing common cases in trusted seller signals easier, by
 // construsting the larger type it needs from simpler string input.
-std::set<TrustedSignals::CreativeInfo> CreateCreativeInfoSet(
+std::set<CreativeInfo> CreateCreativeInfoSet(
     const std::vector<std::string>& urls);
 std::vector<mojom::CreativeInfoWithoutOwnerPtr>
 CreateMojoCreativeInfoWithoutOwnerVector(const std::vector<std::string>& urls);
diff --git a/docs/android_dynamic_feature_modules.md b/docs/android_dynamic_feature_modules.md
index 3eec02f..685590b 100644
--- a/docs/android_dynamic_feature_modules.md
+++ b/docs/android_dynamic_feature_modules.md
@@ -677,11 +677,6 @@
 java_strings_grd("java_strings_grd") {
   defines = chrome_grit_defines
   grd_file = "android/resources/strings/android_foo_strings.grd"
-  outputs = [
-    "values-am/android_foo_strings.xml",
-    # Here, too, list output files for other supported languages.
-    ...
-  ]
 }
 ...
 android_resources("java_resources") {
diff --git a/docs/updater/functional_spec.md b/docs/updater/functional_spec.md
index 6c5bb875..101a956 100644
--- a/docs/updater/functional_spec.md
+++ b/docs/updater/functional_spec.md
@@ -368,6 +368,11 @@
       * Installs the application(s) that are implicitly specified in the tagged
         metainstaller, or explicitly specified using the `--install` or
         `--handoff` parameters.
+*   `RUNFORCEINSTALL` (for MSI installers)
+    * Allows running an MSI metainstaller with the `--force-install` option.
+    * In addition, if the MSI is tagged, this also installs the application(s)
+      that are implicitly specified in the tag.
+    * For example, `msiexec /i GoogleChrome.msi RUNFORCEINSTALL=1`.
 *   --test
     *   Exit immediately with no error.
 *   --healthcheck
diff --git a/gpu/command_buffer/service/dawn_context_provider.cc b/gpu/command_buffer/service/dawn_context_provider.cc
index 3f68b1c..fdf1be68 100644
--- a/gpu/command_buffer/service/dawn_context_provider.cc
+++ b/gpu/command_buffer/service/dawn_context_provider.cc
@@ -932,11 +932,16 @@
         ->AddScalar(MemoryAllocatorDump::kNameSize,
                     MemoryAllocatorDump::kUnitsBytes,
                     mem_usage.depthStencilTexturesUsage);
-    pmd->GetOrCreateAllocatorDump(
-           base::JoinString({kDawnMemoryDumpPrefix, "textures/msaa"}, "/"))
-        ->AddScalar(MemoryAllocatorDump::kNameSize,
-                    MemoryAllocatorDump::kUnitsBytes,
-                    mem_usage.msaaTexturesUsage);
+    auto* msaa_dump = pmd->GetOrCreateAllocatorDump(
+        base::JoinString({kDawnMemoryDumpPrefix, "textures/msaa"}, "/"));
+    msaa_dump->AddScalar(MemoryAllocatorDump::kNameSize,
+                         MemoryAllocatorDump::kUnitsBytes,
+                         mem_usage.msaaTexturesUsage);
+    msaa_dump->AddScalar(MemoryAllocatorDump::kNameObjectCount,
+                         MemoryAllocatorDump::kUnitsObjects,
+                         mem_usage.msaaTexturesCount);
+    msaa_dump->AddScalar("biggest_size", MemoryAllocatorDump::kUnitsBytes,
+                         mem_usage.largestMsaaTextureUsage);
     pmd->GetOrCreateAllocatorDump(
            base::JoinString({kDawnMemoryDumpPrefix, "buffers"}, "/"))
         ->AddScalar(MemoryAllocatorDump::kNameSize,
diff --git "a/infra/config/generated/builders/ci/GPU FYI Mac Builder \050dbg\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/GPU FYI Mac Builder \050dbg\051/targets/chromium.gpu.fyi.json"
index 48d01b74..8dedf82 100644
--- "a/infra/config/generated/builders/ci/GPU FYI Mac Builder \050dbg\051/targets/chromium.gpu.fyi.json"
+++ "b/infra/config/generated/builders/ci/GPU FYI Mac Builder \050dbg\051/targets/chromium.gpu.fyi.json"
@@ -357,7 +357,8 @@
           "hard_timeout": 1800,
           "idempotent": false,
           "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "telemetry_gpu_integration_test",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
@@ -854,7 +855,8 @@
           "hard_timeout": 1800,
           "idempotent": false,
           "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "telemetry_gpu_integration_test",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
diff --git "a/infra/config/generated/builders/ci/Mac FYI Debug \050Intel\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/Mac FYI Debug \050Intel\051/targets/chromium.gpu.fyi.json"
index 6611bfb..60c8df0 100644
--- "a/infra/config/generated/builders/ci/Mac FYI Debug \050Intel\051/targets/chromium.gpu.fyi.json"
+++ "b/infra/config/generated/builders/ci/Mac FYI Debug \050Intel\051/targets/chromium.gpu.fyi.json"
@@ -356,7 +356,8 @@
           "hard_timeout": 1800,
           "idempotent": false,
           "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "telemetry_gpu_integration_test",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
diff --git "a/infra/config/generated/builders/ci/Mac FYI Retina Debug \050AMD\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/Mac FYI Retina Debug \050AMD\051/targets/chromium.gpu.fyi.json"
index 17239298..c71e8cd 100644
--- "a/infra/config/generated/builders/ci/Mac FYI Retina Debug \050AMD\051/targets/chromium.gpu.fyi.json"
+++ "b/infra/config/generated/builders/ci/Mac FYI Retina Debug \050AMD\051/targets/chromium.gpu.fyi.json"
@@ -378,7 +378,8 @@
           "hard_timeout": 1800,
           "idempotent": false,
           "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "telemetry_gpu_integration_test",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-mac-amd-retina-dbg/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/try/gpu-fyi-try-mac-amd-retina-dbg/targets/chromium.gpu.fyi.json
index aba8e6b7..6158d58 100644
--- a/infra/config/generated/builders/try/gpu-fyi-try-mac-amd-retina-dbg/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/try/gpu-fyi-try-mac-amd-retina-dbg/targets/chromium.gpu.fyi.json
@@ -379,7 +379,8 @@
           "hard_timeout": 1800,
           "idempotent": false,
           "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "telemetry_gpu_integration_test",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-mac-intel-dbg/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/try/gpu-fyi-try-mac-intel-dbg/targets/chromium.gpu.fyi.json
index 1976e01..50c43af5 100644
--- a/infra/config/generated/builders/try/gpu-fyi-try-mac-intel-dbg/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/try/gpu-fyi-try-mac-intel-dbg/targets/chromium.gpu.fyi.json
@@ -357,7 +357,8 @@
           "hard_timeout": 1800,
           "idempotent": false,
           "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
         },
         "test": "telemetry_gpu_integration_test",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
diff --git a/infra/config/generated/testing/mixins.pyl b/infra/config/generated/testing/mixins.pyl
index af5dfce..dd9fc79 100644
--- a/infra/config/generated/testing/mixins.pyl
+++ b/infra/config/generated/testing/mixins.pyl
@@ -422,12 +422,12 @@
       },
     },
   },
-  'ios_runtime_cache_18_0': {
+  'ios_runtime_cache_18_2': {
     'swarming': {
       'named_caches': [
         {
-          'name': 'runtime_ios_18_0',
-          'path': 'Runtime-ios-18.0',
+          'name': 'runtime_ios_18_2',
+          'path': 'Runtime-ios-18.2',
         },
       ],
     },
diff --git a/infra/config/generated/testing/test_suites.pyl b/infra/config/generated/testing/test_suites.pyl
index 2012dfe44..8804a46 100644
--- a/infra/config/generated/testing/test_suites.pyl
+++ b/infra/config/generated/testing/test_suites.pyl
@@ -2642,7 +2642,7 @@
     'optimization_guide_ios_sim_gtests': {
       'optimization_guide_ios_unittests': {
         'variants': [
-          'SIM_IPHONE_14_18_0',
+          'SIM_IPHONE_14_18_2',
         ],
       },
     },
diff --git a/infra/config/generated/testing/variants.pyl b/infra/config/generated/testing/variants.pyl
index 18d043d..255f6f7 100644
--- a/infra/config/generated/testing/variants.pyl
+++ b/infra/config/generated/testing/variants.pyl
@@ -86,16 +86,16 @@
       'nvidia_geforce_gtx_1660',
     ],
   },
-  'SIM_IPHONE_14_18_0': {
-    'identifier': 'iPhone 14 18.0',
+  'SIM_IPHONE_14_18_2': {
+    'identifier': 'iPhone 14 18.2',
     'args': [
       '--platform',
       'iPhone 14',
       '--version',
-      '18.0',
+      '18.2',
     ],
     'mixins': [
-      'ios_runtime_cache_18_0',
+      'ios_runtime_cache_18_2',
     ],
   },
 }
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
index 00a7674..f4df5b1 100644
--- a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
@@ -1691,6 +1691,13 @@
         mixins = [
             "mac_mini_intel_gpu_stable",
         ],
+        per_test_modifications = {
+            "pixel_skia_gold_gl_passthrough_ganesh_test": targets.mixin(
+                swarming = targets.swarming(
+                    shards = 2,
+                ),
+            ),
+        },
     ),
     targets_settings = targets.settings(
         browser_config = targets.browser_config.DEBUG,
@@ -2305,6 +2312,13 @@
         mixins = [
             "mac_retina_amd_gpu_stable",
         ],
+        per_test_modifications = {
+            "pixel_skia_gold_gl_passthrough_ganesh_test": targets.mixin(
+                swarming = targets.swarming(
+                    shards = 2,
+                ),
+            ),
+        },
     ),
     targets_settings = targets.settings(
         browser_config = targets.browser_config.DEBUG,
diff --git a/infra/config/targets/matrix_compound_suites.star b/infra/config/targets/matrix_compound_suites.star
index 9a930eb..08ad7b84 100644
--- a/infra/config/targets/matrix_compound_suites.star
+++ b/infra/config/targets/matrix_compound_suites.star
@@ -442,7 +442,7 @@
     basic_suites = {
         "optimization_guide_ios_unittests": targets.legacy_matrix_config(
             variants = [
-                "SIM_IPHONE_14_18_0",
+                "SIM_IPHONE_14_18_2",
             ],
         ),
     },
diff --git a/infra/config/targets/mixins.star b/infra/config/targets/mixins.star
index 4e15f2b..a2859e8 100644
--- a/infra/config/targets/mixins.star
+++ b/infra/config/targets/mixins.star
@@ -1211,18 +1211,6 @@
 )
 
 targets.mixin(
-    name = "ios_runtime_cache_18_0",
-    swarming = targets.swarming(
-        named_caches = [
-            swarming.cache(
-                name = "runtime_ios_18_0",
-                path = "Runtime-ios-18.0",
-            ),
-        ],
-    ),
-)
-
-targets.mixin(
     name = "ios_runtime_cache_18_1",
     generate_pyl_entry = False,
     swarming = targets.swarming(
@@ -1237,7 +1225,6 @@
 
 targets.mixin(
     name = "ios_runtime_cache_18_2",
-    generate_pyl_entry = False,
     swarming = targets.swarming(
         named_caches = [
             swarming.cache(
diff --git a/infra/config/targets/variants.star b/infra/config/targets/variants.star
index df01cc7..5c02c146 100644
--- a/infra/config/targets/variants.star
+++ b/infra/config/targets/variants.star
@@ -432,23 +432,8 @@
 )
 
 targets.variant(
-    name = "SIM_IPHONE_14_18_0",
-    identifier = "iPhone 14 18.0",
-    mixins = [
-        "ios_runtime_cache_18_0",
-    ],
-    args = [
-        "--platform",
-        "iPhone 14",
-        "--version",
-        "18.0",
-    ],
-)
-
-targets.variant(
     name = "SIM_IPHONE_14_18_2",
     identifier = "iPhone 14 18.2",
-    generate_pyl_entry = False,
     mixins = [
         "ios_runtime_cache_18_2",
     ],
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 94a8b2ce..677e1246 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -6880,6 +6880,9 @@
       <message name="IDS_IOS_TOOLS_MENU_HELP_URL" translateable="false">
         https://support.google.com/chrome/?p=chrome_help&amp;hl=[GRITLANGCODE]&amp;ios=1
       </message>
+      <message name="IDS_IOS_TOOLS_MENU_HIDE_READER_MODE" desc="The iOS menu item for closing the Reader Mode UI displayed for the current page. [iOS only]" meaning="[Length: 16em] [iOS only] This is a feature name in title case.">
+        Hide Reader Mode
+      </message>
       <message name="IDS_IOS_TOOLS_MENU_HISTORY" desc="The Tools menu item for opening the navigation history menu" meaning="[Length: 16em] [iOS only] This is a feature name in title case.">
         History
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TOOLS_MENU_HIDE_READER_MODE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TOOLS_MENU_HIDE_READER_MODE.png.sha1
new file mode 100644
index 0000000..256c4c7
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_TOOLS_MENU_HIDE_READER_MODE.png.sha1
@@ -0,0 +1 @@
+12653473792b81030d29acefde56237f47cfdc4d
\ No newline at end of file
diff --git a/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent.mm b/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent.mm
index a82d5353..c1ea749 100644
--- a/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent.mm
+++ b/ios/chrome/browser/app_launcher/model/app_launcher_browser_agent.mm
@@ -153,7 +153,8 @@
 #pragma mark - AppLauncherBrowserAgent
 
 AppLauncherBrowserAgent::AppLauncherBrowserAgent(Browser* browser)
-    : tab_helper_delegate_(browser),
+    : BrowserUserData(browser),
+      tab_helper_delegate_(browser),
       tab_helper_delegate_installer_(&tab_helper_delegate_, browser) {
   browser->AddObserver(this);
   app_launcher_scene_state_observer_ = [[AppLauncherSceneStateObserver alloc]
diff --git a/ios/chrome/browser/authentication/ui_bundled/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/BUILD.gn
index ad8c46f..c461c19 100644
--- a/ios/chrome/browser/authentication/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/authentication/ui_bundled/BUILD.gn
@@ -292,8 +292,8 @@
     "//components/policy/core/browser",
     "//components/signin/public/base:signin_switches",
     "//ios/chrome/app/strings",
+    "//ios/chrome/browser/authentication/ui_bundled/account_menu:constants",
     "//ios/chrome/browser/authentication/ui_bundled/signin:constants",
-    "//ios/chrome/browser/authentication/ui_bundled/signin/account_menu:constants",
     "//ios/chrome/browser/first_run/ui_bundled:constants",
     "//ios/chrome/browser/ntp/ui_bundled:constants",
     "//ios/chrome/browser/ntp/ui_bundled:feature_flags",
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/account_menu/BUILD.gn
similarity index 92%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/BUILD.gn
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/BUILD.gn
index 87669fb..ab6c2b5 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/BUILD.gn
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/BUILD.gn
@@ -8,43 +8,46 @@
   sources = [
     "account_menu_coordinator.h",
     "account_menu_coordinator.mm",
+    "account_menu_coordinator_delegate.h",
     "account_menu_mediator.h",
     "account_menu_mediator.mm",
     "account_menu_mediator_delegate.h",
   ]
+  public_deps = [
+    ":constants",
+    "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
+  ]
   deps = [
     ":account_menu_ui",
-    ":constants",
     "//base",
+    "//components/prefs",
+    "//components/signin/public/base",
     "//components/signin/public/identity_manager/objc",
     "//components/sync/service",
+    "//components/trusted_vault",
     "//ios/chrome/app:change_profile_commands",
     "//ios/chrome/app/application_delegate:app_state",
     "//ios/chrome/app/profile",
-    "//ios/chrome/app/strings:ios_strings_grit",
-    "//ios/chrome/browser/authentication/ui_bundled",
+    "//ios/chrome/app/strings",
     "//ios/chrome/browser/authentication/ui_bundled:continuation",
     "//ios/chrome/browser/authentication/ui_bundled/authentication_flow",
     "//ios/chrome/browser/authentication/ui_bundled/authentication_flow:request_helper",
     "//ios/chrome/browser/authentication/ui_bundled/cells",
     "//ios/chrome/browser/authentication/ui_bundled/change_profile",
     "//ios/chrome/browser/authentication/ui_bundled/enterprise:enterprise_utils",
+    "//ios/chrome/browser/authentication/ui_bundled/signin:constants",
     "//ios/chrome/browser/authentication/ui_bundled/signin:signin_headers",
-    "//ios/chrome/browser/authentication/ui_bundled/signin:signin_protected",
-    "//ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin",
     "//ios/chrome/browser/authentication/ui_bundled/signout_action_sheet",
     "//ios/chrome/browser/authentication/ui_bundled/trusted_vault_reauthentication",
     "//ios/chrome/browser/ntp/ui_bundled:feature_flags",
-    "//ios/chrome/browser/policy/model",
     "//ios/chrome/browser/policy/ui_bundled:management_util",
     "//ios/chrome/browser/push_notification/model:push_notification_service_header",
     "//ios/chrome/browser/settings/model/sync/utils:identity_error_util",
     "//ios/chrome/browser/settings/ui_bundled:constants",
     "//ios/chrome/browser/settings/ui_bundled:settings_root",
-    "//ios/chrome/browser/settings/ui_bundled/google_services",
+    "//ios/chrome/browser/settings/ui_bundled/google_services:sync_error_settings_command_handler",
     "//ios/chrome/browser/settings/ui_bundled/google_services/manage_accounts",
     "//ios/chrome/browser/settings/ui_bundled/sync",
-    "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator:animated_coordinator",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
@@ -60,6 +63,7 @@
     "//ios/chrome/browser/signin/model:system_identity_manager",
     "//ios/chrome/browser/sync/model",
     "//ios/third_party/material_components_ios",
+    "//ui/base",
   ]
 }
 
@@ -71,16 +75,14 @@
     "account_menu_view_controller.h",
     "account_menu_view_controller.mm",
   ]
+  public_deps = [ "//ios/chrome/browser/shared/coordinator/chrome_coordinator" ]
   deps = [
     ":constants",
     "//base",
     "//components/strings",
     "//ios/chrome/app/strings",
-    "//ios/chrome/app/strings",
     "//ios/chrome/browser/authentication/ui_bundled/cells",
     "//ios/chrome/browser/keyboard/ui_bundled",
-    "//ios/chrome/browser/keyboard/ui_bundled",
-    "//ios/chrome/browser/keyboard/ui_bundled",
     "//ios/chrome/browser/policy/model",
     "//ios/chrome/browser/settings/model/sync/utils:identity_error_util",
     "//ios/chrome/browser/settings/ui_bundled/cells",
@@ -88,15 +90,11 @@
     "//ios/chrome/browser/shared/ui/symbols",
     "//ios/chrome/browser/shared/ui/table_view",
     "//ios/chrome/browser/shared/ui/table_view:utils",
-    "//ios/chrome/browser/shared/ui/util",
     "//ios/chrome/browser/signin/model:constants",
-    "//ios/chrome/browser/signin/model:system_identity",
     "//ios/chrome/common/ui/colors",
-    "//ios/chrome/common/ui/util",
     "//ios/chrome/common/ui/util:image_util",
     "//ui/base",
   ]
-  public_deps = [ "//ios/chrome/browser/shared/ui/table_view" ]
 }
 
 source_set("constants") {
@@ -104,7 +102,7 @@
     "account_menu_constants.h",
     "account_menu_constants.mm",
   ]
-  deps = []
+  deps = [ "//base" ]
 }
 
 source_set("unit_tests") {
@@ -117,14 +115,13 @@
   deps = [
     ":account_menu",
     ":account_menu_ui",
-    ":constants",
     "//base",
     "//components/sync:test_support",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/authentication/ui_bundled",
     "//ios/chrome/browser/authentication/ui_bundled/authentication_flow",
     "//ios/chrome/browser/authentication/ui_bundled/cells",
-    "//ios/chrome/browser/authentication/ui_bundled/signin:constants",
+    "//ios/chrome/browser/authentication/ui_bundled/signin",
     "//ios/chrome/browser/authentication/ui_bundled/signin:signin_protected",
     "//ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin",
     "//ios/chrome/browser/authentication/ui_bundled/signout_action_sheet",
@@ -132,6 +129,8 @@
     "//ios/chrome/browser/settings/model/sync/utils:identity_error_util",
     "//ios/chrome/browser/settings/ui_bundled:constants",
     "//ios/chrome/browser/settings/ui_bundled/cells",
+    "//ios/chrome/browser/settings/ui_bundled/google_services",
+    "//ios/chrome/browser/settings/ui_bundled/google_services:sync_error_settings_command_handler",
     "//ios/chrome/browser/settings/ui_bundled/sync",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/coordinator/scene/test",
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/DEPS b/ios/chrome/browser/authentication/ui_bundled/account_menu/DEPS
similarity index 100%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/DEPS
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/DEPS
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/OWNERS b/ios/chrome/browser/authentication/ui_bundled/account_menu/OWNERS
similarity index 100%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/OWNERS
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/OWNERS
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h
similarity index 87%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h
index 08fc0d2..f45dd21c 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.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 IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_CONSTANTS_H_
-#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_CONSTANTS_H_
+#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_CONSTANTS_H_
+#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_CONSTANTS_H_
 
 #import <Foundation/Foundation.h>
 
@@ -45,4 +45,4 @@
 // The accessibility ideentifier for the "manage your account" menu entry.
 extern NSString* const kAccountMenuManageYourGoogleAccountId;
 
-#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_CONSTANTS_H_
+#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_CONSTANTS_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.mm b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.mm
similarity index 92%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.mm
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.mm
index 5f446079..c173c5ce 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
 
 NSString* const kAccountMenuTableViewId = @"AccountMenuTableViewId";
 NSString* const kAccountMenuCloseButtonId = @"AccountMenuCloseButtonId";
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_consumer.h b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_consumer.h
similarity index 75%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_consumer.h
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_consumer.h
index 0efd8bc..a9747f6 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_consumer.h
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_consumer.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 IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_CONSUMER_H_
-#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_CONSUMER_H_
+#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_CONSUMER_H_
+#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_CONSUMER_H_
 
 #import <Foundation/Foundation.h>
 
@@ -33,4 +33,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_CONSUMER_H_
+#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_CONSUMER_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator.h
new file mode 100644
index 0000000..bde438d
--- /dev/null
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator.h
@@ -0,0 +1,35 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_COORDINATOR_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h"
+
+enum class AccountMenuAccessPoint;
+@protocol AccountMenuCoordinatorDelegate;
+class GURL;
+
+// Coordinator to display the fast account menu view controller.
+@interface AccountMenuCoordinator : ChromeCoordinator
+
+@property(nonatomic, weak) id<AccountMenuCoordinatorDelegate> delegate;
+
+// `anchorView`: Clicked view, used to anchor the menu to it when using
+// UIModalPresentationPopover mode.
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser
+                                anchorView:(UIView*)anchorView
+                               accessPoint:(AccountMenuAccessPoint)accessPoint
+                                       URL:(const GURL&)url
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_COORDINATOR_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator.mm
similarity index 91%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.mm
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator.mm
index ed250216..06b83007 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator.h"
 
 #import <MaterialComponents/MaterialSnackbar.h>
 
@@ -21,15 +21,13 @@
 #import "ios/chrome/app/application_delegate/app_state.h"
 #import "ios/chrome/app/change_profile_commands.h"
 #import "ios/chrome/app/profile/profile_state.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator_delegate.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator_delegate.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.h"
 #import "ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow.h"
 #import "ios/chrome/browser/authentication/ui_bundled/continuation.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator_delegate.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_coordinator.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator+protected.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signout_action_sheet/signout_action_sheet_coordinator.h"
 #import "ios/chrome/browser/authentication/ui_bundled/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator.h"
@@ -38,6 +36,7 @@
 #import "ios/chrome/browser/push_notification/model/push_notification_service.h"
 #import "ios/chrome/browser/settings/ui_bundled/google_services/manage_accounts/manage_accounts_coordinator.h"
 #import "ios/chrome/browser/settings/ui_bundled/google_services/manage_accounts/manage_accounts_coordinator_delegate.h"
+#import "ios/chrome/browser/settings/ui_bundled/google_services/sync_error_settings_command_handler.h"
 #import "ios/chrome/browser/settings/ui_bundled/settings_controller_protocol.h"
 #import "ios/chrome/browser/settings/ui_bundled/settings_root_view_controlling.h"
 #import "ios/chrome/browser/settings/ui_bundled/sync/sync_encryption_passphrase_table_view_controller.h"
@@ -67,10 +66,27 @@
 #import "ios/chrome/browser/sync/model/sync_service_factory.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ui/base/l10n/l10n_util.h"
+#import "url/gurl.h"
+
+namespace {
+
+// Potentially shows an IPH, informing the user that they can find Settings in
+// the overflow menu. The handler contains the logic for whether to actually
+// show it.
+void maybeShowSettingsIPH(Browser* browser) {
+  CommandDispatcher* dispatcher = browser->GetCommandDispatcher();
+  id<HelpCommands> helpCommandsHandler =
+      HandlerForProtocol(dispatcher, HelpCommands);
+  [helpCommandsHandler
+      presentInProductHelpWithType:InProductHelpType::kSettingsInOverflowMenu];
+}
+
+}  // namespace
 
 @interface AccountMenuCoordinator () <
     AccountMenuMediatorDelegate,
     ManageAccountsCoordinatorDelegate,
+    SyncErrorSettingsCommandHandler,
     TrustedVaultReauthenticationCoordinatorDelegate,
     UIAdaptivePresentationControllerDelegate>
 
@@ -116,16 +132,12 @@
 
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                    browser:(Browser*)browser
-                              contextStyle:(SigninContextStyle)contextStyle
                                 anchorView:(UIView*)anchorView
                                accessPoint:(AccountMenuAccessPoint)accessPoint
                                        URL:(const GURL&)url {
-  self = [super
-      initWithBaseViewController:viewController
-                         browser:browser
-                    contextStyle:contextStyle
-                     accessPoint:signin_metrics::AccessPoint::kAccountMenu];
+  self = [super initWithBaseViewController:viewController browser:browser];
   if (self) {
+    _accessPoint = accessPoint;
     _anchorView = anchorView;
     _accessPoint = accessPoint;
     _url = url;
@@ -187,6 +199,7 @@
                                                    URL:_url
                                   prepareChangeProfile:prepareChangeProfile];
   _mediator.delegate = self;
+  _mediator.syncErrorSettingsCommandHandler = self;
   _mediator.consumer = _viewController;
   _viewController.mutator = _mediator;
   _viewController.dataSource = _mediator;
@@ -196,9 +209,7 @@
                                       completion:nil];
 }
 
-#pragma mark - AnimatedCoordinator
-
-- (void)stopAnimated:(BOOL)animated {
+- (void)stop {
   // TODO(crbug.com/336719423): Change condition to CHECK(_mediator). But
   // first inform the parent coordinator at didTapClose that this view was
   // dismissed.
@@ -206,7 +217,7 @@
     return;
   }
   [self stopTrustedVaultReauthenticationCoordinator];
-  [self stopChildrenAndViewControllerAnimated:animated];
+  [self stopChildrenAndViewController];
   [_syncEncryptionPassphraseTableViewController settingsWillBeDismissed];
   _syncEncryptionPassphraseTableViewController = nil;
   [_syncEncryptionTableViewController settingsWillBeDismissed];
@@ -222,7 +233,7 @@
   _identityManager = nil;
   _syncService = nullptr;
   _accountManagerService = nullptr;
-  [super stopAnimated:animated];
+  [super stop];
 }
 
 #pragma mark - UIAdaptivePresentationControllerDelegate
@@ -231,9 +242,11 @@
     (UIPresentationController*)presentationController {
   base::RecordAction(
       base::UserMetricsAction("Signin_AccountMenu_Dismissed_By_User"));
-  [self runCompletionWithSigninResult:SigninCoordinatorResultCanceledByUser
-                   completionIdentity:nil];
-  [self maybeShowSettingsIPH];
+  // `self` may be deallocated when -accountMenuCoordinatorWantsToBeStopped
+  // returns. We must access the browser first.
+  Browser* browser = self.browser;
+  [self.delegate accountMenuCoordinatorWantsToBeStopped:self];
+  maybeShowSettingsIPH(browser);
 }
 
 #pragma mark - AccountMenuMediatorDelegate
@@ -269,11 +282,10 @@
 - (void)didTapSettingsButton {
   CHECK(IdentityDiscAccountMenuEnabledWithSettings());
   // Close the account menu and open the Settings page.
-  [self stopChildrenAndViewControllerAnimated:YES];
-  [self runCompletionWithSigninResult:SigninCoordinatorResultCanceledByUser
-                   completionIdentity:nil];
+  [self stopChildrenAndViewController];
   id<ApplicationCommands> applicationHandler = HandlerForProtocol(
       self.browser->GetCommandDispatcher(), ApplicationCommands);
+  [self.delegate accountMenuCoordinatorWantsToBeStopped:self];
   [applicationHandler showSettingsFromViewController:nil];
 }
 
@@ -316,12 +328,15 @@
                     signedIdentity:(id<SystemIdentity>)signedIdentity
                    userTappedClose:(BOOL)userTappedClose {
   CHECK_EQ(mediator, _mediator);
-  [self stopChildrenAndViewControllerAnimated:YES];
-  [self runCompletionWithSigninResult:signinResult
-                   completionIdentity:signedIdentity];
+  [self stopChildrenAndViewController];
+  [self.delegate accountMenuCoordinatorWantsToBeStopped:self];
 
   if (userTappedClose) {
-    [self maybeShowSettingsIPH];
+    // `self` may be deallocated when -accountMenuCoordinatorWantsToBeStopped
+    // returns. We must access the browser first.
+    Browser* browser = self.browser;
+    [self.delegate accountMenuCoordinatorWantsToBeStopped:self];
+    maybeShowSettingsIPH(browser);
   }
 }
 
@@ -410,11 +425,12 @@
       signin_metrics::AccessPoint::kAccountMenu;
   signin_metrics::PromoAction promoAction =
       signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO;
+  auto style = SigninContextStyle::kDefault;
   _signinCoordinator = [SigninCoordinator
       primaryAccountReauthCoordinatorWithBaseViewController:
           _navigationController
                                                     browser:self.browser
-                                               contextStyle:self.contextStyle
+                                               contextStyle:style
                                                 accessPoint:accessPoint
                                                 promoAction:promoAction
                                        continuationProvider:
@@ -475,11 +491,13 @@
                                   completion:
                                       (SigninCoordinatorCompletionCallback)
                                           completion {
+  auto style = SigninContextStyle::kDefault;
+  auto accessPoint = signin_metrics::AccessPoint::kAccountMenu;
   _signinCoordinator = [SigninCoordinator
       addAccountCoordinatorWithBaseViewController:baseViewController
                                           browser:self.browser
-                                     contextStyle:self.contextStyle
-                                      accessPoint:self.accessPoint
+                                     contextStyle:style
+                                      accessPoint:accessPoint
                              continuationProvider:
                                  DoNothingContinuationProvider()];
   [self startSigninCoordinatorWithCompletion:completion];
@@ -527,7 +545,7 @@
 
 // Stops all children, then dismiss the view controller. Executes
 // `completion` synchronously.
-- (void)stopChildrenAndViewControllerAnimated:(BOOL)animated {
+- (void)stopChildrenAndViewController {
   // Stopping all potentially open children views.
   if (!_accountDetailsControllerDismissCallback.is_null()) {
     std::move(_accountDetailsControllerDismissCallback).Run(/*animated=*/false);
@@ -537,7 +555,7 @@
   // Add Account coordinator should be stopped before the Manage Accounts
   // Coordinator, as the former may be presented by the latter.
   [self stopManageAccountsCoordinator];
-  [self dismissViewControllerAnimated:animated];
+  [self dismissViewControllerAnimated:NO];
 }
 
 // Unplugs the view and navigation controller. Dismisses the navigation
@@ -559,17 +577,6 @@
                          completion:nil];
 }
 
-// Potentially shows an IPH, informing the user that they can find Settings in
-// the overflow menu. The handler contains the logic for whether to actually
-// show it.
-- (void)maybeShowSettingsIPH {
-  CommandDispatcher* dispatcher = self.browser->GetCommandDispatcher();
-  id<HelpCommands> helpCommandsHandler =
-      HandlerForProtocol(dispatcher, HelpCommands);
-  [helpCommandsHandler
-      presentInProductHelpWithType:InProductHelpType::kSettingsInOverflowMenu];
-}
-
 #pragma mark - TrustedVaultReauthenticationCoordinatorDelegate
 
 - (void)trustedVaultReauthenticationCoordinatorWantsToBeStopped:
diff --git a/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator_delegate.h b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator_delegate.h
new file mode 100644
index 0000000..ac01d0e
--- /dev/null
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator_delegate.h
@@ -0,0 +1,16 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_COORDINATOR_DELEGATE_H_
+#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_COORDINATOR_DELEGATE_H_
+@class AccountMenuCoordinator;
+
+@protocol AccountMenuCoordinatorDelegate <NSObject>
+
+- (void)accountMenuCoordinatorWantsToBeStopped:
+    (AccountMenuCoordinator*)coordinator;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_COORDINATOR_DELEGATE_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator_unittests.mm b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator_unittests.mm
similarity index 93%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator_unittests.mm
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator_unittests.mm
index 9c75a63b..2ea0d3bc 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator_unittests.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator_unittests.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator.h"
 
 #import <MaterialComponents/MaterialSnackbar.h>
 
@@ -10,14 +10,16 @@
 #import "base/test/scoped_feature_list.h"
 #import "components/sync/service/sync_service_utils.h"
 #import "components/trusted_vault/trusted_vault_server_constants.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator_delegate.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator_delegate.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator_delegate.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_coordinator.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator+protected.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signout_action_sheet/signout_action_sheet_coordinator.h"
+#import "ios/chrome/browser/settings/ui_bundled/google_services/sync_error_settings_command_handler.h"
 #import "ios/chrome/browser/settings/ui_bundled/sync/sync_encryption_passphrase_table_view_controller.h"
 #import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
 #import "ios/chrome/browser/shared/coordinator/scene/test/stub_browser_provider_interface.h"
@@ -61,6 +63,7 @@
 @interface AccountMenuCoordinator (Testing) <
     AccountMenuMediatorDelegate,
     SignoutActionSheetCoordinatorDelegate,
+    SyncErrorSettingsCommandHandler,
     UIAdaptivePresentationControllerDelegate,
     UINavigationControllerDelegate>
 
@@ -132,14 +135,15 @@
     coordinator_ = [[AccountMenuCoordinator alloc]
         initWithBaseViewController:nil
                            browser:browser_.get()
-                      contextStyle:SigninContextStyle::kDefault
                         anchorView:nil
                        accessPoint:AccountMenuAccessPoint::kNewTabPage
                                URL:GURL()];
-    coordinator_.signinCompletion =
-        ^(SigninCoordinatorResult, id<SystemIdentity>) {
+    id<AccountMenuCoordinatorDelegate> delegate =
+        OCMStrictProtocolMock(@protocol(AccountMenuCoordinatorDelegate));
+    OCMExpect([delegate accountMenuCoordinatorWantsToBeStopped:coordinator_])
+        .andDo(^(NSInvocation*) {
           Stop();
-        };
+        });
     [coordinator_ start];
 
     // Replacing the view controller and mediator by mock.
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_data_source.h b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_data_source.h
similarity index 81%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_data_source.h
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_data_source.h
index 4d6f5d0..29b7466 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_data_source.h
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_data_source.h
@@ -2,13 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_DATA_SOURCE_H_
-#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_DATA_SOURCE_H_
+#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_DATA_SOURCE_H_
+#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_DATA_SOURCE_H_
 
 #import <UIKit/UIKit.h>
 
 @class AccountErrorUIInfo;
-struct ManagementState;
 
 // Identity data source for AccountMenuViewController instance, to
 // manage the model.
@@ -46,4 +45,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_DATA_SOURCE_H_
+#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_DATA_SOURCE_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_egtest.mm b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_egtest.mm
similarity index 99%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_egtest.mm
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_egtest.mm
index d08dc402..93b3d35 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_egtest.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_egtest.mm
@@ -6,7 +6,7 @@
 
 #import "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey_ui_test_util.h"
 #import "ios/chrome/browser/bookmarks/ui_bundled/bookmark_earl_grey.h"
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator.h b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator.h
similarity index 70%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator.h
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator.h
index 5be2a30..55ac04326 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator.h
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator.h
@@ -2,23 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_MEDIATOR_H_
-#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_MEDIATOR_H_
+#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_MEDIATOR_H_
+#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_MEDIATOR_H_
 
 #import <Foundation/Foundation.h>
 
 #import "base/ios/block_types.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_data_source.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mutator.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_data_source.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mutator.h"
 
 enum class AccountMenuAccessPoint;
 @protocol AccountMenuConsumer;
 @protocol AccountMenuMediatorDelegate;
+@protocol SyncErrorSettingsCommandHandler;
 class AuthenticationService;
 class ChromeAccountManagerService;
 class GURL;
 class PrefService;
-typedef NS_ENUM(NSUInteger, SigninCoordinatorResult);
 namespace signin {
 class IdentityManager;
 }  // namespace signin
@@ -36,6 +36,10 @@
 // The delegate of the mediator.
 @property(nonatomic, weak) id<AccountMenuMediatorDelegate> delegate;
 
+// The sync error settings command handler.
+@property(nonatomic, weak) id<SyncErrorSettingsCommandHandler>
+    syncErrorSettingsCommandHandler;
+
 - (instancetype)initWithSyncService:(syncer::SyncService*)syncService
               accountManagerService:
                   (ChromeAccountManagerService*)accountManagerService
@@ -53,4 +57,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_MEDIATOR_H_
+#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_MEDIATOR_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator.mm b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator.mm
similarity index 93%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator.mm
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator.mm
index fb1771d..3aea720 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator.h"
 
 #import <optional>
 #import <string>
@@ -14,6 +14,11 @@
 #import "components/prefs/pref_service.h"
 #import "components/signin/public/base/consent_level.h"
 #import "components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_consumer.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_data_source.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator_delegate.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.h"
 #import "ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow.h"
 #import "ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow_request_helper.h"
 #import "ios/chrome/browser/authentication/ui_bundled/cells/table_view_account_item.h"
@@ -22,16 +27,12 @@
 #import "ios/chrome/browser/authentication/ui_bundled/change_profile/change_profile_settings_continuation.h"
 #import "ios/chrome/browser/authentication/ui_bundled/continuation.h"
 #import "ios/chrome/browser/authentication/ui_bundled/enterprise/enterprise_utils.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_consumer.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_data_source.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator_delegate.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_utils.h"
 #import "ios/chrome/browser/policy/ui_bundled/management_util.h"
 #import "ios/chrome/browser/settings/model/sync/utils/account_error_ui_info.h"
 #import "ios/chrome/browser/settings/model/sync/utils/identity_error_util.h"
+#import "ios/chrome/browser/settings/ui_bundled/google_services/sync_error_settings_command_handler.h"
 #import "ios/chrome/browser/settings/ui_bundled/settings_table_view_controller_constants.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
@@ -310,43 +311,46 @@
               _primaryIdentityBeforeSignin)) {
         base::RecordAction(
             base::UserMetricsAction("Signin_AccountMenu_ErrorButton_MDM"));
-        [self.delegate
+        [self.syncErrorSettingsCommandHandler
             openMDMErrodDialogWithSystemIdentity:_primaryIdentityBeforeSignin];
       } else {
         base::RecordAction(
             base::UserMetricsAction("Signin_AccountMenu_ErrorButton_Reauth"));
-        [self.delegate openPrimaryAccountReauthDialog];
+        [self.syncErrorSettingsCommandHandler openPrimaryAccountReauthDialog];
       }
       break;
     }
     case syncer::SyncService::UserActionableError::kNeedsPassphrase:
       base::RecordAction(
           base::UserMetricsAction("Signin_AccountMenu_ErrorButton_Passphrase"));
-      [self.delegate openPassphraseDialogWithModalPresentation:YES];
+      [self.syncErrorSettingsCommandHandler
+          openPassphraseDialogWithModalPresentation:YES];
       break;
     case syncer::SyncService::UserActionableError::
         kNeedsTrustedVaultKeyForPasswords:
       base::RecordAction(base::UserMetricsAction(
           "Signin_AccountMenu_ErrorButton_TrustedVaultForPasswords"));
-      [self.delegate openTrustedVaultReauthForFetchKeys];
+      [self.syncErrorSettingsCommandHandler openTrustedVaultReauthForFetchKeys];
       break;
     case syncer::SyncService::UserActionableError::
         kNeedsTrustedVaultKeyForEverything:
       base::RecordAction(base::UserMetricsAction(
           "Signin_AccountMenu_ErrorButton_TrustedVaultForEverything"));
-      [self.delegate openTrustedVaultReauthForFetchKeys];
+      [self.syncErrorSettingsCommandHandler openTrustedVaultReauthForFetchKeys];
       break;
     case syncer::SyncService::UserActionableError::
         kTrustedVaultRecoverabilityDegradedForPasswords:
       base::RecordAction(base::UserMetricsAction(
           "Signin_AccountMenu_ErrorButton_TrustedVaultDegradedForPasswords"));
-      [self.delegate openTrustedVaultReauthForDegradedRecoverability];
+      [self.syncErrorSettingsCommandHandler
+              openTrustedVaultReauthForDegradedRecoverability];
       break;
     case syncer::SyncService::UserActionableError::
         kTrustedVaultRecoverabilityDegradedForEverything:
       base::RecordAction(base::UserMetricsAction(
           "Signin_AccountMenu_ErrorButton_TrustedVaultDegradedForEverything"));
-      [self.delegate openTrustedVaultReauthForDegradedRecoverability];
+      [self.syncErrorSettingsCommandHandler
+              openTrustedVaultReauthForDegradedRecoverability];
       break;
     case syncer::SyncService::UserActionableError::kNone:
       NOTREACHED();
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator_delegate.h b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator_delegate.h
similarity index 78%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator_delegate.h
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator_delegate.h
index a41c5a4..740f2ab0 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator_delegate.h
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator_delegate.h
@@ -2,15 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_MEDIATOR_DELEGATE_H_
-#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_MEDIATOR_DELEGATE_H_
+#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_MEDIATOR_DELEGATE_H_
+#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_MEDIATOR_DELEGATE_H_
 
 #import <Foundation/Foundation.h>
 
 #import <string_view>
 
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
-#import "ios/chrome/browser/settings/ui_bundled/google_services/sync_error_settings_command_handler.h"
 #import "ios/chrome/browser/shared/public/commands/show_signin_command.h"
 #import "ios/chrome/browser/signin/model/constants.h"
 
@@ -18,7 +17,7 @@
 @class AuthenticationFlow;
 @protocol SystemIdentity;
 
-@protocol AccountMenuMediatorDelegate <SyncErrorSettingsCommandHandler>
+@protocol AccountMenuMediatorDelegate <NSObject>
 
 // Requests to dismiss the account menu.
 - (void)mediatorWantsToBeDismissed:(AccountMenuMediator*)mediator
@@ -53,4 +52,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_MEDIATOR_DELEGATE_H_
+#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_MEDIATOR_DELEGATE_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator_unittests.mm b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator_unittests.mm
similarity index 96%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator_unittests.mm
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator_unittests.mm
index 3aed69a..5d7cabd 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator_unittests.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator_unittests.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator.h"
 
 #import "base/containers/flat_map.h"
 #import "base/memory/raw_ptr.h"
@@ -10,15 +10,16 @@
 #import "base/test/scoped_feature_list.h"
 #import "base/test/task_environment.h"
 #import "components/sync/test/test_sync_service.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_consumer.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mediator_delegate.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.h"
 #import "ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow.h"
 #import "ios/chrome/browser/authentication/ui_bundled/cells/table_view_account_item.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_consumer.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mediator_delegate.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
 #import "ios/chrome/browser/settings/model/sync/utils/account_error_ui_info.h"
 #import "ios/chrome/browser/settings/model/sync/utils/identity_error_util.h"
+#import "ios/chrome/browser/settings/ui_bundled/google_services/sync_error_settings_command_handler.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
 #import "ios/chrome/browser/shared/public/commands/show_signin_command.h"
@@ -115,6 +116,8 @@
     // Set the mediator and its mocks delegate.
     delegate_mock_ =
         OCMStrictProtocolMock(@protocol(AccountMenuMediatorDelegate));
+    sync_error_settings_mock_ =
+        OCMStrictProtocolMock(@protocol(SyncErrorSettingsCommandHandler));
     consumer_mock_ = OCMStrictProtocolMock(@protocol(AccountMenuConsumer));
     mediator_ = [[AccountMenuMediator alloc]
           initWithSyncService:test_sync_service_
@@ -126,6 +129,7 @@
                           URL:GURL()
          prepareChangeProfile:nil];
     mediator_.delegate = delegate_mock_;
+    mediator_.syncErrorSettingsCommandHandler = sync_error_settings_mock_;
     mediator_.consumer = consumer_mock_;
     authentication_flow_mock_ = OCMStrictClassMock([AuthenticationFlow class]);
   }
@@ -156,6 +160,7 @@
   // Verify that all mocks expectation are fulfilled.
   void VerifyMock() {
     EXPECT_OCMOCK_VERIFY(delegate_mock_);
+    EXPECT_OCMOCK_VERIFY(sync_error_settings_mock_);
     EXPECT_OCMOCK_VERIFY(consumer_mock_);
     EXPECT_OCMOCK_VERIFY((id)authentication_flow_mock_);
   }
@@ -194,6 +199,7 @@
   base::test::ScopedFeatureList feature_list_;
 
   id<AccountMenuMediatorDelegate> delegate_mock_;
+  id<SyncErrorSettingsCommandHandler> sync_error_settings_mock_;
   id<AccountMenuConsumer> consumer_mock_;
   AuthenticationFlow* authentication_flow_mock_;
   AccountMenuMediator* mediator_;
@@ -593,7 +599,8 @@
   // `kSignInNeedsUpdate` is not an error displayed to the user (technically,
   // `GetAccountErrorUIInfo` returns `nil` on `kSignInNeedsUpdate`.)
   SignInAndSetPassphraseRequired();
-  OCMExpect([delegate_mock_ openPassphraseDialogWithModalPresentation:YES]);
+  OCMExpect([sync_error_settings_mock_
+      openPassphraseDialogWithModalPresentation:YES]);
   [mediator_ didTapErrorButton];
   EXPECT_EQ(1, user_actions_.GetActionCount(
                    "Signin_AccountMenu_ErrorButton_Passphrase"));
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mutator.h b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mutator.h
similarity index 80%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mutator.h
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mutator.h
index bec79ca..1d07883a 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mutator.h
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mutator.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 IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_MUTATOR_H_
-#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_MUTATOR_H_
+#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_MUTATOR_H_
+#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_MUTATOR_H_
 
 #import <Foundation/Foundation.h>
 
@@ -42,4 +42,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_MUTATOR_H_
+#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_MUTATOR_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.h b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.h
similarity index 74%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.h
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.h
index 06d7c1e..75056abe 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.h
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_VIEW_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_VIEW_CONTROLLER_H_
+#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_VIEW_CONTROLLER_H_
 
 #import <UIKit/UIKit.h>
 
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_consumer.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_consumer.h"
 #import "ios/chrome/browser/shared/ui/table_view/chrome_table_view_controller.h"
 
 @protocol AccountMenuDataSource;
@@ -39,4 +39,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_VIEW_CONTROLLER_H_
+#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_ACCOUNT_MENU_ACCOUNT_MENU_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.mm b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.mm
similarity index 96%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.mm
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.mm
index 817a29a..263c067 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.h"
 
 #import "base/apple/foundation_util.h"
 #import "base/check.h"
@@ -12,11 +12,11 @@
 #import "base/metrics/user_metrics_action.h"
 #import "base/strings/sys_string_conversions.h"
 #import "components/strings/grit/components_strings.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_data_source.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mutator.h"
 #import "ios/chrome/browser/authentication/ui_bundled/cells/central_account_view.h"
 #import "ios/chrome/browser/authentication/ui_bundled/cells/table_view_account_item.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_data_source.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mutator.h"
 #import "ios/chrome/browser/keyboard/ui_bundled/UIKeyCommand+Chrome.h"
 #import "ios/chrome/browser/policy/model/management_state.h"
 #import "ios/chrome/browser/settings/model/sync/utils/account_error_ui_info.h"
@@ -286,17 +286,17 @@
   manageYourAccountAction.subtitle = [self.dataSource primaryAccountEmail];
 
   UIMenu* ellipsisMenu;
-  UIAction* editAccountListAction = [UIAction
-      actionWithTitle:l10n_util::GetNSString(
-                          IDS_IOS_ACCOUNT_MENU_EDIT_ACCOUNT_LIST)
-                image:DefaultSymbolWithConfiguration(@"pencil",
-                                                     symbolConfiguration)
-           identifier:kAccountMenuEditAccountListId
-              handler:^(UIAction* action) {
-                base::RecordAction(base::UserMetricsAction(
-                    "Signin_AccountMenu_EditAccountList"));
-                [self.mutator didTapManageAccounts];
-              }];
+  UIAction* editAccountListAction =
+      [UIAction actionWithTitle:l10n_util::GetNSString(
+                                    IDS_IOS_ACCOUNT_MENU_EDIT_ACCOUNT_LIST)
+                          image:DefaultSymbolWithConfiguration(
+                                    @"pencil", symbolConfiguration)
+                     identifier:kAccountMenuEditAccountListId
+                        handler:^(UIAction* action) {
+                          base::RecordAction(base::UserMetricsAction(
+                              "Signin_AccountMenu_EditAccountList"));
+                          [self.mutator didTapManageAccounts];
+                        }];
   ellipsisMenu = [UIMenu
       menuWithChildren:@[ manageYourAccountAction, editAccountListAction ]];
 
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller_unittests.mm b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller_unittests.mm
similarity index 98%
rename from ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller_unittests.mm
rename to ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller_unittests.mm
index b14b2aa..ff962a1e 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller_unittests.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller_unittests.mm
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.h"
 
 #import "base/check_op.h"
 #import "base/memory/raw_ptr.h"
 #import "base/test/metrics/histogram_tester.h"
 #import "base/test/metrics/user_action_tester.h"
 #import "base/test/scoped_feature_list.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_data_source.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_mutator.h"
 #import "ios/chrome/browser/authentication/ui_bundled/cells/central_account_view.h"
 #import "ios/chrome/browser/authentication/ui_bundled/cells/table_view_account_item.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_data_source.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_mutator.h"
 #import "ios/chrome/browser/policy/model/management_state.h"
 #import "ios/chrome/browser/settings/model/sync/utils/account_error_ui_info.h"
 #import "ios/chrome/browser/settings/ui_bundled/cells/settings_image_detail_text_cell.h"
diff --git a/ios/chrome/browser/authentication/ui_bundled/separate_profiles_egtest.mm b/ios/chrome/browser/authentication/ui_bundled/separate_profiles_egtest.mm
index 00c6727b..a469533 100644
--- a/ios/chrome/browser/authentication/ui_bundled/separate_profiles_egtest.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/separate_profiles_egtest.mm
@@ -7,8 +7,8 @@
 #import "base/test/ios/wait_util.h"
 #import "components/policy/core/browser/signin/profile_separation_policies.h"
 #import "components/signin/public/base/signin_pref_names.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/separate_profiles_util.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey_ui_test_util.h"
diff --git a/ios/chrome/browser/authentication/ui_bundled/separate_profiles_util.mm b/ios/chrome/browser/authentication/ui_bundled/separate_profiles_util.mm
index 08f7f1c..f9bb03d0 100644
--- a/ios/chrome/browser/authentication/ui_bundled/separate_profiles_util.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/separate_profiles_util.mm
@@ -5,7 +5,7 @@
 #import "ios/chrome/browser/authentication/ui_bundled/separate_profiles_util.h"
 
 #import "base/strings/sys_string_conversions.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
 #import "ios/chrome/browser/first_run/ui_bundled/first_run_constants.h"
 #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_constants.h"
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/signin/BUILD.gn
index d8f4c793..3d5cd03 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/BUILD.gn
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/BUILD.gn
@@ -60,10 +60,10 @@
     "//ios/chrome/app:tests_hook",
     "//ios/chrome/app/application_delegate:app_state",
     "//ios/chrome/app/profile",
+    "//ios/chrome/browser/authentication/ui_bundled/account_menu:account_menu",
     "//ios/chrome/browser/authentication/ui_bundled/change_profile",
     "//ios/chrome/browser/authentication/ui_bundled/history_sync",
     "//ios/chrome/browser/authentication/ui_bundled/signin:features",
-    "//ios/chrome/browser/authentication/ui_bundled/signin/account_menu",
     "//ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin",
     "//ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin",
     "//ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/coordinator",
@@ -80,6 +80,7 @@
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile:features",
+    "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/shared/public/features:system_flags",
     "//ios/chrome/browser/signin/model",
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.h
deleted file mode 100644
index 77a91aa..0000000
--- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_COORDINATOR_H_
-#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_COORDINATOR_H_
-
-#import <UIKit/UIKit.h>
-
-#import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h"
-
-enum class AccountMenuAccessPoint;
-@protocol AccountMenuCoordinatorDelegate;
-
-// Coordinator to display the fast account menu view controller.
-@interface AccountMenuCoordinator : SigninCoordinator
-
-// `anchorView`: Clicked view, used to anchor the menu to it when using
-// UIModalPresentationPopover mode.
-- (instancetype)initWithBaseViewController:(UIViewController*)viewController
-                                   browser:(Browser*)browser
-                              contextStyle:(SigninContextStyle)contextStyle
-                                anchorView:(UIView*)anchorView
-                               accessPoint:(AccountMenuAccessPoint)accessPoint
-                                       URL:(const GURL&)url
-    NS_DESIGNATED_INITIALIZER;
-
-- (instancetype)initWithBaseViewController:(UIViewController*)viewController
-                                   browser:(Browser*)browser
-                              contextStyle:(SigninContextStyle)contextStyle
-                               accessPoint:
-                                   (signin_metrics::AccessPoint)accessPoint
-    NS_UNAVAILABLE;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_ACCOUNT_MENU_ACCOUNT_MENU_COORDINATOR_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/test/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/test/BUILD.gn
index d132c37..b273259 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/test/BUILD.gn
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/test/BUILD.gn
@@ -17,8 +17,8 @@
     "//components/sync/base:features",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/authentication/ui_bundled:eg_test_support+eg2",
+    "//ios/chrome/browser/authentication/ui_bundled/account_menu:constants",
     "//ios/chrome/browser/authentication/ui_bundled/signin:constants",
-    "//ios/chrome/browser/authentication/ui_bundled/signin/account_menu:constants",
     "//ios/chrome/browser/authentication/ui_bundled/views:views_constants",
     "//ios/chrome/browser/first_run/ui_bundled:constants",
     "//ios/chrome/browser/first_run/ui_bundled:eg_test_support+eg2",
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/test/forced_signin_egtest.mm b/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/test/forced_signin_egtest.mm
index ad0b14b..e9a877d8 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/test/forced_signin_egtest.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/test/forced_signin_egtest.mm
@@ -12,8 +12,8 @@
 #import "components/policy/policy_constants.h"
 #import "components/signin/ios/browser/features.h"
 #import "components/signin/public/base/signin_metrics.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/expected_signin_histograms.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey_ui_test_util.h"
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h
index ece380a4..03d0700 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h
@@ -27,6 +27,7 @@
 namespace user_prefs {
 class PrefRegistrySyncable;
 }  // namespace user_prefs
+@class ShowSigninCommand;
 
 // Main class for sign-in coordinator. This class should not be instantiated
 // directly, this should be done using the class methods.
@@ -61,6 +62,12 @@
 // Registers preferences related to sign-in coordinator.
 + (void)registerProfilePrefs:(user_prefs::PrefRegistrySyncable*)registry;
 
+// Returns a coordinator according to the command
++ (SigninCoordinator*)signinCoordinatorWithCommand:(ShowSigninCommand*)command
+                                           browser:(Browser*)browser
+                                baseViewController:
+                                    (UIViewController*)baseViewController;
+
 // Returns a coordinator to sign-in the user without taps if the identity has
 // been selected with `identity`. Otherwise, it will ask the user to select
 // an identity, and starts the sign-in flow. If there is no identity on the
@@ -214,18 +221,6 @@
                                          (const ChangeProfileContinuationProvider&)
                                              continuationProvider;
 
-// Returns a coordinator to switch account.
-+ (SigninCoordinator*)
-    accountMenuCoordinatorWithBaseViewController:
-        (UIViewController*)viewController
-                                         browser:(Browser*)browser
-                                    contextStyle:
-                                        (SigninContextStyle)contextStyle
-                                      anchorView:(UIView*)anchorView
-                                     accessPoint:
-                                         (AccountMenuAccessPoint)accessPoint
-                                             URL:(const GURL&)url;
-
 // Returns a coordinator to show the history sync.
 + (SigninCoordinator*)
     historySyncCoordinatorWithBaseViewController:
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.mm
index 30ff394..8e14c6e 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.mm
@@ -10,7 +10,6 @@
 #import "components/pref_registry/pref_registry_syncable.h"
 #import "components/prefs/pref_service.h"
 #import "components/signin/public/base/signin_metrics.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_coordinator.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_coordinator.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/coordinator/fullscreen_signin_coordinator.h"
@@ -27,6 +26,7 @@
 #import "ios/chrome/browser/shared/model/browser/browser.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
+#import "ios/chrome/browser/shared/public/commands/show_signin_command.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/signin/model/chrome_account_manager_service_factory.h"
 
@@ -54,6 +54,109 @@
   registry->RegisterIntegerPref(prefs::kSigninWebSignDismissalCount, 0);
   registry->RegisterDictionaryPref(prefs::kSigninHasAcceptedManagementDialog);
 }
+// Returns a coordinator according to the command
++ (SigninCoordinator*)signinCoordinatorWithCommand:(ShowSigninCommand*)command
+                                           browser:(Browser*)browser
+                                baseViewController:
+                                    (UIViewController*)baseViewController {
+  switch (command.operation) {
+    case AuthenticationOperation::kPrimaryAccountReauth:
+      return [SigninCoordinator
+          primaryAccountReauthCoordinatorWithBaseViewController:
+              baseViewController
+                                                        browser:browser
+                                                   contextStyle:
+                                                       command.contextStyle
+
+                                                    accessPoint:command
+                                                                    .accessPoint
+                                                    promoAction:command
+                                                                    .promoAction
+                                           continuationProvider:
+                                               command
+                                                   .changeProfileContinuationProvider];
+    case AuthenticationOperation::kResignin:
+      return [SigninCoordinator
+          signinAndSyncReauthCoordinatorWithBaseViewController:
+              baseViewController
+                                                       browser:browser
+                                                  contextStyle:command
+                                                                   .contextStyle
+                                                   accessPoint:command
+                                                                   .accessPoint
+                                                   promoAction:command
+                                                                   .promoAction
+                                          continuationProvider:
+                                              command
+                                                  .changeProfileContinuationProvider];
+    case AuthenticationOperation::kSigninOnly: {
+      auto& provider = command.changeProfileContinuationProvider;
+      return [SigninCoordinator
+          consistencyPromoSigninCoordinatorWithBaseViewController:
+              baseViewController
+                                                          browser:browser
+                                                     contextStyle:
+                                                         command.contextStyle
+                                                      accessPoint:
+                                                          command.accessPoint
+                                             prepareChangeProfile:
+                                                 command.prepareChangeProfile
+                                             continuationProvider:provider];
+    }
+    case AuthenticationOperation::kAddAccount:
+      return [SigninCoordinator
+          addAccountCoordinatorWithBaseViewController:baseViewController
+                                              browser:browser
+                                         contextStyle:command.contextStyle
+                                          accessPoint:command.accessPoint
+                                 continuationProvider:
+                                     command.changeProfileContinuationProvider];
+    case AuthenticationOperation::kForcedSigninAndSync:
+      return [SigninCoordinator
+          fullscreenSigninCoordinatorWithBaseViewController:baseViewController
+                                                    browser:browser
+                                               contextStyle:command.contextStyle
+                                                accessPoint:command.accessPoint
+                          changeProfileContinuationProvider:
+                              command.changeProfileContinuationProvider];
+    case AuthenticationOperation::kInstantSignin:
+      return [SigninCoordinator
+          instantSigninCoordinatorWithBaseViewController:baseViewController
+                                                 browser:browser
+                                                identity:command.identity
+                                            contextStyle:command.contextStyle
+                                             accessPoint:command.accessPoint
+                                             promoAction:command.promoAction
+                                    continuationProvider:
+                                        command
+                                            .changeProfileContinuationProvider];
+    case AuthenticationOperation::kSheetSigninAndHistorySync: {
+      auto& provider = command.changeProfileContinuationProvider;
+      return [SigninCoordinator
+          signinAndHistorySyncCoordinatorWithBaseViewController:
+              baseViewController
+                                                        browser:browser
+                                                   contextStyle:
+                                                       command.contextStyle
+                                                    accessPoint:command
+                                                                    .accessPoint
+                                                    promoAction:command
+                                                                    .promoAction
+                                            optionalHistorySync:
+                                                command.optionalHistorySync
+                                                fullscreenPromo:
+                                                    command.fullScreenPromo
+                                           continuationProvider:provider];
+    }
+    case AuthenticationOperation::kHistorySync:
+      return [SigninCoordinator
+          historySyncCoordinatorWithBaseViewController:baseViewController
+                                               browser:browser
+                                          contextStyle:command.contextStyle
+                                           accessPoint:command.accessPoint
+                                           promoAction:command.promoAction];
+  }
+}
 
 + (SigninCoordinator*)
     instantSigninCoordinatorWithBaseViewController:
@@ -244,25 +347,6 @@
 }
 
 + (SigninCoordinator*)
-    accountMenuCoordinatorWithBaseViewController:
-        (UIViewController*)viewController
-                                         browser:(Browser*)browser
-                                    contextStyle:
-                                        (SigninContextStyle)contextStyle
-                                      anchorView:(UIView*)anchorView
-                                     accessPoint:
-                                         (AccountMenuAccessPoint)accessPoint
-                                             URL:(const GURL&)url {
-  return
-      [[AccountMenuCoordinator alloc] initWithBaseViewController:viewController
-                                                         browser:browser
-                                                    contextStyle:contextStyle
-                                                      anchorView:anchorView
-                                                     accessPoint:accessPoint
-                                                             URL:url];
-}
-
-+ (SigninCoordinator*)
     historySyncCoordinatorWithBaseViewController:
         (UIViewController*)viewController
                                          browser:(Browser*)browser
diff --git a/ios/chrome/browser/browser_view/model/browser_view_visibility_notifier_browser_agent.mm b/ios/chrome/browser/browser_view/model/browser_view_visibility_notifier_browser_agent.mm
index 68f4fa46..bb9b663 100644
--- a/ios/chrome/browser/browser_view/model/browser_view_visibility_notifier_browser_agent.mm
+++ b/ios/chrome/browser/browser_view/model/browser_view_visibility_notifier_browser_agent.mm
@@ -9,7 +9,8 @@
 #import "ios/chrome/browser/browser_view/model/browser_view_visibility_observer.h"
 
 BrowserViewVisibilityNotifierBrowserAgent::
-    BrowserViewVisibilityNotifierBrowserAgent(Browser* browser) {
+    BrowserViewVisibilityNotifierBrowserAgent(Browser* browser)
+    : BrowserUserData(browser) {
   BrowserViewVisibilityStateChangeCallback callback =
       base::BindRepeating(&BrowserViewVisibilityNotifierBrowserAgent::
                               BrowserViewVisibilityStateDidChange,
diff --git a/ios/chrome/browser/browser_view/ui_bundled/DEPS b/ios/chrome/browser/browser_view/ui_bundled/DEPS
index 257b8f10..4f05241 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/DEPS
+++ b/ios/chrome/browser/browser_view/ui_bundled/DEPS
@@ -52,6 +52,7 @@
   "+ios/chrome/browser/lens/ui_bundled",
   "+ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h",
   "+ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.h",
+  "+ios/chrome/browser/lens_overlay/coordinator/lens_overlay_presentation_environment.h",
   "+ios/chrome/browser/lens_overlay/coordinator/lens_view_finder_coordinator.h",
   "+ios/chrome/browser/lens_overlay/model/lens_overlay_tab_helper.h",
   "+ios/chrome/browser/main_content/ui_bundled",
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 2cd7465..eb963e32 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
+++ b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
@@ -1540,6 +1540,7 @@
     _lensOverlayCoordinator = [[LensOverlayCoordinator alloc]
         initWithBaseViewController:self.viewController
                            browser:self.browser];
+    _lensOverlayCoordinator.presentationEnvironment = self.viewController;
     [_lensOverlayCoordinator start];
   }
 }
diff --git a/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.h b/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.h
index 61170d78..b4a8e45 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.h
+++ b/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.h
@@ -15,6 +15,7 @@
 #import "ios/chrome/browser/find_bar/ui_bundled/find_bar_coordinator.h"
 #import "ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_consumer.h"
 #import "ios/chrome/browser/lens/ui_bundled/lens_coordinator.h"
+#import "ios/chrome/browser/lens_overlay/coordinator/lens_overlay_presentation_environment.h"
 #import "ios/chrome/browser/ntp/ui_bundled/logo_animation_controller.h"
 #import "ios/chrome/browser/omnibox/ui/omnibox_focus_delegate.h"
 #import "ios/chrome/browser/omnibox/ui/popup/omnibox_popup_presenter.h"
@@ -81,6 +82,7 @@
                         FindBarPresentationDelegate,
                         IncognitoReauthConsumer,
                         LensPresentationDelegate,
+                        LensOverlayPresentationEnvironment,
                         LogoAnimationControllerOwnerOwner,
                         TabConsumer,
                         OmniboxFocusDelegate,
diff --git a/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm b/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm
index f98d54c4..c401c28 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm
+++ b/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm
@@ -2731,4 +2731,22 @@
        aboveSubview:self.toolbarCoordinator.primaryToolbarViewController.view];
 }
 
+#pragma mark - LensOverlayPresentationEnvironment
+
+- (void)lensOverlayWillAppear {
+  [_sideSwipeCoordinator setEnabled:NO];
+}
+
+- (void)lensOverlayWillDisappear {
+  [_sideSwipeCoordinator setEnabled:YES];
+}
+
+- (NSDirectionalEdgeInsets)presentationInsetsForLensOverlay {
+  if (IsRegularXRegularSizeClass(self)) {
+    return NSDirectionalEdgeInsetsMake([self expandedTopToolbarHeight], 0, 0,
+                                       0);
+  }
+  return NSDirectionalEdgeInsetsZero;
+}
+
 @end
diff --git a/ios/chrome/browser/bubble/model/tab_based_iph_browser_agent.mm b/ios/chrome/browser/bubble/model/tab_based_iph_browser_agent.mm
index 19281db..7a56f8a 100644
--- a/ios/chrome/browser/bubble/model/tab_based_iph_browser_agent.mm
+++ b/ios/chrome/browser/bubble/model/tab_based_iph_browser_agent.mm
@@ -29,7 +29,8 @@
 #import "ios/web/public/ui/crw_web_view_scroll_view_proxy.h"
 
 TabBasedIPHBrowserAgent::TabBasedIPHBrowserAgent(Browser* browser)
-    : web_state_list_(browser->GetWebStateList()),
+    : BrowserUserData(browser),
+      web_state_list_(browser->GetWebStateList()),
       active_web_state_observer_(
           std::make_unique<ActiveWebStateObservationForwarder>(web_state_list_,
                                                                this)),
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_coordinator.mm b/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_coordinator.mm
index b3c7178..e9ecf92 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/content_suggestions_coordinator.mm
@@ -259,8 +259,6 @@
 
   raw_ptr<segmentation_platform::SegmentationPlatformService>
       _segmentationService;
-  raw_ptr<segmentation_platform::DeviceSwitcherResultDispatcher>
-      _deviceSwitcherResultDispatcher;
 }
 
 - (void)start {
@@ -278,8 +276,6 @@
   _segmentationService =
       segmentation_platform::SegmentationPlatformServiceFactory::GetForProfile(
           profile);
-  _deviceSwitcherResultDispatcher = segmentation_platform::
-      SegmentationPlatformServiceFactory::GetDispatcherForProfile(profile);
 
   self.authService = AuthenticationServiceFactory::GetForProfile(profile);
 
@@ -505,12 +501,7 @@
                  authenticationService:authenticationService
                             sceneState:self.browser->GetSceneState()
                  isDefaultSearchEngine:isDefaultSearchEngine
-                   segmentationService:_segmentationService
-        deviceSwitcherResultDispatcher:_deviceSwitcherResultDispatcher
                   priceTrackingEnabled:IsPriceTrackingEnabled(self.profile)];
-    if (IsSegmentedDefaultBrowserPromoEnabled()) {
-      [_setUpListMediator retrieveUserSegment];
-    }
     _setUpListMediator.commandHandler = self;
     _setUpListMediator.contentSuggestionsMetricsRecorder =
         self.contentSuggestionsMetricsRecorder;
@@ -552,7 +543,6 @@
 
 - (void)stop {
   _segmentationService = nullptr;
-  _deviceSwitcherResultDispatcher = nullptr;
   [_shortcutsMediator disconnect];
   _shortcutsMediator = nil;
   [_safetyCheckMediator disconnect];
@@ -1186,11 +1176,9 @@
 
   _defaultBrowserPromoCoordinator =
       [[SetUpListDefaultBrowserPromoCoordinator alloc]
-              initWithBaseViewController:self.magicStackCollectionView
-                                 browser:self.browser
-                             application:[UIApplication sharedApplication]
-                     segmentationService:_segmentationService
-          deviceSwitcherResultDispatcher:_deviceSwitcherResultDispatcher];
+          initWithBaseViewController:self.magicStackCollectionView
+                             browser:self.browser
+                         application:[UIApplication sharedApplication]];
   _defaultBrowserPromoCoordinator.delegate = self;
   [_defaultBrowserPromoCoordinator start];
 }
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_ranking_model_unittest.mm b/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_ranking_model_unittest.mm
index ad8999e4..6e7bb86 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_ranking_model_unittest.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/magic_stack/magic_stack_ranking_model_unittest.mm
@@ -291,8 +291,6 @@
                  authenticationService:authenticationService
                             sceneState:scene_state_
                  isDefaultSearchEngine:NO
-                   segmentationService:nullptr
-        deviceSwitcherResultDispatcher:nullptr
                   priceTrackingEnabled:NO];
     _setUpListMediator.shouldShowSetUpList = YES;
     _tabResumptionMediator = [[FakeTabResumptionMediator alloc]
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/BUILD.gn b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/BUILD.gn
index 7770163..73af2225 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/BUILD.gn
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/BUILD.gn
@@ -30,7 +30,6 @@
     "//components/prefs",
     "//components/prefs/ios",
     "//components/segmentation_platform/embedder/default_model",
-    "//components/segmentation_platform/public",
     "//components/signin/public/identity_manager",
     "//components/signin/public/identity_manager/objc",
     "//components/sync/base:features",
@@ -49,7 +48,6 @@
     "//ios/chrome/browser/ntp/model:set_up_list_item_type",
     "//ios/chrome/browser/ntp/model:set_up_list_prefs",
     "//ios/chrome/browser/push_notification/model:push_notification_settings_util_header",
-    "//ios/chrome/browser/segmentation_platform/model:segmented_default_browser",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
@@ -75,33 +73,25 @@
     "set_up_list_default_browser_promo_coordinator.h",
     "set_up_list_default_browser_promo_coordinator.mm",
     "set_up_list_default_browser_promo_coordinator_delegate.h",
-    "set_up_list_default_browser_promo_mediator.h",
-    "set_up_list_default_browser_promo_mediator.mm",
   ]
   deps = [
     ":constants",
     "//base",
     "//components/feature_engagement/public",
-    "//components/segmentation_platform/embedder/default_model",
-    "//components/segmentation_platform/public",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/default_browser/model:utils",
     "//ios/chrome/browser/default_promo/ui_bundled:coordinator",
-    "//ios/chrome/browser/default_promo/ui_bundled/generic",
     "//ios/chrome/browser/feature_engagement/model",
     "//ios/chrome/browser/first_run/ui_bundled/default_browser:default_browser_mediator",
     "//ios/chrome/browser/first_run/ui_bundled/default_browser:default_browser_ui",
     "//ios/chrome/browser/ntp/model:set_up_list_item_type",
     "//ios/chrome/browser/ntp/model:set_up_list_prefs",
-    "//ios/chrome/browser/segmentation_platform/model",
-    "//ios/chrome/browser/segmentation_platform/model:segmented_default_browser",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features",
-    "//ios/chrome/common/ui/confirmation_alert",
     "//ios/chrome/common/ui/promo_style",
   ]
   frameworks = [ "UIKit.framework" ]
@@ -133,10 +123,7 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [
-    "set_up_list_default_browser_promo_coordinator_unittest.mm",
-    "set_up_list_default_browser_promo_mediator_unittest.mm",
-  ]
+  sources = [ "set_up_list_default_browser_promo_coordinator_unittest.mm" ]
   deps = [
     ":constants",
     ":default_browser_promo",
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_egtest.mm b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_egtest.mm
index c58bc92fb..93f82491 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_egtest.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_egtest.mm
@@ -23,12 +23,11 @@
 
 namespace {
 
-// Returns a matcher to the Default Browser Set Up List item default title.
-id<GREYMatcher> DefaultItemTitle() {
+// Returns a matcher to the Default Browser Set Up List item default
+// description.
+id<GREYMatcher> DefaultItemDescription() {
   return grey_text(
-      GetNSString([ChromeEarlGrey isIPadIdiom]
-                      ? IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_TITLE_IPAD
-                      : IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_TITLE));
+      GetNSString(IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_SHORT_DESCRIPTION));
 }
 
 // Returns a matcher to the Default Browser see more Set Up List item default
@@ -41,97 +40,21 @@
       grey_accessibilityID(set_up_list::kAccessibilityID), nil);
 }
 
-// Returns a matcher to the Default Browser Set Up List item default
-// description.
-id<GREYMatcher> DefaultItemDescription() {
-  return grey_text(
-      GetNSString(IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_SHORT_DESCRIPTION));
-}
-
 // Returns a matcher to the Default Browser see more Set Up List item default
 // description.
 id<GREYMatcher> DefaultItemSeeMoreDescription() {
-  return grey_allOf(DefaultItemDescription(),
-                    grey_accessibilityID(set_up_list::kAccessibilityID), nil);
-}
-
-// Returns a matcher to the Default Browser Set Up List item description shown
-// to device switchers.
-id<GREYMatcher> DeviceSwitcherDescription() {
-  return grey_text(GetNSString(
-      IDS_IOS_SET_UP_LIST_SEGMENTED_DEFAULT_BROWSER_DEVICE_SWITCHER_SHORT_DESCRIPTION));
-}
-
-// Returns a matcher to the Default Browser see more Set Up List item
-// description shown to device switchers.
-id<GREYMatcher> DeviceSwitcherSeeMoreDescription() {
-  return grey_allOf(DeviceSwitcherDescription(),
-                    grey_accessibilityID(set_up_list::kAccessibilityID), nil);
-}
-
-// Returns a matcher to the Default Browser Set Up List item description shown
-// to shopping users.
-id<GREYMatcher> ShopperDescription() {
-  return grey_text(GetNSString(
-      IDS_IOS_SET_UP_LIST_SEGMENTED_DEFAULT_BROWSER_SHOPPER_SHORT_DESCRIPTION));
-}
-
-// Returns a matcher to the Default Browser see more Set Up List item
-// description shown to shopping users.
-id<GREYMatcher> ShopperSeeMoreDescription() {
-  return grey_allOf(ShopperDescription(),
-                    grey_accessibilityID(set_up_list::kAccessibilityID), nil);
-}
-
-// Returns a matcher to the Set Up List Default Browser promo title shown to
-// device switchers.
-id<GREYMatcher> DeviceSwitcherPromoTitle() {
   return grey_allOf(
       grey_text(GetNSString(
-          [ChromeEarlGrey isIPadIdiom]
-              ? IDS_IOS_FIRST_RUN_SEGMENTED_DEFAULT_BROWSER_DEVICE_SWITCHER_TITLE_IPAD
-              : IDS_IOS_FIRST_RUN_SEGMENTED_DEFAULT_BROWSER_DEVICE_SWITCHER_TITLE_IPHONE)),
-      grey_accessibilityID(kPromoStyleTitleAccessibilityIdentifier), nil);
+          IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_SEE_MORE_DESCRIPTION)),
+      grey_accessibilityID(set_up_list::kAccessibilityID), nil);
 }
 
-// Returns a matcher to the Set Up List Default Browser promo subtitle shown to
-// desktop users.
-id<GREYMatcher> DesktopUserPromoSubtitle() {
-  return grey_allOf(
-      grey_text(GetNSString(
-          [ChromeEarlGrey isIPadIdiom]
-              ? IDS_IOS_FIRST_RUN_SEGMENTED_DEFAULT_BROWSER_DESKTOP_USER_SUBTITLE_IPAD
-              : IDS_IOS_FIRST_RUN_SEGMENTED_DEFAULT_BROWSER_DESKTOP_USER_SUBTITLE_IPHONE)),
-      grey_accessibilityID(kPromoStyleSubtitleAccessibilityIdentifier), nil);
-}
-
-// Returns a matcher to the Set Up List Default Browser promo subtitle shown to
-// android switchers.
-id<GREYMatcher> AndroidSwitcherPromoSubtitle() {
-  return grey_allOf(
-      grey_text(GetNSString(
-          [ChromeEarlGrey isIPadIdiom]
-              ? IDS_IOS_FIRST_RUN_SEGMENTED_DEFAULT_BROWSER_ANDROID_SWITCHER_SUBTITLE_IPAD
-              : IDS_IOS_FIRST_RUN_SEGMENTED_DEFAULT_BROWSER_ANDROID_SWITCHER_SUBTITLE_IPHONE)),
-      grey_accessibilityID(kPromoStyleSubtitleAccessibilityIdentifier), nil);
-}
-
-// Returns a matcher to the Set Up List Default Browser promo title shown to
-// shopping users.
-id<GREYMatcher> ShopperPromoTitle() {
-  return grey_allOf(
-      grey_text(
-          GetNSString(IDS_IOS_SEGMENTED_DEFAULT_BROWSER_SCREEN_SHOPPER_TITLE)),
-      grey_accessibilityID(kPromoStyleTitleAccessibilityIdentifier), nil);
-}
-
-// Returns a matcher to the Set Up List Default Browser promo subtitle shown to
-// shopping users.
-id<GREYMatcher> ShopperPromoSubtitle() {
-  return grey_allOf(
-      grey_text(GetNSString(
-          IDS_IOS_SEGMENTED_DEFAULT_BROWSER_SCREEN_SHOPPER_SUBTITLE)),
-      grey_accessibilityID(kPromoStyleSubtitleAccessibilityIdentifier), nil);
+// Returns a matcher to the Default Browser Set Up List item default title.
+id<GREYMatcher> DefaultItemTitle() {
+  return grey_text(
+      GetNSString([ChromeEarlGrey isIPadIdiom]
+                      ? IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_TITLE_IPAD
+                      : IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_TITLE));
 }
 
 // Returns a matcher to the Set Up List Default Browser promo default title.
@@ -164,8 +87,6 @@
 
 - (AppLaunchConfiguration)appConfigurationForTestCase {
   AppLaunchConfiguration config;
-  // Enable Segmented Default Browser promo strings.
-  config.features_enabled.push_back(kSegmentedDefaultBrowserPromo);
   // TODO(crbug.com/379305809): Re-enable if kSetUpListInFirstRun is launched
   // with the Default Browser item.
   config.features_disabled.push_back(set_up_list::kSetUpListInFirstRun);
@@ -175,24 +96,6 @@
   // Relaunch app at each test to rewind the startup state.
   config.relaunch_policy = ForceRelaunchByCleanShutdown;
 
-  if ([self isRunningTest:@selector(testDesktopUserPromo)] ||
-      [self isRunningTest:@selector(testDesktopUserCompactedItem)] ||
-      [self isRunningTest:@selector(testDesktopUserSeeMoreItem)]) {
-    config.additional_args.push_back("-ForceExperienceForDeviceSwitcher");
-    config.additional_args.push_back("Desktop");
-  }
-  if ([self isRunningTest:@selector(testAndroidSwitcherPromo)] ||
-      [self isRunningTest:@selector(testAndroidSwitcherCompactedItem)] ||
-      [self isRunningTest:@selector(testAndroidSwitcherSeeMoreItem)]) {
-    config.additional_args.push_back("-ForceExperienceForDeviceSwitcher");
-    config.additional_args.push_back("AndroidPhone");
-  }
-  if ([self isRunningTest:@selector(testShopperPromo)] ||
-      [self isRunningTest:@selector(testShopperCompactedItem)] ||
-      [self isRunningTest:@selector(testShopperSeeMoreItem)]) {
-    config.additional_args.push_back("-ForceExperienceForShopper");
-    config.additional_args.push_back("true");
-  }
   return config;
 }
 
@@ -203,33 +106,6 @@
 
 #pragma mark - Tests
 
-// Tests that the text on the Default Browser compacted Set Up List item shown
-// to desktop users is correctly displayed.
-- (void)testDesktopUserCompactedItem {
-  [[EarlGrey selectElementWithMatcher:DefaultItemTitle()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:DeviceSwitcherDescription()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
-// Tests that the text on the Default Browser compacted Set Up List item shown
-// to android switchers is correctly displayed.
-- (void)testAndroidSwitcherCompactedItem {
-  [[EarlGrey selectElementWithMatcher:DefaultItemTitle()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:DeviceSwitcherDescription()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
-// Tests that the text on the Default Browser compacted Set Up List item shown
-// to shopping users is correctly displayed.
-- (void)testShopperCompactedItem {
-  [[EarlGrey selectElementWithMatcher:DefaultItemTitle()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:ShopperDescription()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
 // Tests that the default text on the Default Browser compacted Set Up List item
 // is correctly displayed.
 - (void)testDefaultCompactedItem {
@@ -239,36 +115,6 @@
       assertWithMatcher:grey_sufficientlyVisible()];
 }
 
-// Tests that the text on the Default Browser see more Set Up List item shown to
-// desktop users is correctly displayed.
-- (void)testDesktopUserSeeMoreItem {
-  [self openSeeMore];
-  [[EarlGrey selectElementWithMatcher:DefaultItemSeeMoreTitle()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:DeviceSwitcherSeeMoreDescription()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
-// Tests that the text on the Default Browser see more Set Up List item shown to
-// android switchers is correctly displayed.
-- (void)testAndroidSwitcherSeeMoreItem {
-  [self openSeeMore];
-  [[EarlGrey selectElementWithMatcher:DefaultItemSeeMoreTitle()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:DeviceSwitcherSeeMoreDescription()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
-// Tests that the text on the Default Browser see more Set Up List item shown to
-// shopping users is correctly displayed.
-- (void)testShopperSeeMoreItem {
-  [self openSeeMore];
-  [[EarlGrey selectElementWithMatcher:DefaultItemSeeMoreTitle()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:ShopperSeeMoreDescription()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
 // Tests that the default text on the Default Browser see more Set Up List item
 // is correctly displayed.
 - (void)testDefaultSeeMoreItem {
@@ -279,36 +125,6 @@
       assertWithMatcher:grey_sufficientlyVisible()];
 }
 
-// Tests that the text on the Set Up List Default Browser promo shown to desktop
-// users is correctly displayed.
-- (void)testDesktopUserPromo {
-  [self openPromo];
-  [[EarlGrey selectElementWithMatcher:DeviceSwitcherPromoTitle()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:DesktopUserPromoSubtitle()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
-// Tests that the text on the Set Up List Default Browser promo shown to android
-// switchers is correctly displayed.
-- (void)testAndroidSwitcherPromo {
-  [self openPromo];
-  [[EarlGrey selectElementWithMatcher:DeviceSwitcherPromoTitle()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:AndroidSwitcherPromoSubtitle()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
-// Tests that the text on the Set Up List Default Browser promo shown to
-// shopping users is correctly displayed.
-- (void)testShopperPromo {
-  [self openPromo];
-  [[EarlGrey selectElementWithMatcher:ShopperPromoTitle()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:ShopperPromoSubtitle()]
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
 // Tests that the default text on the Set Up List Default Browser promo is
 // correctly displayed.
 - (void)testDefaultPromo {
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_coordinator.h b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_coordinator.h
index 27b390a..847e9e98 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_coordinator.h
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_coordinator.h
@@ -8,11 +8,6 @@
 #import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h"
 #import "ios/chrome/common/ui/promo_style/promo_style_view_controller_delegate.h"
 
-namespace segmentation_platform {
-class DeviceSwitcherResultDispatcher;
-class SegmentationPlatformService;
-}  // namespace segmentation_platform
-
 @protocol SetUpListDefaultBrowserPromoCoordinatorDelegate;
 
 // A coordinator that handles the display of the Default Browser Promo for the
@@ -26,19 +21,11 @@
     delegate;
 
 // Creates a coordinator that uses `viewController` and `browser`. Uses
-// `application` to open the app's settings. Uses `segmentationService` and
-// `deviceSwitcherResultDispatcher` to retrieve segmentation data for
-// personalized messaging. Pass  `nullptr` to `segmentationService` and
-// `deviceSwitcherResultDispatcher` to not use segmentation features.
+// `application` to open the app's settings.
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                    browser:(Browser*)browser
                                application:(UIApplication*)application
-                       segmentationService:
-                           (segmentation_platform::SegmentationPlatformService*)
-                               segmentationService
-            deviceSwitcherResultDispatcher:
-                (segmentation_platform::DeviceSwitcherResultDispatcher*)
-                    dispatcher NS_DESIGNATED_INITIALIZER;
+    NS_DESIGNATED_INITIALIZER;
 
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                    browser:(Browser*)browser NS_UNAVAILABLE;
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_coordinator.mm b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_coordinator.mm
index 6ea69f016..632a12c5 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_coordinator.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_coordinator.mm
@@ -11,26 +11,20 @@
 #import "base/metrics/user_metrics_action.h"
 #import "components/feature_engagement/public/event_constants.h"
 #import "components/feature_engagement/public/tracker.h"
-#import "components/segmentation_platform/public/segmentation_platform_service.h"
 #import "ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/constants.h"
 #import "ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_coordinator_delegate.h"
-#import "ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_mediator.h"
 #import "ios/chrome/browser/default_browser/model/utils.h"
-#import "ios/chrome/browser/default_promo/ui_bundled/default_browser_instructions_view_controller.h"
 #import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
 #import "ios/chrome/browser/first_run/ui_bundled/default_browser/default_browser_screen_view_controller.h"
 #import "ios/chrome/browser/ntp/model/set_up_list_item_type.h"
 #import "ios/chrome/browser/ntp/model/set_up_list_prefs.h"
-#import "ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/browser/browser.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/features/features.h"
-#import "ios/chrome/common/ui/confirmation_alert/confirmation_alert_action_handler.h"
 
-@interface SetUpListDefaultBrowserPromoCoordinator () <
-    ConfirmationAlertActionHandler>
+@interface SetUpListDefaultBrowserPromoCoordinator ()
 @end
 
 using base::RecordAction;
@@ -39,40 +33,21 @@
 
 @implementation SetUpListDefaultBrowserPromoCoordinator {
   // The Default Browser view controller.
-  UIViewController* _viewController;
+  DefaultBrowserScreenViewController* _viewController;
 
   // Application is used to open the OS settings for this app.
   UIApplication* _application;
 
   // Whether or not the Set Up List Item should be marked complete.
   BOOL _markItemComplete;
-
-  raw_ptr<segmentation_platform::DeviceSwitcherResultDispatcher>
-      _deviceSwitcherResultDispatcher;
-  raw_ptr<segmentation_platform::SegmentationPlatformService>
-      _segmentationService;
-  SetUpListDefaultBrowserPromoMediator* _mediator;
-
-  // TODO: (crbug.com/357867254) Transparent view to block user interaction
-  // while waiting for classification results. This ivar is a temporary
-  // solution.
-  UIView* _transparentView;
 }
 
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                    browser:(Browser*)browser
-                               application:(UIApplication*)application
-                       segmentationService:
-                           (segmentation_platform::SegmentationPlatformService*)
-                               segmentationService
-            deviceSwitcherResultDispatcher:
-                (segmentation_platform::DeviceSwitcherResultDispatcher*)
-                    dispatcher {
+                               application:(UIApplication*)application {
   self = [super initWithBaseViewController:viewController browser:browser];
   if (self) {
     _application = application;
-    _segmentationService = segmentationService;
-    _deviceSwitcherResultDispatcher = dispatcher;
   }
   return self;
 }
@@ -83,26 +58,7 @@
   RecordAction(UserMetricsAction("IOS.DefaultBrowserPromo.SetUpList.Appear"));
   [self recordDefaultBrowserPromoShown];
 
-  if (IsSegmentedDefaultBrowserPromoEnabled()) {
-    CHECK(_segmentationService);
-    CHECK(_deviceSwitcherResultDispatcher);
-    _mediator = [[SetUpListDefaultBrowserPromoMediator alloc]
-           initWithSegmentationService:_segmentationService
-        deviceSwitcherResultDispatcher:_deviceSwitcherResultDispatcher];
-
-    // Present a transparent view to block UI interaction until promo presents.
-    _transparentView =
-        [[UIView alloc] initWithFrame:self.baseViewController.view.bounds];
-    _transparentView.backgroundColor = [UIColor clearColor];
-    [self.baseViewController.view addSubview:_transparentView];
-
-    __weak __typeof(self) weakSelf = self;
-    [_mediator retrieveUserSegmentWithCompletion:^{
-      [weakSelf showPromo];
-    }];
-  } else {
-    [self showPromo];
-  }
+  [self showPromo];
 }
 
 - (void)stop {
@@ -121,51 +77,14 @@
 
   _viewController = nil;
   _application = nil;
-  _transparentView = nil;
-  _segmentationService = nullptr;
-  _deviceSwitcherResultDispatcher = nullptr;
-  if (![self shouldAnimate]) {
-    _mediator.consumer = nil;
-  }
-  [_mediator disconnect];
-  _mediator = nil;
   self.delegate = nil;
 
   [super stop];
 }
 
-#pragma mark - ConfirmationAlertActionHandler
-
-- (void)confirmationAlertPrimaryAction {
-  base::UmaHistogramEnumeration(
-      kSetUpListDefaultBrowserPromoAction,
-      SegmentedDefaultBrowserPromoAction::kAnimatedPromoAccept);
-  [_mediator didTapPrimaryActionButton];
-  _markItemComplete = YES;
-  [self.delegate setUpListDefaultBrowserPromoDidFinish:YES];
-}
-
-- (void)confirmationAlertSecondaryAction {
-  base::UmaHistogramEnumeration(
-      kSetUpListDefaultBrowserPromoAction,
-      SegmentedDefaultBrowserPromoAction::kAnimatedPromoDismiss);
-  _markItemComplete = YES;
-  [self.delegate setUpListDefaultBrowserPromoDidFinish:NO];
-}
-
-- (void)confirmationAlertTertiaryAction {
-  base::UmaHistogramEnumeration(
-      kSetUpListDefaultBrowserPromoAction,
-      SegmentedDefaultBrowserPromoAction::kAnimatedPromoDismiss);
-  [self.delegate setUpListDefaultBrowserPromoDidFinish:NO];
-}
-
 #pragma mark - PromoStyleViewControllerDelegate
 
 - (void)didTapPrimaryActionButton {
-  base::UmaHistogramEnumeration(
-      kSetUpListDefaultBrowserPromoAction,
-      SegmentedDefaultBrowserPromoAction::kStaticPromoAccept);
   RecordDefaultBrowserPromoLastAction(
       IOSDefaultBrowserPromoAction::kActionButton);
   RecordAction(UserMetricsAction("IOS.DefaultBrowserPromo.SetUpList.Accepted"));
@@ -179,9 +98,6 @@
 }
 
 - (void)didTapSecondaryActionButton {
-  base::UmaHistogramEnumeration(
-      kSetUpListDefaultBrowserPromoAction,
-      SegmentedDefaultBrowserPromoAction::kStaticPromoDismiss);
   RecordDefaultBrowserPromoLastAction(IOSDefaultBrowserPromoAction::kCancel);
   RecordAction(UserMetricsAction("IOS.DefaultBrowserPromo.SetUpList.Dismiss"));
   [self logDefaultBrowserFullscreenPromoHistogramForAction:
@@ -218,34 +134,9 @@
 
 // Presents the default browser promo.
 - (void)showPromo {
-  [_transparentView removeFromSuperview];
-  _transparentView = nil;
+  _viewController = [[DefaultBrowserScreenViewController alloc] init];
+  _viewController.delegate = self;
 
-  if ([self shouldAnimate]) {
-    base::UmaHistogramEnumeration(
-        kSetUpListDefaultBrowserPromoAction,
-        SegmentedDefaultBrowserPromoAction::kAnimatedPromoAppear);
-    DefaultBrowserInstructionsViewController* animatedViewController = [self
-        createAnimatedViewControllerWithTitle:[_mediator retrievePromoTitle]];
-
-    CHECK(animatedViewController);
-    CHECK(!_transparentView);
-
-    _viewController = animatedViewController;
-  } else {
-    base::UmaHistogramEnumeration(
-        kSetUpListDefaultBrowserPromoAction,
-        SegmentedDefaultBrowserPromoAction::kStaticPromoAppear);
-    DefaultBrowserScreenViewController* staticViewController =
-        [[DefaultBrowserScreenViewController alloc] init];
-    staticViewController.delegate = self;
-
-    if (IsSegmentedDefaultBrowserPromoEnabled()) {
-      _mediator.consumer = staticViewController;
-    }
-
-    _viewController = staticViewController;
-  }
   [_viewController setModalPresentationStyle:UIModalPresentationFormSheet];
   [self.baseViewController presentViewController:_viewController
                                         animated:YES
@@ -254,23 +145,4 @@
   _viewController.presentationController.delegate = self;
 }
 
-// Create the animated View Controller.
-- (DefaultBrowserInstructionsViewController*)
-    createAnimatedViewControllerWithTitle:(NSString*)promoTitle {
-  return [[DefaultBrowserInstructionsViewController alloc]
-      initWithDismissButton:YES
-           hasRemindMeLater:NO
-                   hasSteps:NO
-              actionHandler:self
-                  titleText:promoTitle];
-}
-
-// Determine which version of the Default Browser Promo should be shown.
-// Returns `YES` if the animated Default Browser Promo should be shown.
-- (BOOL)shouldAnimate {
-  return (IsSegmentedDefaultBrowserPromoEnabled() &&
-          (SegmentedDefaultBrowserExperimentTypeEnabled() ==
-           SegmentedDefaultBrowserExperimentType::kAnimatedPromo));
-}
-
 @end
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_coordinator_unittest.mm b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_coordinator_unittest.mm
index cc98f87f..1cfa844 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_coordinator_unittest.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_coordinator_unittest.mm
@@ -37,11 +37,9 @@
     UIView.animationsEnabled = NO;
     mock_application_ = OCMStrictClassMock([UIApplication class]);
     coordinator_ = [[SetUpListDefaultBrowserPromoCoordinator alloc]
-            initWithBaseViewController:window_.rootViewController
-                               browser:browser_.get()
-                           application:mock_application_
-                   segmentationService:nullptr
-        deviceSwitcherResultDispatcher:nullptr];
+        initWithBaseViewController:window_.rootViewController
+                           browser:browser_.get()
+                       application:mock_application_];
     delegate_ = OCMProtocolMock(
         @protocol(SetUpListDefaultBrowserPromoCoordinatorDelegate));
     coordinator_.delegate = delegate_;
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_mediator.h b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_mediator.h
deleted file mode 100644
index d1acfcb..0000000
--- a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_mediator.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_CONTENT_SUGGESTIONS_UI_BUNDLED_SET_UP_LIST_SET_UP_LIST_DEFAULT_BROWSER_PROMO_MEDIATOR_H_
-#define IOS_CHROME_BROWSER_CONTENT_SUGGESTIONS_UI_BUNDLED_SET_UP_LIST_SET_UP_LIST_DEFAULT_BROWSER_PROMO_MEDIATOR_H_
-
-#import <UIKit/UIKit.h>
-
-#import "base/ios/block_types.h"
-
-namespace segmentation_platform {
-class SegmentationPlatformService;
-class DeviceSwitcherResultDispatcher;
-}  // namespace segmentation_platform
-
-@protocol DefaultBrowserScreenConsumer;
-
-// Mediator for presenting segmented default browser promo.
-@interface SetUpListDefaultBrowserPromoMediator : NSObject
-
-// Main consumer for this mediator.
-@property(nonatomic, weak) id<DefaultBrowserScreenConsumer> consumer;
-
-// Initializer with 'segmentationService' and `deviceSwitcherResultDispatcher`
-// for retrieving segmentation info from Segmentation Platform.
-- (instancetype)initWithSegmentationService:
-                    (segmentation_platform::SegmentationPlatformService*)
-                        segmentationService
-             deviceSwitcherResultDispatcher:
-                 (segmentation_platform::DeviceSwitcherResultDispatcher*)
-                     dispatcher NS_DESIGNATED_INITIALIZER;
-
-- (instancetype)init NS_UNAVAILABLE;
-
-// Disconnects this mediator.
-- (void)disconnect;
-
-// Sets the user segment retrieved from the Segmentation Platform.
-- (void)retrieveUserSegmentWithCompletion:(ProceduralBlock)completion;
-
-// Handles user tap on primary action. Allows the user to open the iOS settings.
-- (void)didTapPrimaryActionButton;
-
-// Returns the personalized messaging for the animated Default Browser Promo.
-- (NSString*)retrievePromoTitle;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_CONTENT_SUGGESTIONS_UI_BUNDLED_SET_UP_LIST_SET_UP_LIST_DEFAULT_BROWSER_PROMO_MEDIATOR_H_
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_mediator.mm b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_mediator.mm
deleted file mode 100644
index 0e81a72f..0000000
--- a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_mediator.mm
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_mediator.h"
-
-#import "components/segmentation_platform/embedder/default_model/device_switcher_model.h"
-#import "components/segmentation_platform/embedder/default_model/device_switcher_result_dispatcher.h"
-#import "components/segmentation_platform/public/constants.h"
-#import "components/segmentation_platform/public/result.h"
-#import "components/segmentation_platform/public/segmentation_platform_service.h"
-#import "ios/chrome/browser/first_run/ui_bundled/default_browser/default_browser_screen_consumer.h"
-#import "ios/chrome/browser/segmentation_platform/model/segmentation_platform_service_factory.h"
-#import "ios/chrome/browser/segmentation_platform/model/segmented_default_browser_utils.h"
-#import "ios/chrome/browser/shared/public/features/features.h"
-#import "ui/base/l10n/l10n_util.h"
-
-@implementation SetUpListDefaultBrowserPromoMediator {
-  raw_ptr<segmentation_platform::SegmentationPlatformService>
-      _segmentationService;
-  raw_ptr<segmentation_platform::DeviceSwitcherResultDispatcher>
-      _deviceSwitcherResultDispatcher;
-  segmentation_platform::DefaultBrowserUserSegment _userSegment;
-}
-
-#pragma mark - Public
-
-- (instancetype)initWithSegmentationService:
-                    (segmentation_platform::SegmentationPlatformService*)
-                        segmentationService
-             deviceSwitcherResultDispatcher:
-                 (segmentation_platform::DeviceSwitcherResultDispatcher*)
-                     dispatcher {
-  self = [super init];
-  if (self) {
-    _segmentationService = segmentationService;
-    _deviceSwitcherResultDispatcher = dispatcher;
-  }
-  return self;
-}
-
-- (void)disconnect {
-  _segmentationService = nullptr;
-  _deviceSwitcherResultDispatcher = nullptr;
-}
-
-// Retrieves the title for the animated animated Default Browser Promo.
-- (NSString*)retrievePromoTitle {
-  return l10n_util::GetNSString(
-      GetDefaultBrowserGenericPromoTitleStringID(_userSegment));
-}
-
-- (void)setConsumer:(id<DefaultBrowserScreenConsumer>)consumer {
-  _consumer = consumer;
-
-  if (!_consumer) {
-    return;
-  }
-
-  // Sets the Default Browser static view title and subtitle to the consumer
-  // with targeted messaging based on the user's segment.
-  if (IsSegmentedDefaultBrowserPromoEnabled() &&
-      (SegmentedDefaultBrowserExperimentTypeEnabled() ==
-       SegmentedDefaultBrowserExperimentType::kStaticPromo)) {
-    [_consumer setPromoTitle:l10n_util::GetNSString(
-                                 GetFirstRunDefaultBrowserScreenTitleStringID(
-                                     _userSegment))];
-
-    [_consumer
-        setPromoSubtitle:l10n_util::GetNSString(
-                             GetFirstRunDefaultBrowserScreenSubtitleStringID(
-                                 _userSegment))];
-  }
-}
-
-- (void)didTapPrimaryActionButton {
-  [[UIApplication sharedApplication]
-                openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]
-                options:{}
-      completionHandler:nil];
-}
-
-#pragma mark - Private
-
-// Retrieves user segmentation data from the Segmentation Platform.
-- (void)retrieveUserSegmentWithCompletion:(ProceduralBlock)completion {
-  CHECK(_segmentationService);
-  CHECK(_deviceSwitcherResultDispatcher);
-  segmentation_platform::PredictionOptions options =
-      segmentation_platform::PredictionOptions::ForCached();
-  segmentation_platform::ClassificationResult deviceSwitcherResult =
-      _deviceSwitcherResultDispatcher->GetCachedClassificationResult();
-
-  __weak __typeof(self) weakSelf = self;
-  auto classificationResultCallback = base::BindOnce(
-      [](__typeof(self) strongSelf,
-         segmentation_platform::ClassificationResult deviceSwitcherResult,
-         ProceduralBlock completion,
-         const segmentation_platform::ClassificationResult& shopperResult) {
-        [strongSelf didReceiveShopperSegmentationResult:shopperResult
-                                   deviceSwitcherResult:deviceSwitcherResult];
-        if (completion) {
-          completion();
-        }
-      },
-      weakSelf, deviceSwitcherResult, completion);
-  _segmentationService->GetClassificationResult(
-      segmentation_platform::kShoppingUserSegmentationKey, options, nullptr,
-      std::move(classificationResultCallback));
-}
-
-// Sets user's highest priority targeted segment retrieved from the Segmentation
-// Platform.
-- (void)didReceiveShopperSegmentationResult:
-            (const segmentation_platform::ClassificationResult&)shopperResult
-                       deviceSwitcherResult:
-                           (const segmentation_platform::ClassificationResult&)
-                               deviceSwitcherResult {
-  _userSegment =
-      GetDefaultBrowserUserSegment(&deviceSwitcherResult, &shopperResult);
-}
-
-@end
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_mediator_unittest.mm b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_mediator_unittest.mm
deleted file mode 100644
index a92d604..0000000
--- a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_mediator_unittest.mm
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_default_browser_promo_mediator.h"
-
-#import "base/test/gmock_callback_support.h"
-#import "base/test/scoped_feature_list.h"
-#import "components/segmentation_platform/embedder/default_model/device_switcher_model.h"
-#import "components/segmentation_platform/embedder/default_model/device_switcher_result_dispatcher.h"
-#import "components/segmentation_platform/public/testing/mock_segmentation_platform_service.h"
-#import "ios/chrome/browser/first_run/ui_bundled/default_browser/default_browser_screen_consumer.h"
-#import "ios/chrome/browser/segmentation_platform/model/segmented_default_browser_test_utils.h"
-#import "ios/chrome/browser/segmentation_platform/model/segmented_default_browser_utils.h"
-#import "ios/chrome/browser/shared/public/features/features.h"
-#import "ios/chrome/grit/ios_branded_strings.h"
-#import "ios/chrome/grit/ios_strings.h"
-#import "ios/chrome/test/ios_chrome_scoped_testing_local_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"
-#import "ui/base/l10n/l10n_util.h"
-
-using base::test::RunOnceCallback;
-using base::test::ScopedFeatureList;
-using l10n_util::GetNSString;
-using syncer::FakeDeviceInfoTracker;
-using testing::_;
-using testing::NiceMock;
-using testing::Return;
-
-namespace segmentation_platform {
-
-namespace test {
-
-// Test class for SetUpListDefaultBrowserPromoMediator.
-class SetUpListDefaultBrowserPromoMediatorTest : public PlatformTest {
- public:
-  void SetUp() override {
-    PlatformTest::SetUp();
-    prefs_ = std::make_unique<TestingPrefServiceSimple>();
-    scoped_feature_list_.InitAndEnableFeature(kSegmentedDefaultBrowserPromo);
-    DeviceSwitcherResultDispatcher::RegisterProfilePrefs(prefs_->registry());
-    device_info_tracker_ = std::make_unique<FakeDeviceInfoTracker>();
-    consumer_mock_ =
-        OCMStrictProtocolMock(@protocol(DefaultBrowserScreenConsumer));
-  }
-
-  void TearDown() override {
-    EXPECT_OCMOCK_VERIFY((id)consumer_mock_);
-    PlatformTest::TearDown();
-    mediator_to_test_.consumer = nil;
-    [mediator_to_test_ disconnect];
-    mediator_to_test_ = nil;
-  }
-
-  void SetUpMediatorTest(DefaultBrowserUserSegment label,
-                         PredictionStatus status) {
-    ClassificationResult device_switcher_result(status);
-    ClassificationResult shopper_result(status);
-    device_switcher_result.ordered_labels =
-        GetDeviceSwitcherOrderedLabelsForTesting(label);
-    shopper_result.ordered_labels = GetShopperOrderedLabelsForTesting(label);
-
-    NiceMock<MockDeviceSwitcherResultDispatcher>
-        device_switcher_result_dispatcher(&segmentation_platform_service_,
-                                          device_info_tracker_.get(),
-                                          prefs_.get(), &field_trial_register_);
-
-    EXPECT_CALL(device_switcher_result_dispatcher,
-                GetCachedClassificationResult())
-        .WillOnce(Return(device_switcher_result));
-
-    EXPECT_CALL(segmentation_platform_service_,
-                GetClassificationResult(_, _, _, _))
-        .WillOnce(RunOnceCallback<3>(shopper_result));
-
-    mediator_to_test_ = [[SetUpListDefaultBrowserPromoMediator alloc]
-           initWithSegmentationService:&segmentation_platform_service_
-        deviceSwitcherResultDispatcher:&device_switcher_result_dispatcher];
-    [mediator_to_test_ retrieveUserSegmentWithCompletion:^{
-    }];
-  }
-
-  void ExpectTextForSegment(DefaultBrowserUserSegment label) {
-    OCMExpect([consumer_mock_
-        setPromoTitle:GetNSString(GetFirstRunDefaultBrowserScreenTitleStringID(
-                          label))]);
-    OCMExpect([consumer_mock_
-        setPromoSubtitle:GetNSString(
-                             GetFirstRunDefaultBrowserScreenSubtitleStringID(
-                                 label))]);
-    mediator_to_test_.consumer = consumer_mock_;
-  }
-
- protected:
-  web::WebTaskEnvironment task_environment_;
-  IOSChromeScopedTestingLocalState scoped_testing_local_state_;
-  ScopedFeatureList scoped_feature_list_;
-  std::unique_ptr<TestingPrefServiceSimple> prefs_;
-  std::unique_ptr<FakeDeviceInfoTracker> device_info_tracker_;
-  NiceMock<MockSegmentationPlatformService> segmentation_platform_service_;
-  NiceMock<MockFieldTrialRegister> field_trial_register_;
-  SetUpListDefaultBrowserPromoMediator* mediator_to_test_;
-  id<DefaultBrowserScreenConsumer> consumer_mock_;
-};
-
-#pragma mark - Unit Tests
-
-// Tests that consumer is correctly informed if a user's retrieved segment is
-// Desktop User.
-TEST_F(SetUpListDefaultBrowserPromoMediatorTest, UserIsDesktopUser) {
-  SetUpMediatorTest(DefaultBrowserUserSegment::kDesktopUser,
-                    PredictionStatus::kSucceeded);
-  ExpectTextForSegment(DefaultBrowserUserSegment::kDesktopUser);
-}
-
-// Tests that consumer is correctly informed if a user's retrieved segment is
-// Android switcher.
-TEST_F(SetUpListDefaultBrowserPromoMediatorTest, UserIsAndroidSwitcher) {
-  SetUpMediatorTest(DefaultBrowserUserSegment::kAndroidSwitcher,
-                    PredictionStatus::kSucceeded);
-  ExpectTextForSegment(DefaultBrowserUserSegment::kAndroidSwitcher);
-}
-
-// Tests that consumer is correctly informed if a user's retrieved segment is
-// Shopper.
-TEST_F(SetUpListDefaultBrowserPromoMediatorTest, UserIsShopper) {
-  SetUpMediatorTest(DefaultBrowserUserSegment::kShopper,
-                    PredictionStatus::kSucceeded);
-  ExpectTextForSegment(DefaultBrowserUserSegment::kShopper);
-}
-
-// Tests that consumer is correctly informed if a user's retrieved segment is
-// not a targeted segment.
-TEST_F(SetUpListDefaultBrowserPromoMediatorTest, UserIsNotInTargetedSegment) {
-  SetUpMediatorTest(DefaultBrowserUserSegment::kDefault,
-                    PredictionStatus::kSucceeded);
-  ExpectTextForSegment(DefaultBrowserUserSegment::kDefault);
-}
-
-// Tests that consumer is correctly informed if user classification is not ready
-// yet.
-TEST_F(SetUpListDefaultBrowserPromoMediatorTest, ClassificationNotReady) {
-  SetUpMediatorTest(DefaultBrowserUserSegment::kDesktopUser,
-                    PredictionStatus::kNotReady);
-  ExpectTextForSegment(DefaultBrowserUserSegment::kDefault);
-}
-
-// Tests that consumer is correctly informed if user classification retrieval
-// fails.
-TEST_F(SetUpListDefaultBrowserPromoMediatorTest, ClassificationFailed) {
-  SetUpMediatorTest(DefaultBrowserUserSegment::kDesktopUser,
-                    PredictionStatus::kFailed);
-  ExpectTextForSegment(DefaultBrowserUserSegment::kDefault);
-}
-
-}  // namespace test
-
-}  // namespace segmentation_platform
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_item_view.mm b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_item_view.mm
index b4a0c91..2bd91e75 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_item_view.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_item_view.mm
@@ -16,7 +16,6 @@
 #import "ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_item_view_data.h"
 #import "ios/chrome/browser/ntp/model/set_up_list_item.h"
 #import "ios/chrome/browser/ntp/model/set_up_list_item_type.h"
-#import "ios/chrome/browser/segmentation_platform/model/segmented_default_browser_utils.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/ui/elements/crossfade_label.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
@@ -89,9 +88,7 @@
       int syncString =
           IDS_IOS_SET_UP_LIST_SIGN_IN_SYNC_SHORT_DESCRIPTION_NO_SYNC;
       int defaultBrowserString =
-          IsSegmentedDefaultBrowserPromoEnabled()
-              ? GetSetUpListDefaultBrowserDescriptionStringID(data.userSegment)
-              : IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_SHORT_DESCRIPTION;
+          IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_SHORT_DESCRIPTION;
       _config = {
           YES,
           NO,
@@ -106,9 +103,7 @@
     } else if (data.heroCellMagicStackLayout) {
       int syncString = IDS_IOS_IDENTITY_DISC_SIGN_IN_PROMO_LABEL;
       int defaultBrowserString =
-          IsSegmentedDefaultBrowserPromoEnabled()
-              ? GetSetUpListDefaultBrowserDescriptionStringID(data.userSegment)
-              : IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_MAGIC_STACK_DESCRIPTION;
+          IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_MAGIC_STACK_DESCRIPTION;
       _config = {
           NO,
           YES,
@@ -124,9 +119,7 @@
       // Normal ViewConfig.
       int syncString = IDS_IOS_IDENTITY_DISC_SIGN_IN_PROMO_LABEL;
       int defaultBrowserString =
-          IsSegmentedDefaultBrowserPromoEnabled()
-              ? GetSetUpListDefaultBrowserDescriptionStringID(data.userSegment)
-              : IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_DESCRIPTION;
+          IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_DESCRIPTION;
       _config = {
           NO,
           NO,
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_item_view_data.h b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_item_view_data.h
index ba141cbf..6b2389d2 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_item_view_data.h
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_item_view_data.h
@@ -29,11 +29,6 @@
 // Stack.
 @property(nonatomic, assign) BOOL heroCellMagicStackLayout;
 
-// User classification retrieved by the Segmentation Platform. Used to
-// personalize Set Up List item messaging.
-@property(nonatomic, assign)
-    segmentation_platform::DefaultBrowserUserSegment userSegment;
-
 // YES if price tracking is enabled for the current user.
 @property(nonatomic, assign) BOOL priceTrackingEnabled;
 
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_mediator.h b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_mediator.h
index d8f2e60..0acc6ec 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_mediator.h
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_mediator.h
@@ -19,11 +19,6 @@
 @class SetUpListItem;
 @class SetUpListItemViewData;
 
-namespace segmentation_platform {
-class DeviceSwitcherResultDispatcher;
-class SegmentationPlatformService;
-}  // namespace segmentation_platform
-
 namespace signin {
 class IdentityManager;
 }  // namespace signin
@@ -74,20 +69,13 @@
 @property(nonatomic, weak)
     ContentSuggestionsMetricsRecorder* contentSuggestionsMetricsRecorder;
 
-// Initializer with optional `segmentationService` and
-// `deviceSwitcherResultDispatcher` used for personalizing messaging in the Set
-// Up List Default Browser item.
+// Default initializer.
 - (instancetype)initWithPrefService:(PrefService*)prefService
                         syncService:(syncer::SyncService*)syncService
                     identityManager:(signin::IdentityManager*)identityManager
               authenticationService:(AuthenticationService*)authService
                          sceneState:(SceneState*)sceneState
               isDefaultSearchEngine:(BOOL)isDefaultSearchEngine
-                segmentationService:
-                    (segmentation_platform::SegmentationPlatformService*)
-                        segmentationService
-     deviceSwitcherResultDispatcher:
-         (segmentation_platform::DeviceSwitcherResultDispatcher*)dispatcher
                priceTrackingEnabled:(BOOL)priceTrackingEnabled
     NS_DESIGNATED_INITIALIZER;
 
@@ -115,9 +103,6 @@
 // Indicates to the mediator to disable SetUpList entirely.
 - (void)disableModule;
 
-// Retrieves user segmentation data from the Segmentation Platform.
-- (void)retrieveUserSegment;
-
 @end
 
 #endif  // IOS_CHROME_BROWSER_CONTENT_SUGGESTIONS_UI_BUNDLED_SET_UP_LIST_SET_UP_LIST_MEDIATOR_H_
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_mediator.mm b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_mediator.mm
index 8719257..d20c4489 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_mediator.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_mediator.mm
@@ -11,11 +11,6 @@
 #import "base/strings/sys_string_conversions.h"
 #import "components/prefs/ios/pref_observer_bridge.h"
 #import "components/prefs/pref_service.h"
-#import "components/segmentation_platform/embedder/default_model/device_switcher_model.h"
-#import "components/segmentation_platform/embedder/default_model/device_switcher_result_dispatcher.h"
-#import "components/segmentation_platform/public/constants.h"
-#import "components/segmentation_platform/public/result.h"
-#import "components/segmentation_platform/public/segmentation_platform_service.h"
 #import "components/signin/public/identity_manager/identity_manager.h"
 #import "components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h"
 #import "ios/chrome/browser/content_notification/model/content_notification_util.h"
@@ -35,7 +30,6 @@
 #import "ios/chrome/browser/ntp/model/set_up_list_item_type.h"
 #import "ios/chrome/browser/ntp/model/set_up_list_prefs.h"
 #import "ios/chrome/browser/push_notification/model/push_notification_settings_util.h"
-#import "ios/chrome/browser/segmentation_platform/model/segmented_default_browser_utils.h"
 #import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
@@ -120,14 +114,6 @@
   SceneState* _sceneState;
   SetUpListConsumerList* _consumers;
   NSArray<SetUpListConfig*>* _setUpListConfigs;
-  // Components for retrieving user segmentation information from the
-  // Segmentation Platform.
-  raw_ptr<segmentation_platform::SegmentationPlatformService>
-      _segmentationService;
-  raw_ptr<segmentation_platform::DeviceSwitcherResultDispatcher>
-      _deviceSwitcherResultDispatcher;
-  // User segment retrieved by the Segmentation Platform.
-  segmentation_platform::DefaultBrowserUserSegment _userSegment;
   // YES if price tracking is enabled for the current user.
   BOOL _priceTrackingEnabled;
 }
@@ -140,11 +126,6 @@
               authenticationService:(AuthenticationService*)authService
                          sceneState:(SceneState*)sceneState
               isDefaultSearchEngine:(BOOL)isDefaultSearchEngine
-                segmentationService:
-                    (segmentation_platform::SegmentationPlatformService*)
-                        segmentationService
-     deviceSwitcherResultDispatcher:
-         (segmentation_platform::DeviceSwitcherResultDispatcher*)dispatcher
                priceTrackingEnabled:(BOOL)priceTrackingEnabled {
   self = [super init];
   if (self) {
@@ -201,10 +182,6 @@
     _sceneState = sceneState;
     [_sceneState addObserver:self];
 
-    if (IsSegmentedDefaultBrowserPromoEnabled()) {
-      _segmentationService = segmentationService;
-      _deviceSwitcherResultDispatcher = dispatcher;
-    }
     BOOL isContentNotificationEnabled =
         IsContentNotificationExperimentEnabled() &&
         IsContentNotificationSetUpListEnabled(
@@ -226,8 +203,6 @@
 }
 
 - (void)disconnect {
-  _segmentationService = nullptr;
-  _deviceSwitcherResultDispatcher = nullptr;
   _authenticationService = nullptr;
   _authServiceObserverBridge.reset();
   _syncObserverBridge.reset();
@@ -259,9 +234,6 @@
     SetUpListItemViewData* item =
         [[SetUpListItemViewData alloc] initWithType:model.type
                                            complete:model.complete];
-    if (IsSegmentedDefaultBrowserPromoEnabled()) {
-      [item setUserSegment:_userSegment];
-    }
     item.priceTrackingEnabled = _priceTrackingEnabled;
     [allItems addObject:item];
   }
@@ -343,30 +315,6 @@
   return _setUpListConfigs;
 }
 
-- (void)retrieveUserSegment {
-  CHECK(_segmentationService);
-  CHECK(_deviceSwitcherResultDispatcher);
-  segmentation_platform::PredictionOptions options =
-      segmentation_platform::PredictionOptions::ForCached();
-
-  segmentation_platform::ClassificationResult deviceSwitcherResult =
-      _deviceSwitcherResultDispatcher->GetCachedClassificationResult();
-
-  __weak __typeof(self) weakSelf = self;
-  auto classificationResultCallback = base::BindOnce(
-      [](__typeof(self) strongSelf,
-         segmentation_platform::ClassificationResult deviceSwitcherResult,
-
-         const segmentation_platform::ClassificationResult& shopperResult) {
-        [strongSelf didReceiveShopperSegmentationResult:shopperResult
-                                   deviceSwitcherResult:deviceSwitcherResult];
-      },
-      weakSelf, deviceSwitcherResult);
-  _segmentationService->GetClassificationResult(
-      segmentation_platform::kShoppingUserSegmentationKey, options, nullptr,
-      std::move(classificationResultCallback));
-}
-
 #pragma mark - SetUpListDelegate
 
 - (void)setUpListItemDidComplete:(SetUpListItem*)item
@@ -497,9 +445,6 @@
         [[SetUpListItemViewData alloc] initWithType:model.type
                                            complete:model.complete];
 
-    if (IsSegmentedDefaultBrowserPromoEnabled()) {
-      [item setUserSegment:_userSegment];
-    }
     item.priceTrackingEnabled = _priceTrackingEnabled;
     [items addObject:item];
   }
@@ -513,9 +458,6 @@
         [[SetUpListItemViewData alloc] initWithType:model.type
                                            complete:model.complete];
 
-    if (IsSegmentedDefaultBrowserPromoEnabled()) {
-      [item setUserSegment:_userSegment];
-    }
     item.priceTrackingEnabled = _priceTrackingEnabled;
     [items addObject:item];
   }
@@ -576,17 +518,6 @@
       GaiaId(identity.gaiaID), _prefService);
 }
 
-// Sets user's highest priority segment retrieved from the Segmentation
-// Platform.
-- (void)didReceiveShopperSegmentationResult:
-            (const segmentation_platform::ClassificationResult&)shopperResult
-                       deviceSwitcherResult:
-                           (const segmentation_platform::ClassificationResult&)
-                               deviceSwitcherResult {
-  _userSegment =
-      GetDefaultBrowserUserSegment(&deviceSwitcherResult, &shopperResult);
-}
-
 // Returns YES if the current configs contains an item with the given `type`.
 - (BOOL)configsContainItem:(SetUpListItemType)type {
   for (SetUpListConfig* config in [self setUpListConfigs]) {
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_show_more_item_view.mm b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_show_more_item_view.mm
index 114ee1e6..1a07d13 100644
--- a/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_show_more_item_view.mm
+++ b/ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_show_more_item_view.mm
@@ -11,7 +11,6 @@
 #import "ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_item_view_data.h"
 #import "ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/set_up_list_tap_delegate.h"
 #import "ios/chrome/browser/ntp/model/set_up_list_item_type.h"
-#import "ios/chrome/browser/segmentation_platform/model/segmented_default_browser_utils.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/ui/elements/crossfade_label.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
@@ -242,9 +241,7 @@
       return l10n_util::GetNSString(IDS_IOS_IDENTITY_DISC_SIGN_IN_PROMO_LABEL);
     case SetUpListItemType::kDefaultBrowser:
       return l10n_util::GetNSString(
-          IsSegmentedDefaultBrowserPromoEnabled()
-              ? GetSetUpListDefaultBrowserDescriptionStringID(_data.userSegment)
-              : IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_SEE_MORE_DESCRIPTION);
+          IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_SEE_MORE_DESCRIPTION);
     case SetUpListItemType::kAutofill:
       return l10n_util::GetNSString(
           IDS_IOS_SET_UP_LIST_AUTOFILL_SEE_MORE_DESCRIPTION);
diff --git a/ios/chrome/browser/crash_report/model/breadcrumbs/breadcrumb_manager_browser_agent.mm b/ios/chrome/browser/crash_report/model/breadcrumbs/breadcrumb_manager_browser_agent.mm
index 49ee7fc..192055c 100644
--- a/ios/chrome/browser/crash_report/model/breadcrumbs/breadcrumb_manager_browser_agent.mm
+++ b/ios/chrome/browser/crash_report/model/breadcrumbs/breadcrumb_manager_browser_agent.mm
@@ -39,7 +39,7 @@
 const char kBreadcrumbOverlayJsPrompt[] = "#js-prompt";
 
 BreadcrumbManagerBrowserAgent::BreadcrumbManagerBrowserAgent(Browser* browser)
-    : browser_(browser) {
+    : BrowserUserData(browser), browser_(browser) {
   browser_->AddObserver(this);
   browser_->GetWebStateList()->AddObserver(this);
 
diff --git a/ios/chrome/browser/credential_provider/model/credential_provider_browser_agent.mm b/ios/chrome/browser/credential_provider/model/credential_provider_browser_agent.mm
index d5f32f3..40e6cc1 100644
--- a/ios/chrome/browser/credential_provider/model/credential_provider_browser_agent.mm
+++ b/ios/chrome/browser/credential_provider/model/credential_provider_browser_agent.mm
@@ -23,7 +23,8 @@
 static constexpr base::TimeDelta kRecentlyAddedDelay = base::Seconds(5);
 
 CredentialProviderBrowserAgent::CredentialProviderBrowserAgent(Browser* browser)
-    : browser_(browser),
+    : BrowserUserData(browser),
+      browser_(browser),
       model_(IOSPasskeyModelFactory::GetForProfile(
           // Here, we want to observe the user's passkey model, so we need the
           // original profile.
diff --git a/ios/chrome/browser/device_sharing/model/device_sharing_browser_agent.mm b/ios/chrome/browser/device_sharing/model/device_sharing_browser_agent.mm
index 06d1cd6..cbbc0e1 100644
--- a/ios/chrome/browser/device_sharing/model/device_sharing_browser_agent.mm
+++ b/ios/chrome/browser/device_sharing/model/device_sharing_browser_agent.mm
@@ -11,7 +11,8 @@
 #import "ios/chrome/browser/shared/model/web_state_list/active_web_state_observation_forwarder.h"
 
 DeviceSharingBrowserAgent::DeviceSharingBrowserAgent(Browser* browser)
-    : browser_(browser),
+    : BrowserUserData(browser),
+      browser_(browser),
       is_incognito_(browser->GetProfile()->IsOffTheRecord()),
       active_web_state_observer_(
           std::make_unique<ActiveWebStateObservationForwarder>(
diff --git a/ios/chrome/browser/enterprise/connectors/BUILD.gn b/ios/chrome/browser/enterprise/connectors/BUILD.gn
index ef5ed64..bf49d8f 100644
--- a/ios/chrome/browser/enterprise/connectors/BUILD.gn
+++ b/ios/chrome/browser/enterprise/connectors/BUILD.gn
@@ -107,6 +107,7 @@
     "//components/enterprise/connectors/core:test_support",
     "//components/policy/core/common:test_support",
     "//components/prefs:test_support",
+    "//components/safe_browsing/core/common/proto:csd_proto",
     "//components/safe_browsing/core/common/proto:realtimeapi_proto",
     "//components/safe_browsing/ios/browser:allow_list",
     "//components/security_interstitials/core",
diff --git a/ios/chrome/browser/favicon/model/favicon_browser_agent.mm b/ios/chrome/browser/favicon/model/favicon_browser_agent.mm
index 8e66ad08f..9436e878 100644
--- a/ios/chrome/browser/favicon/model/favicon_browser_agent.mm
+++ b/ios/chrome/browser/favicon/model/favicon_browser_agent.mm
@@ -12,7 +12,8 @@
 #import "ios/web/public/web_state.h"
 #import "url/gurl.h"
 
-FaviconBrowserAgent::FaviconBrowserAgent(Browser* browser) : browser_(browser) {
+FaviconBrowserAgent::FaviconBrowserAgent(Browser* browser)
+    : BrowserUserData(browser), browser_(browser) {
   // All the BrowserAgent are attached to the Browser during the creation,
   // the WebStateList must be empty at this point.
   DCHECK(browser_->GetWebStateList()->empty())
diff --git a/ios/chrome/browser/follow/model/follow_browser_agent.mm b/ios/chrome/browser/follow/model/follow_browser_agent.mm
index a6abbdc..25f8e630 100644
--- a/ios/chrome/browser/follow/model/follow_browser_agent.mm
+++ b/ios/chrome/browser/follow/model/follow_browser_agent.mm
@@ -151,7 +151,8 @@
   return weak_ptr_factory_.GetWeakPtr();
 }
 
-FollowBrowserAgent::FollowBrowserAgent(Browser* browser) : browser_(browser) {}
+FollowBrowserAgent::FollowBrowserAgent(Browser* browser)
+    : BrowserUserData(browser), browser_(browser) {}
 
 void FollowBrowserAgent::ShowOverlayMessage(FollowSource source,
                                             NSString* message,
diff --git a/ios/chrome/browser/infobars/model/overlays/browser_agent/infobar_overlay_browser_agent.mm b/ios/chrome/browser/infobars/model/overlays/browser_agent/infobar_overlay_browser_agent.mm
index 5f433af5..b77b1e5 100644
--- a/ios/chrome/browser/infobars/model/overlays/browser_agent/infobar_overlay_browser_agent.mm
+++ b/ios/chrome/browser/infobars/model/overlays/browser_agent/infobar_overlay_browser_agent.mm
@@ -13,6 +13,7 @@
 
 InfobarOverlayBrowserAgent::InfobarOverlayBrowserAgent(Browser* browser)
     : OverlayBrowserAgentBase(browser),
+      BrowserUserData(browser),
       overlay_visibility_observer_(browser, this) {}
 
 InfobarOverlayBrowserAgent::~InfobarOverlayBrowserAgent() = default;
diff --git a/ios/chrome/browser/infobars/ui_bundled/presentation/infobar_banner_presentation_controller.mm b/ios/chrome/browser/infobars/ui_bundled/presentation/infobar_banner_presentation_controller.mm
index fc0211b..f8d70e53 100644
--- a/ios/chrome/browser/infobars/ui_bundled/presentation/infobar_banner_presentation_controller.mm
+++ b/ios/chrome/browser/infobars/ui_bundled/presentation/infobar_banner_presentation_controller.mm
@@ -46,7 +46,7 @@
 #pragma mark - Accessors
 
 - (CGRect)bannerFrame {
-  DCHECK(self.bannerPositioner);
+  CHECK(self.bannerPositioner);
   UIWindow* window = self.containerView.window;
   CGRect bannerFrame = CGRectZero;
 
@@ -92,6 +92,9 @@
 }
 
 - (void)presentationTransitionWillBegin {
+  if (!self.bannerPositioner) {
+    return;
+  }
   UIView* containerView = self.containerView;
   containerView.frame =
       [containerView.superview convertRect:self.bannerFrame
@@ -99,6 +102,10 @@
 }
 
 - (void)containerViewWillLayoutSubviews {
+  if (!self.bannerPositioner) {
+    [super containerViewWillLayoutSubviews];
+    return;
+  }
   CGRect bannerFrame = self.bannerFrame;
   UIView* containerView = self.containerView;
   UIWindow* window = containerView.window;
diff --git a/ios/chrome/browser/intelligence/glic/coordinator/BUILD.gn b/ios/chrome/browser/intelligence/glic/coordinator/BUILD.gn
index db153ad..d9501810 100644
--- a/ios/chrome/browser/intelligence/glic/coordinator/BUILD.gn
+++ b/ios/chrome/browser/intelligence/glic/coordinator/BUILD.gn
@@ -9,12 +9,18 @@
     "glic_consent_mediator.h",
     "glic_consent_mediator.mm",
     "glic_consent_mediator_delegate.h",
+    "glic_promo_scene_agent.h",
+    "glic_promo_scene_agent.mm",
   ]
   deps = [
     "//base",
     "//components/prefs:prefs",
+    "//ios/chrome/browser/intelligence/glic/metrics",
     "//ios/chrome/browser/intelligence/glic/ui",
+    "//ios/chrome/browser/promos_manager/model",
+    "//ios/chrome/browser/promos_manager/model:constants",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
+    "//ios/chrome/browser/shared/coordinator/scene:observing_scene_agent",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile",
   ]
diff --git a/ios/chrome/browser/intelligence/glic/coordinator/DEPS b/ios/chrome/browser/intelligence/glic/coordinator/DEPS
new file mode 100644
index 0000000..e91d7d6
--- /dev/null
+++ b/ios/chrome/browser/intelligence/glic/coordinator/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ios/chrome/browser/promos_manager/model",
+]
\ No newline at end of file
diff --git a/ios/chrome/browser/intelligence/glic/coordinator/glic_consent_coordinator.mm b/ios/chrome/browser/intelligence/glic/coordinator/glic_consent_coordinator.mm
index 4e53eb3d..b817215 100644
--- a/ios/chrome/browser/intelligence/glic/coordinator/glic_consent_coordinator.mm
+++ b/ios/chrome/browser/intelligence/glic/coordinator/glic_consent_coordinator.mm
@@ -5,8 +5,10 @@
 #import "ios/chrome/browser/intelligence/glic/coordinator/glic_consent_coordinator.h"
 
 #import "base/functional/bind.h"
+#import "base/metrics/histogram_functions.h"
 #import "ios/chrome/browser/intelligence/glic/coordinator/glic_consent_mediator.h"
 #import "ios/chrome/browser/intelligence/glic/coordinator/glic_consent_mediator_delegate.h"
+#import "ios/chrome/browser/intelligence/glic/metrics/glic_metrics.h"
 #import "ios/chrome/browser/intelligence/glic/ui/glic_consent_view_controller.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 
@@ -59,6 +61,8 @@
 // Handles the dismissal of the UI.
 - (void)presentationControllerDidDismiss:
     (UIPresentationController*)presentationController {
+  base::UmaHistogramEnumeration(kGLICConsentTypeHistogram,
+                                GLICConsentType::kDismiss);
   [self stop];
 }
 
diff --git a/ios/chrome/browser/intelligence/glic/coordinator/glic_consent_mediator.mm b/ios/chrome/browser/intelligence/glic/coordinator/glic_consent_mediator.mm
index 22f59d38..3dc08af 100644
--- a/ios/chrome/browser/intelligence/glic/coordinator/glic_consent_mediator.mm
+++ b/ios/chrome/browser/intelligence/glic/coordinator/glic_consent_mediator.mm
@@ -6,8 +6,10 @@
 
 #import <memory>
 
+#import "base/metrics/histogram_functions.h"
 #import "components/prefs/pref_service.h"
 #import "ios/chrome/browser/intelligence/glic/coordinator/glic_consent_mediator_delegate.h"
+#import "ios/chrome/browser/intelligence/glic/metrics/glic_metrics.h"
 #import "ios/chrome/browser/intelligence/glic/ui/glic_consent_view_controller.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
 
@@ -27,12 +29,16 @@
 
 // Did consent to GLIC.
 - (void)didConsentGLIC {
+  base::UmaHistogramEnumeration(kGLICConsentTypeHistogram,
+                                GLICConsentType::kAccept);
   _prefService->SetBoolean(prefs::kIOSGLICConsent, YES);
   [_delegate dismissGLICConsentUI];
 }
 
 // Did dismisses the Consent UI.
 - (void)didRefuseGLICConsent {
+  base::UmaHistogramEnumeration(kGLICConsentTypeHistogram,
+                                GLICConsentType::kCancel);
   [_delegate dismissGLICConsentUI];
 }
 
diff --git a/ios/chrome/browser/intelligence/glic/coordinator/glic_promo_scene_agent.h b/ios/chrome/browser/intelligence/glic/coordinator/glic_promo_scene_agent.h
new file mode 100644
index 0000000..d183ddc
--- /dev/null
+++ b/ios/chrome/browser/intelligence/glic/coordinator/glic_promo_scene_agent.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_INTELLIGENCE_GLIC_COORDINATOR_GLIC_PROMO_SCENE_AGENT_H_
+#define IOS_CHROME_BROWSER_INTELLIGENCE_GLIC_COORDINATOR_GLIC_PROMO_SCENE_AGENT_H_
+
+#import <Foundation/Foundation.h>
+
+#import "ios/chrome/browser/shared/coordinator/scene/observing_scene_state_agent.h"
+
+class PromosManager;
+
+// A scene agent that registers the GLIC promo in the promo manager on
+// SceneActivationLevelForegroundActive.
+@interface GLICPromoSceneAgent : ObservingSceneAgent
+
+- (instancetype)initWithPromosManager:(PromosManager*)promosManager;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_INTELLIGENCE_GLIC_COORDINATOR_GLIC_PROMO_SCENE_AGENT_H_
diff --git a/ios/chrome/browser/intelligence/glic/coordinator/glic_promo_scene_agent.mm b/ios/chrome/browser/intelligence/glic/coordinator/glic_promo_scene_agent.mm
new file mode 100644
index 0000000..1a11385f
--- /dev/null
+++ b/ios/chrome/browser/intelligence/glic/coordinator/glic_promo_scene_agent.mm
@@ -0,0 +1,50 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/intelligence/glic/coordinator/glic_promo_scene_agent.h"
+
+#import "base/memory/raw_ptr.h"
+#import "ios/chrome/browser/promos_manager/model/constants.h"
+#import "ios/chrome/browser/promos_manager/model/promos_manager.h"
+
+@implementation GLICPromoSceneAgent {
+  raw_ptr<PromosManager> _promosManager;
+}
+
+- (instancetype)initWithPromosManager:(PromosManager*)promosManager {
+  self = [super init];
+  if (self) {
+    _promosManager = promosManager;
+  }
+  return self;
+}
+
+#pragma mark - SceneStateObserver
+
+- (void)sceneState:(SceneState*)sceneState
+    transitionedToActivationLevel:(SceneActivationLevel)level {
+  switch (level) {
+    case SceneActivationLevelForegroundActive: {
+      [self registerPromoForSingleDisplay];
+      break;
+    }
+    case SceneActivationLevelUnattached:
+    case SceneActivationLevelBackground:
+    case SceneActivationLevelDisconnected:
+    case SceneActivationLevelForegroundInactive: {
+      break;
+    }
+  }
+}
+
+#pragma mark - Private
+
+// Register the What's New promo for a single display in the promo manager.
+- (void)registerPromoForSingleDisplay {
+  DCHECK(_promosManager);
+  _promosManager->RegisterPromoForSingleDisplay(
+      promos_manager::Promo::GLICPromo);
+}
+
+@end
diff --git a/ios/chrome/browser/intelligence/glic/metrics/BUILD.gn b/ios/chrome/browser/intelligence/glic/metrics/BUILD.gn
new file mode 100644
index 0000000..2d8afd5
--- /dev/null
+++ b/ios/chrome/browser/intelligence/glic/metrics/BUILD.gn
@@ -0,0 +1,10 @@
+# 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.
+
+source_set("metrics") {
+  sources = [
+    "glic_metrics.h",
+    "glic_metrics.mm",
+  ]
+}
diff --git a/ios/chrome/browser/intelligence/glic/metrics/glic_metrics.h b/ios/chrome/browser/intelligence/glic/metrics/glic_metrics.h
new file mode 100644
index 0000000..88df2d80
--- /dev/null
+++ b/ios/chrome/browser/intelligence/glic/metrics/glic_metrics.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_INTELLIGENCE_GLIC_METRICS_GLIC_METRICS_H_
+#define IOS_CHROME_BROWSER_INTELLIGENCE_GLIC_METRICS_GLIC_METRICS_H_
+
+extern const char kGLICConsentTypeHistogram[];
+
+// Enum for the IOS.GLIC.Outcome histogram.
+// Keep in sync with "GLICConsentType"
+// LINT.IfChange(GLICConsentType)
+enum class GLICConsentType {
+  kCancel = 0,
+  kDismiss = 1,
+  kAccept = 2,
+  kMaxValue = kAccept,
+};
+// LINT.ThenChange(//tools/metrics/histograms/enums.xml:GLICConsentType)
+
+#endif  // IOS_CHROME_BROWSER_INTELLIGENCE_GLIC_METRICS_GLIC_METRICS_H_
diff --git a/ios/chrome/browser/intelligence/glic/metrics/glic_metrics.mm b/ios/chrome/browser/intelligence/glic/metrics/glic_metrics.mm
new file mode 100644
index 0000000..e61b2b8
--- /dev/null
+++ b/ios/chrome/browser/intelligence/glic/metrics/glic_metrics.mm
@@ -0,0 +1,7 @@
+// 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/intelligence/glic/metrics/glic_metrics.h"
+
+const char kGLICConsentTypeHistogram[] = "IOS.GLIC.Outcome";
diff --git a/ios/chrome/browser/intelligence/glic/ui/BUILD.gn b/ios/chrome/browser/intelligence/glic/ui/BUILD.gn
index 2df0074e..c7075870 100644
--- a/ios/chrome/browser/intelligence/glic/ui/BUILD.gn
+++ b/ios/chrome/browser/intelligence/glic/ui/BUILD.gn
@@ -14,6 +14,7 @@
     ":constants",
     "//base",
     "//components/feature_engagement/public:feature_constants",
+    "//ios/chrome/browser/intelligence/glic/metrics",
     "//ios/chrome/browser/promos_manager/model:types",
     "//ios/chrome/browser/promos_manager/ui_bundled:promos",
     "//ios/chrome/browser/shared/ui/symbols",
diff --git a/ios/chrome/browser/intelligence/glic/ui/glic_consent_view_controller.mm b/ios/chrome/browser/intelligence/glic/ui/glic_consent_view_controller.mm
index 0e30183..1795cc5 100644
--- a/ios/chrome/browser/intelligence/glic/ui/glic_consent_view_controller.mm
+++ b/ios/chrome/browser/intelligence/glic/ui/glic_consent_view_controller.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/browser/intelligence/glic/ui/glic_consent_view_controller.h"
 
+#import "base/metrics/histogram_functions.h"
+#import "ios/chrome/browser/intelligence/glic/metrics/glic_metrics.h"
 #import "ios/chrome/browser/intelligence/glic/ui/glic_consent_mutator.h"
 #import "ios/chrome/browser/intelligence/glic/ui/glic_constants.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
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 ef7363d3..7b62b27 100644
--- a/ios/chrome/browser/intents/model/user_activity_browser_agent.mm
+++ b/ios/chrome/browser/intents/model/user_activity_browser_agent.mm
@@ -110,7 +110,9 @@
 }  // namespace
 
 UserActivityBrowserAgent::UserActivityBrowserAgent(Browser* browser)
-    : browser_(browser), profile_(browser->GetProfile()) {
+    : BrowserUserData(browser),
+      browser_(browser),
+      profile_(browser->GetProfile()) {
   SceneState* scene_state = browser_->GetSceneState();
   connection_information_ = scene_state.controller;
   tab_opener_ = scene_state.controller;
diff --git a/ios/chrome/browser/lens/model/lens_browser_agent.mm b/ios/chrome/browser/lens/model/lens_browser_agent.mm
index 8b38f9c..410bcff 100644
--- a/ios/chrome/browser/lens/model/lens_browser_agent.mm
+++ b/ios/chrome/browser/lens/model/lens_browser_agent.mm
@@ -18,7 +18,8 @@
 #import "ios/web/public/navigation/navigation_manager.h"
 #import "ios/web/public/web_state.h"
 
-LensBrowserAgent::LensBrowserAgent(Browser* browser) : browser_(browser) {
+LensBrowserAgent::LensBrowserAgent(Browser* browser)
+    : BrowserUserData(browser), browser_(browser) {
   browser->AddObserver(this);
 }
 
diff --git a/ios/chrome/browser/lens_overlay/coordinator/BUILD.gn b/ios/chrome/browser/lens_overlay/coordinator/BUILD.gn
index 8c7f17e..4ea0917 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/BUILD.gn
+++ b/ios/chrome/browser/lens_overlay/coordinator/BUILD.gn
@@ -28,6 +28,7 @@
     "lens_overlay_mediator.h",
     "lens_overlay_mediator.mm",
     "lens_overlay_mediator_delegate.h",
+    "lens_overlay_presentation_environment.h",
     "lens_overlay_tab_change_audience.h",
     "lens_result_page_mediator.h",
     "lens_result_page_mediator.mm",
@@ -69,6 +70,7 @@
     "//ios/chrome/browser/omnibox/ui:omnibox_internal",
     "//ios/chrome/browser/omnibox/ui/popup",
     "//ios/chrome/browser/orchestrator/ui_bundled",
+    "//ios/chrome/browser/overlays/model",
     "//ios/chrome/browser/prerender/model",
     "//ios/chrome/browser/search_engines/model",
     "//ios/chrome/browser/search_engines/model:search_engines_util",
diff --git a/ios/chrome/browser/lens_overlay/coordinator/DEPS b/ios/chrome/browser/lens_overlay/coordinator/DEPS
index 27bf6de2..0d949a1 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/DEPS
+++ b/ios/chrome/browser/lens_overlay/coordinator/DEPS
@@ -15,5 +15,7 @@
   "+ios/chrome/browser/context_menu/ui_bundled",
   "+ios/chrome/browser/web/model",
   "+ios/chrome/browser/omnibox/public",
-  "+ios/chrome/browser/omnibox/model/chrome_omnibox_client_ios.h"
+  "+ios/chrome/browser/omnibox/model/chrome_omnibox_client_ios.h",
+  "+ios/chrome/browser/overlays/model",
+  "+ios/chrome/browser/overlays/model/public",
 ]
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.h b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.h
index 1a662b6a..913b7bfd 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.h
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.h
@@ -5,6 +5,7 @@
 #ifndef IOS_CHROME_BROWSER_LENS_OVERLAY_COORDINATOR_LENS_OVERLAY_COORDINATOR_H_
 #define IOS_CHROME_BROWSER_LENS_OVERLAY_COORDINATOR_LENS_OVERLAY_COORDINATOR_H_
 
+#import "ios/chrome/browser/lens_overlay/coordinator/lens_overlay_presentation_environment.h"
 #import "ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.h"
 #import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h"
 
@@ -14,6 +15,12 @@
 // Lens overlay view controller.
 @property(nonatomic, readonly) UIViewController* viewController;
 
+// The context in which the overlay is presented.
+//
+// This property should not be changed while the overlay is being presented.
+@property(nonatomic, weak) id<LensOverlayPresentationEnvironment>
+    presentationEnvironment;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_LENS_OVERLAY_COORDINATOR_LENS_OVERLAY_COORDINATOR_H_
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
index 0345f7b..17608d0 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
@@ -50,6 +50,7 @@
 #import "ios/chrome/browser/omnibox/coordinator/omnibox_coordinator.h"
 #import "ios/chrome/browser/omnibox/model/chrome_omnibox_client_ios.h"
 #import "ios/chrome/browser/omnibox/ui/omnibox_focus_delegate.h"
+#import "ios/chrome/browser/overlays/model/public/overlay_presentation_context.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"
@@ -103,6 +104,7 @@
                                       LensOverlayMediatorDelegate,
                                       LensOverlayOverflowMenuDelegate,
                                       LensOverlayResultConsumer,
+                                      LensOverlayContainerPresenterDelegate,
                                       LensOverlayResultsPagePresenterDelegate,
                                       LensOverlayTabChangeAudience>
 
@@ -167,6 +169,9 @@
 
   // The entrypoint associated with the current session.
   LensOverlayEntrypoint _entrypoint;
+
+  // The view controller that serves as the base of the presentation.
+  __weak UIViewController* _presentationBaseViewController;
 }
 
 #pragma mark - public
@@ -300,6 +305,7 @@
 
 - (void)searchWithLensImageMetadata:(id<LensImageMetadata>)metadata
                          entrypoint:(LensOverlayEntrypoint)entrypoint
+            initialPresentationBase:(UIViewController*)initialPresentationBase
                          completion:(void (^)(BOOL))completion {
   BOOL success = [self prepareOverlayWithEntrypoint:entrypoint];
   if (!success) {
@@ -309,6 +315,7 @@
 
     return;
   }
+  _presentationBaseViewController = initialPresentationBase;
   // Even if the image is already prepared at this point, the snapshotting
   // infrastructure still needs to be built to allow the restoration window to
   // be displayed when exiting and re-entering the experience.
@@ -411,9 +418,15 @@
 
   [self showRestorationWindowIfNeeded];
 
+  UIViewController* containerBase =
+      _presentationBaseViewController ?: self.baseViewController;
+  // Once consumed, the presentation base can be reset.
+  _presentationBaseViewController = nil;
+
   _containerPresenter = [[LensOverlayContainerPresenter alloc]
-      initWithBaseViewController:self.baseViewController
+      initWithBaseViewController:containerBase
          containerViewController:_containerViewController];
+  _containerPresenter.delegate = self;
 
   __weak __typeof(self) weakSelf = self;
   [_containerPresenter
@@ -508,25 +521,29 @@
       HandlerForProtocol(self.browser->GetCommandDispatcher(), LensCommands);
   [weakCommands lensOverlayWillDismissWithCause:
                     LensOverlayDismissalCauseExternalNavigation];
-  __weak LensOverlayResultsPagePresenter* weakResultsPagePresenter =
-      _resultsPagePresenter;
+  __weak LensOverlayContainerPresenter* weakContainerPresenter =
+      _containerPresenter;
 
-  [_containerPresenter
-      dismissContainerAnimated:animated
-                    completion:^{
-                      [weakCommands
-                          lensOverlayDidDismissWithCause:
-                              LensOverlayDismissalCauseExternalNavigation];
-                      // If the result page is still present, dismiss it before
-                      // calling the completion.
-                      if (weakResultsPagePresenter) {
-                        [weakResultsPagePresenter
-                            dismissResultsPageAnimated:animated
-                                            completion:completion];
-                      } else if (completion) {
-                        completion();
-                      }
-                    }];
+  auto dismissLensOverlayContainer = ^{
+    [weakContainerPresenter
+        dismissContainerAnimated:animated
+                      completion:^{
+                        [weakCommands
+                            lensOverlayDidDismissWithCause:
+                                LensOverlayDismissalCauseExternalNavigation];
+                        if (completion) {
+                          completion();
+                        }
+                      }];
+  };
+
+  if (_resultsPagePresenter.isResultPageVisible) {
+    [_resultsPagePresenter
+        dismissResultsPageAnimated:animated
+                        completion:dismissLensOverlayContainer];
+  } else {
+    dismissLensOverlayContainer();
+  }
 }
 
 - (void)destroyLensUI:(BOOL)animated
@@ -690,6 +707,30 @@
                reason:lens::LensOverlayDismissalSource::kNetworkIssue];
 }
 
+#pragma mark - LensOverlayContainerPresenterDelegate
+
+- (void)lensOverlayContainerPresenterWillBeginPresentation:
+    (LensOverlayContainerPresenter*)containerPresenter {
+  [self setInfobarBannerOverlaysEnabled:NO];
+  [self.presentationEnvironment lensOverlayWillAppear];
+}
+
+- (void)lensOverlayContainerPresenterWillDismissPresentation:
+    (LensOverlayContainerPresenter*)containerPresenter {
+  [self setInfobarBannerOverlaysEnabled:YES];
+  [self.presentationEnvironment lensOverlayWillDisappear];
+}
+
+- (void)lensOverlayContainerPresenterDidReadjustPresentation:
+    (LensOverlayContainerPresenter*)containerPresenter {
+  [_resultsPagePresenter readjustPresentationIfNeeded];
+}
+
+- (NSDirectionalEdgeInsets)lensOverlayContainerPresenterInsetsForPresentation:
+    (LensOverlayContainerPresenter*)containerPresenter {
+  return self.presentationEnvironment.presentationInsetsForLensOverlay;
+}
+
 #pragma mark - LensOverlayResultsPagePresenterDelegate
 
 - (void)lensOverlayResultsPagePresenterWillInitiateGestureDrivenDismiss:
@@ -963,6 +1004,17 @@
 
 #pragma mark - private
 
+// Temporarily disables the infobar banners that might overlap the Lens UI
+// during it's presentation.
+- (void)setInfobarBannerOverlaysEnabled:(BOOL)enabled {
+  OverlayPresentationContext* infobarBannerContext =
+      OverlayPresentationContext::FromBrowser(self.browser,
+                                              OverlayModality::kInfobarBanner);
+  if (infobarBannerContext) {
+    infobarBannerContext->SetUIDisabled(!enabled);
+  }
+}
+
 // Prepares the lens overlay for display from the given entrypoint.
 - (BOOL)prepareOverlayWithEntrypoint:(LensOverlayEntrypoint)entrypoint {
   if (self.isUICreated) {
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator_unittest.mm b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator_unittest.mm
index ebd8259..197a8bc 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator_unittest.mm
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator_unittest.mm
@@ -52,6 +52,7 @@
 
 @interface LensOverlayCoordinator ()
 - (BOOL)isUICreated;
+- (BOOL)isLensOverlayVisible;
 @end
 
 namespace {
@@ -281,7 +282,7 @@
   [HandlerForProtocol(dispatcher_, LensOverlayCommands) showLensUI:NO];
 
   // Then nothing should be presented.
-  EXPECT_TRUE(base_view_controller_.presentedViewController == nil);
+  EXPECT_FALSE(coordinator_.isLensOverlayVisible);
 }
 
 // Showing the overlay should present the container view controller.
@@ -290,7 +291,7 @@
   [coordinator_ start];
 
   // Before showing anything nothing should appear presented.
-  EXPECT_TRUE(base_view_controller_.presentedViewController == nil);
+  EXPECT_FALSE(coordinator_.isLensOverlayVisible);
 
   // Dispatch the create & show command.
   [HandlerForProtocol(dispatcher_, LensOverlayCommands)
@@ -302,7 +303,7 @@
   // appear presented.
   EXPECT_TRUE(
       WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, true, ^bool {
-        return base_view_controller_.presentedViewController != nil;
+        return coordinator_.isLensOverlayVisible;
       }));
 }
 
@@ -321,7 +322,7 @@
   // appear presented.
   EXPECT_TRUE(
       WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, true, ^bool {
-        return base_view_controller_.presentedViewController != nil;
+        return coordinator_.isLensOverlayVisible;
       }));
 
   __block BOOL completion_called = NO;
@@ -333,7 +334,7 @@
 
   // The presented view controller is set to `nil` when the dismiss is over.
   EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
-    return base_view_controller_.presentedViewController == nil;
+    return !coordinator_.isLensOverlayVisible;
   }));
 
   // The completion is called.
@@ -361,7 +362,7 @@
 
   EXPECT_TRUE(
       WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, true, ^bool {
-        return base_view_controller_.presentedViewController != nil;
+        return coordinator_.isLensOverlayVisible;
       }));
 
   // Then the UI should appear created.
@@ -376,7 +377,7 @@
                                                               YES;
                                                         }];
   EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
-    return base_view_controller_.presentedViewController == nil;
+    return !coordinator_.isLensOverlayVisible;
   }));
 
   EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^bool {
@@ -408,7 +409,7 @@
   run_loop_.Run();
   EXPECT_TRUE(
       WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, true, ^bool {
-        return base_view_controller_.presentedViewController != nil;
+        return coordinator_.isLensOverlayVisible;
       }));
 
   // Then the UI should appear created and shown to the user.
@@ -443,15 +444,15 @@
 
   EXPECT_TRUE(
       WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, true, ^bool {
-        return base_view_controller_.presentedViewController != nil;
+        return coordinator_.isLensOverlayVisible;
       }));
 
   // After the overlay is displayed, wait once more for the constent dialog to
   // be presented.
-  UIViewController* containerVC = base_view_controller_.presentedViewController;
   EXPECT_TRUE(
       WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, true, ^bool {
-        return [containerVC.presentedViewController
+        return [base_view_controller_.childViewControllers.firstObject
+                    .presentedViewController
             isKindOfClass:[LensOverlayConsentViewController class]];
       }));
 }
@@ -476,7 +477,7 @@
 
   EXPECT_TRUE(
       WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, true, ^bool {
-        return base_view_controller_.presentedViewController != nil;
+        return coordinator_.isLensOverlayVisible;
       }));
 
   EXPECT_TRUE([coordinator_ isUICreated]);
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_presentation_environment.h b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_presentation_environment.h
new file mode 100644
index 0000000..791fc81
--- /dev/null
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_presentation_environment.h
@@ -0,0 +1,27 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_LENS_OVERLAY_COORDINATOR_LENS_OVERLAY_PRESENTATION_ENVIRONMENT_H_
+#define IOS_CHROME_BROWSER_LENS_OVERLAY_COORDINATOR_LENS_OVERLAY_PRESENTATION_ENVIRONMENT_H_
+
+#import <UIKit/UIKit.h>
+
+// This protocol should be implemented by the objects that manage the
+// presentation context of Lens Overlay.
+// Entities that conform to this protocol will be notified at various stages
+// of the Lens Overlay's presentation lifecycle.
+@protocol LensOverlayPresentationEnvironment <NSObject>
+
+// Notifies the embedder that Lens Overlay is about to appear.
+- (void)lensOverlayWillAppear;
+
+// Notifies the embedder that Lens Overlay is about to disappear.
+- (void)lensOverlayWillDisappear;
+
+// Returns the required insets for the Lens Overlay presentation.
+- (NSDirectionalEdgeInsets)presentationInsetsForLensOverlay;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_LENS_OVERLAY_COORDINATOR_LENS_OVERLAY_PRESENTATION_ENVIRONMENT_H_
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_view_finder_coordinator.mm b/ios/chrome/browser/lens_overlay/coordinator/lens_view_finder_coordinator.mm
index f12991a..7e3ad30 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/lens_view_finder_coordinator.mm
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_view_finder_coordinator.mm
@@ -160,6 +160,7 @@
   [_lensOverlayCommands
       searchWithLensImageMetadata:imageMetadata
                        entrypoint:entrypoint
+          initialPresentationBase:_lensViewController
                        completion:^(BOOL success) {
                          [weakLensViewController tearDownCaptureInfrastructure];
                        }];
diff --git a/ios/chrome/browser/lens_overlay/ui/BUILD.gn b/ios/chrome/browser/lens_overlay/ui/BUILD.gn
index bfe8f20dc..8db4fc44 100644
--- a/ios/chrome/browser/lens_overlay/ui/BUILD.gn
+++ b/ios/chrome/browser/lens_overlay/ui/BUILD.gn
@@ -63,6 +63,7 @@
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/public/features",
+    "//ios/chrome/common/ui/util",
     "//ui/base",
   ]
   frameworks = [ "UIKit.framework" ]
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_presenter.h b/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_presenter.h
index c23ef08..a856b98 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_presenter.h
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_presenter.h
@@ -7,12 +7,18 @@
 
 #import <UIKit/UIKit.h>
 
+#import "ios/chrome/browser/lens_overlay/model/lens_overlay_presentation_type.h"
+
 @class LensOverlayContainerViewController;
 @class SceneState;
+@protocol LensOverlayContainerPresenterDelegate;
 
 // Presenter for the Lens overlay container.
 @interface LensOverlayContainerPresenter : NSObject
 
+// Delegate for overlay container presentation events.
+@property(nonatomic, weak) id<LensOverlayContainerPresenterDelegate> delegate;
+
 // Whether the overlay is presented or not;
 @property(nonatomic, readonly) BOOL isLensOverlayVisible;
 
@@ -35,4 +41,26 @@
 
 @end
 
+// Presentation delegate for the Lens Overlay container.
+@protocol LensOverlayContainerPresenterDelegate
+
+// Notifies the delegate that the container presentation is about to start.
+- (void)lensOverlayContainerPresenterWillBeginPresentation:
+    (LensOverlayContainerPresenter*)containerPresenter;
+
+// Notifies the delegate that the container presentation is about to be
+// dismissed.
+- (void)lensOverlayContainerPresenterWillDismissPresentation:
+    (LensOverlayContainerPresenter*)containerPresenter;
+
+// Informs the delegate that the container presentation was readjusted.
+- (void)lensOverlayContainerPresenterDidReadjustPresentation:
+    (LensOverlayContainerPresenter*)containerPresenter;
+
+// Returns the required directional edge insets for the presentation.
+- (NSDirectionalEdgeInsets)lensOverlayContainerPresenterInsetsForPresentation:
+    (LensOverlayContainerPresenter*)containerPresenter;
+
+@end
+
 #endif  // IOS_CHROME_BROWSER_LENS_OVERLAY_UI_LENS_OVERLAY_CONTAINER_PRESENTER_H_
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_presenter.mm b/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_presenter.mm
index 19dcbde..7a31bbb 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_presenter.mm
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_presenter.mm
@@ -13,14 +13,19 @@
 #import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
+#import "ios/chrome/common/ui/util/constraints_ui_util.h"
 
 namespace {
 
-// The duration of the dismiss animation when exiting the selection UI.
-const CGFloat kSelectionViewDismissAnimationDuration = 0.2f;
+// The duration of the animation when exiting the selection UI.
+const CGFloat kSelectionViewAnimationDuration = 0.2f;
 
 }  // namespace
 
+@interface LensOverlayContainerPresenter () <LensOverlayContainerDelegate>
+
+@end
+
 @implementation LensOverlayContainerPresenter {
   // The controller on which to present the container.
   __weak UIViewController* _baseViewController;
@@ -30,10 +35,14 @@
 
   /// Forces the device orientation in portrait mode.
   std::unique_ptr<ScopedForcePortraitOrientation> _scopedForceOrientation;
+
+  // The top constraint for the controller.
+  NSLayoutConstraint* _topConstraint;
 }
 
 - (BOOL)isLensOverlayVisible {
-  return _containerViewController.presentingViewController != nil;
+  return _containerViewController.isViewLoaded &&
+         _containerViewController.view.window != nil;
 }
 
 - (instancetype)initWithBaseViewController:(UIViewController*)baseViewController
@@ -58,6 +67,7 @@
     return;
   }
 
+  _containerViewController.delegate = self;
   AppState* appState = sceneState.profileState.appState;
   ProfileIOS* profile = sceneState.profileState.profile;
   CHECK(profile, kLensOverlayNotFatalUntil);
@@ -66,45 +76,82 @@
     _scopedForceOrientation = ForcePortraitOrientationOnIphone(appState);
   }
 
-  _containerViewController.modalPresentationStyle =
-      UIModalPresentationOverCurrentContext;
-  _containerViewController.modalTransitionStyle =
-      UIModalTransitionStyleCrossDissolve;
+  [self.delegate lensOverlayContainerPresenterWillBeginPresentation:self];
 
-  UIViewController* presentingBase = _baseViewController;
+  [_baseViewController.view endEditing:YES];
+  [_baseViewController.view addSubview:_containerViewController.view];
+  [_baseViewController addChildViewController:_containerViewController];
+  _containerViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
+  NSDirectionalEdgeInsets insets =
+      [self.delegate lensOverlayContainerPresenterInsetsForPresentation:self];
 
-  if (_baseViewController.presentedViewController &&
-      !_baseViewController.presentedViewController.isBeingDismissed) {
-    presentingBase = _baseViewController.presentedViewController;
-  }
+  AddSameConstraintsToSides(
+      _containerViewController.view, _baseViewController.view,
+      LayoutSides::kLeading | LayoutSides::kBottom | LayoutSides::kTrailing);
+  _topConstraint = [_containerViewController.view.topAnchor
+      constraintEqualToAnchor:_baseViewController.view.topAnchor
+                     constant:insets.top];
+  [NSLayoutConstraint activateConstraints:@[ _topConstraint ]];
 
-  [presentingBase presentViewController:_containerViewController
-                               animated:animated
-                             completion:completion];
-}
+  [_containerViewController didMoveToParentViewController:_baseViewController];
+  _containerViewController.selectionViewController.view.alpha = 1;
 
-- (void)dismissContainerAnimated:(BOOL)animated
-                      completion:(void (^)())completion {
-  if (!_containerViewController.presentingViewController) {
+  if (!animated) {
     if (completion) {
-      _scopedForceOrientation.reset();
       completion();
     }
     return;
   }
 
+  _containerViewController.view.alpha = 0;
+  __weak UIViewController* weakContainer = _containerViewController;
+  [UIView animateWithDuration:kSelectionViewAnimationDuration
+      animations:^{
+        weakContainer.view.alpha = 1.0;
+      }
+      completion:^(BOOL finished) {
+        if (completion) {
+          completion();
+        }
+      }];
+}
+
+- (void)dismissContainerAnimated:(BOOL)animated
+                      completion:(void (^)())completion {
   _scopedForceOrientation.reset();
-  [_containerViewController.presentingViewController
-      dismissViewControllerAnimated:animated
-                         completion:completion];
+  _containerViewController.delegate = nil;
+  [self.delegate lensOverlayContainerPresenterWillDismissPresentation:self];
+  // If the container is not attached, directly call completion.
+  if (!_containerViewController.view.superview) {
+    if (completion) {
+      completion();
+    }
+    return;
+  }
+
+  __weak UIViewController* weakContainer = _containerViewController;
+  auto executeCleanup = ^{
+    [weakContainer.view removeFromSuperview];
+    [weakContainer removeFromParentViewController];
+    if (completion) {
+      completion();
+    }
+  };
+
+  if (!animated) {
+    executeCleanup();
+    return;
+  }
+
+  [self fadeSelectionUIWithCompletion:executeCleanup];
 }
 
 - (void)fadeSelectionUIWithCompletion:(void (^)())completion {
-  __weak UIViewController* weakContainer = _containerViewController;
-
-  [UIView animateWithDuration:kSelectionViewDismissAnimationDuration
+  __weak UIViewController* weakSelectionUI =
+      _containerViewController.selectionViewController;
+  [UIView animateWithDuration:kSelectionViewAnimationDuration
       animations:^{
-        weakContainer.view.alpha = 0;
+        weakSelectionUI.view.alpha = 0;
       }
       completion:^(BOOL success) {
         if (completion) {
@@ -113,4 +160,14 @@
       }];
 }
 
+#pragma mark - LensOverlayContainerDelegate
+
+- (void)lensOverlayContainerDidChangeSizeClass:
+    (LensOverlayContainerViewController*)lensOverlayContainerViewController {
+  NSDirectionalEdgeInsets insets =
+      [self.delegate lensOverlayContainerPresenterInsetsForPresentation:self];
+  _topConstraint.constant = insets.top;
+  [self.delegate lensOverlayContainerPresenterDidReadjustPresentation:self];
+}
+
 @end
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.h b/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.h
index ada36014..c0a6f48c 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.h
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.h
@@ -7,9 +7,11 @@
 
 #import <UIKit/UIKit.h>
 
+#import "base/ios/block_types.h"
 #import "ios/public/provider/chrome/browser/lens/lens_overlay_api.h"
 
 @protocol LensOverlayCommands;
+@protocol LensOverlayContainerDelegate;
 
 /// The top level view controller for lens overlay.
 /// Contains or presents the other view controllers.
@@ -24,6 +26,9 @@
 - (instancetype)initWithCoder:(NSCoder*)coder NS_UNAVAILABLE;
 - (instancetype)init NS_UNAVAILABLE;
 
+/// The delegate for Lens Overlay Container events.
+@property(nonatomic, weak) id<LensOverlayContainerDelegate> delegate;
+
 /// The selection view controller contained by this view controller.
 /// Currently should be set by `viewDidLoad` and only set once.
 @property(nonatomic, strong)
@@ -32,6 +37,28 @@
 /// Disables the interaction with the presented overlay.
 @property(nonatomic, assign) BOOL selectionInteractionDisabled;
 
+/// Whether the side panel is being presented.
+@property(nonatomic, readonly, getter=isSidePanelPresented)
+    BOOL sidePanelPresented;
+
+/// Presents the given view controller in a side panel, optionally animated.
+- (void)presentViewControllerInSidePanel:(UIViewController*)viewController
+                                animated:(BOOL)animated
+                              completion:(ProceduralBlock)completion;
+
+/// Dismisses the side panel presentation, optionally animated.
+- (void)dismissSidePanelAnimated:(BOOL)animated
+                      completion:(ProceduralBlock)completion;
+
+@end
+
+/// The delegate of the lens overlay container.
+@protocol LensOverlayContainerDelegate <NSObject>
+
+/// Called when the container changes the current horizontal size class
+- (void)lensOverlayContainerDidChangeSizeClass:
+    (LensOverlayContainerViewController*)lensOverlayContainerViewController;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_LENS_OVERLAY_UI_LENS_OVERLAY_CONTAINER_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.mm b/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.mm
index 2a337026..f2b385e 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.mm
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.mm
@@ -8,8 +8,122 @@
 #import "ios/chrome/browser/lens_overlay/ui/lens_overlay_accessibility_identifier_constants.h"
 #import "ios/chrome/browser/shared/public/commands/lens_overlay_commands.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
 
+namespace {
+
+// The width of the side panel.
+const CGFloat kSidePanelWidth = 400.0;
+
+// The ammount padding from the side panel to the selection UI.
+const CGFloat kSidePannelSelectionPadding = 20.0;
+
+// The duration of the side panel appear and dissapear animations.
+const CGFloat kSidePannelAnimationDuration = 0.4;
+
+// The width of the border outline that surrounds the results page.
+const CGFloat kSidePannelOutlineBorderWidth = 1.0;
+
+// The corner radius of the outline that surrounds the results page.
+const CGFloat kSidePannelOutlineCornerRadius = 10.0;
+
+// The lateral inset ammount of the border outlining the results page.
+const CGFloat kSidePannelOutlineLateralInset = 10.0;
+
+// The bottom inset ammount of the border outlining the results page.
+const CGFloat kSidePannelOutlineBottomInset = 8.0;
+
+// The corner radius of the selection UI when presented in the side panel
+// presentation.
+const CGFloat kSelectionUICornerRadius = 16.0;
+
+}  // namespace
+
+@interface LensOverlaySidePanel : UIViewController
+
+// Creates a new instance wrapping the given content view controller.
+- (instancetype)initWithContent:(UIViewController*)contentViewController;
+
+@end
+
+@implementation LensOverlaySidePanel {
+  // The content view being presented in the side panel.
+  // It is not intended for the ovelay to own the UI it presents.
+  __weak UIViewController* _contentViewController;
+
+  // The outline border of the results page.
+  UIView* _borderView;
+}
+
+- (instancetype)initWithContent:(UIViewController*)contentViewController {
+  self = [super init];
+  if (self) {
+    _contentViewController = contentViewController;
+  }
+
+  return self;
+}
+
+- (void)viewDidLoad {
+  [super viewDidLoad];
+  self.view.backgroundColor = [UIColor colorNamed:kBackgroundColor];
+  _borderView = [self createBorderView];
+  [self.view addSubview:_borderView];
+  AddSameConstraintsWithInsets(_borderView, self.view,
+                               [self insetsForInnerOutline]);
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+  [super viewWillAppear:animated];
+
+  [self addChildViewController:_contentViewController];
+  _contentViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
+  [_borderView addSubview:_contentViewController.view];
+  [_contentViewController didMoveToParentViewController:self];
+
+  AddSameConstraints(_contentViewController.view, _borderView);
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+  [_contentViewController removeFromParentViewController];
+  [_contentViewController.view removeFromSuperview];
+  [super viewWillDisappear:animated];
+}
+
+#pragma mark - Private
+
+// The ammount of insets for the outline relative to the bounds.
+- (NSDirectionalEdgeInsets)insetsForInnerOutline {
+  return NSDirectionalEdgeInsetsMake(0, kSidePannelOutlineLateralInset,
+                                     kSidePannelOutlineBottomInset,
+                                     kSidePannelOutlineLateralInset);
+}
+
+// Creates a new border outline view.
+- (UIView*)createBorderView {
+  UIView* borderView = [[UIView alloc] init];
+  borderView.translatesAutoresizingMaskIntoConstraints = NO;
+  borderView.layer.borderWidth = kSidePannelOutlineBorderWidth;
+  borderView.layer.borderColor = [UIColor colorNamed:kGrey200Color].CGColor;
+  borderView.layer.cornerRadius = kSidePannelOutlineCornerRadius;
+  borderView.clipsToBounds = YES;
+
+  return borderView;
+}
+
+@end
+
+@interface LensOverlayContainerViewController ()
+
+// Whether the side panel is open.
+@property(nonatomic, getter=isSidePanelOpen) BOOL sidePanelOpen;
+
+// The selection area occlusion insets for the side panel presentation.
+@property(nonatomic, readonly) UIEdgeInsets sidePanelOcclusionInsets;
+
+@end
+
 @implementation LensOverlayContainerViewController {
   // The overlay commands handler.
   id<LensOverlayCommands> _overlayCommandsHandler;
@@ -19,6 +133,12 @@
   // controller is displayed. Note that selection UI isn't started, so it won't
   // accept many interactions, but we do this to be extra safe.
   UIView* _selectionInteractionBlockingView;
+  // The side panel container for the results page.
+  LensOverlaySidePanel* _sidePanel;
+  // Layout guide separating the selection UI and results in the side panel.
+  UILayoutGuide* _splitViewLayoutGuide;
+  // The constraint controlling the display of the side panel.
+  NSLayoutConstraint* _splitViewConstraint;
 }
 
 - (instancetype)initWithLensOverlayCommandsHandler:
@@ -41,25 +161,53 @@
     return;
   }
 
-  [self addChildViewController:self.selectionViewController];
-  [self.view addSubview:self.selectionViewController.view];
-
-  self.selectionViewController.view.translatesAutoresizingMaskIntoConstraints =
-      NO;
+  _splitViewLayoutGuide = [[UILayoutGuide alloc] init];
+  [self.view addLayoutGuide:_splitViewLayoutGuide];
+  _splitViewConstraint = [self.view.rightAnchor
+      constraintEqualToAnchor:_splitViewLayoutGuide.leftAnchor];
   [NSLayoutConstraint activateConstraints:@[
-    [self.selectionViewController.view.topAnchor
+    _splitViewConstraint,
+    [_splitViewLayoutGuide.rightAnchor
+        constraintEqualToAnchor:_splitViewLayoutGuide.leftAnchor],
+    [_splitViewLayoutGuide.topAnchor
         constraintEqualToAnchor:self.view.topAnchor],
-    [self.selectionViewController.view.bottomAnchor
+    [_splitViewLayoutGuide.bottomAnchor
         constraintEqualToAnchor:self.view.bottomAnchor],
-    [self.selectionViewController.view.leftAnchor
-        constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leftAnchor],
-    [self.selectionViewController.view.rightAnchor
-        constraintEqualToAnchor:self.view.safeAreaLayoutGuide.rightAnchor],
   ]];
 
+  [self addChildViewController:self.selectionViewController];
+  [self.view addSubview:self.selectionViewController.view];
   [self.selectionViewController didMoveToParentViewController:self];
+  self.selectionViewController.view.translatesAutoresizingMaskIntoConstraints =
+      NO;
+  AddSameConstraintsToSides(
+      self.selectionViewController.view, self.view,
+      (LayoutSides::kLeading | LayoutSides::kTop | LayoutSides::kBottom));
+  [NSLayoutConstraint activateConstraints:@[
+    [self.selectionViewController.view.rightAnchor
+        constraintEqualToAnchor:_splitViewLayoutGuide.leftAnchor],
+  ]];
+
+  if (@available(iOS 17, *)) {
+    [self registerForTraitChanges:@[ UITraitHorizontalSizeClass.class ]
+                       withAction:@selector(sizeClassDidChange)];
+  }
 }
 
+#if !defined(__IPHONE_17_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_17_0
+- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
+  [super traitCollectionDidChange:previousTraitCollection];
+  if (@available(iOS 17, *)) {
+    return;
+  }
+
+  if (self.traitCollection.horizontalSizeClass !=
+      previousTraitCollection.horizontalSizeClass) {
+    [self sizeClassDidChange];
+  }
+}
+#endif
+
 - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
   return UIInterfaceOrientationMaskPortrait;
 }
@@ -68,6 +216,27 @@
   return _selectionInteractionBlockingView != nil;
 }
 
+- (BOOL)isSidePanelPresented {
+  return _sidePanel != nil;
+}
+
+- (BOOL)isSidePanelOpen {
+  return _splitViewConstraint.constant != 0;
+}
+
+- (void)setSidePanelOpen:(BOOL)sidePanelOpen {
+  if (sidePanelOpen) {
+    _splitViewConstraint.constant = kSidePanelWidth;
+  } else {
+    _splitViewConstraint.constant = 0;
+  }
+}
+
+- (UIEdgeInsets)sidePanelOcclusionInsets {
+  return UIEdgeInsetsMake(0, 0, 0,
+                          kSidePanelWidth + kSidePannelSelectionPadding);
+}
+
 - (void)setSelectionInteractionDisabled:(BOOL)selectionInteractionDisabled {
   if (!selectionInteractionDisabled) {
     [_selectionInteractionBlockingView removeFromSuperview];
@@ -88,6 +257,107 @@
   _selectionInteractionBlockingView = blocker;
 }
 
+- (void)presentViewControllerInSidePanel:(UIViewController*)viewController
+                                animated:(BOOL)animated
+                              completion:(ProceduralBlock)completion {
+  _sidePanel = [[LensOverlaySidePanel alloc] initWithContent:viewController];
+  _sidePanel.view.translatesAutoresizingMaskIntoConstraints = NO;
+  [self addChildViewController:_sidePanel];
+  [self.view addSubview:_sidePanel.view];
+
+  AddSameConstraintsToSides(_sidePanel.view, _splitViewLayoutGuide,
+                            (LayoutSides::kTop | LayoutSides::kBottom));
+  [NSLayoutConstraint activateConstraints:@[
+    [_sidePanel.view.leftAnchor
+        constraintEqualToAnchor:_splitViewLayoutGuide.rightAnchor],
+    [_sidePanel.view.widthAnchor constraintEqualToConstant:kSidePanelWidth],
+  ]];
+
+  self.selectionViewController.view.clipsToBounds = YES;
+  self.selectionViewController.view.layer.cornerRadius =
+      kSelectionUICornerRadius;
+  self.selectionViewController.view.layer.backgroundColor =
+      [UIColor colorNamed:kBackgroundColor].CGColor;
+  self.selectionViewController.view.layer.maskedCorners =
+      kCALayerMaxXMinYCorner | kCALayerMaxXMaxYCorner;
+
+  [self.selectionViewController setOcclusionInsets:self.sidePanelOcclusionInsets
+                                        reposition:YES
+                                          animated:animated];
+  if (!animated) {
+    self.sidePanelOpen = YES;
+    if (completion) {
+      completion();
+    }
+    return;
+  }
+
+  [self.view layoutIfNeeded];
+  [UIView animateWithDuration:kSidePannelAnimationDuration
+      delay:0
+      options:UIViewAnimationCurveEaseInOut
+      animations:^{
+        self.sidePanelOpen = YES;
+        [self.view layoutIfNeeded];
+      }
+      completion:^(BOOL) {
+        if (completion) {
+          completion();
+        }
+      }];
+}
+
+- (void)dismissSidePanelAnimated:(BOOL)animated
+                      completion:(ProceduralBlock)completion {
+  if (!self.isSidePanelPresented) {
+    completion();
+    return;
+  }
+
+  if (!animated) {
+    self.sidePanelOpen = NO;
+    self.selectionViewController.view.layer.cornerRadius = 0;
+    self.selectionViewController.view.layer.backgroundColor =
+        [UIColor clearColor].CGColor;
+    [self sidePanelDidDismissAnimated:animated];
+    if (completion) {
+      completion();
+    }
+    return;
+  }
+
+  [UIView animateWithDuration:kSidePannelAnimationDuration
+      delay:0
+      options:UIViewAnimationCurveEaseInOut
+      animations:^{
+        self.sidePanelOpen = NO;
+        self.selectionViewController.view.layer.cornerRadius = 0;
+        self.selectionViewController.view.layer.backgroundColor =
+            [UIColor clearColor].CGColor;
+        [self.view layoutIfNeeded];
+      }
+      completion:^(BOOL) {
+        [self sidePanelDidDismissAnimated:animated];
+        if (completion) {
+          completion();
+        }
+      }];
+}
+
+// Called after the side panel gets dismissed.
+- (void)sidePanelDidDismissAnimated:(BOOL)animated {
+  [self.selectionViewController setOcclusionInsets:UIEdgeInsetsZero
+                                        reposition:YES
+                                          animated:animated];
+  [_sidePanel removeFromParentViewController];
+  [_sidePanel.view removeFromSuperview];
+  _sidePanel = nil;
+}
+
+- (void)sizeClassDidChange {
+  [self.delegate lensOverlayContainerDidChangeSizeClass:self];
+}
+
 #pragma mark - Accessibility
 
 - (BOOL)accessibilityPerformEscape {
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.h b/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.h
index fabd8b7..f1e4928 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.h
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.h
@@ -12,6 +12,7 @@
 
 @protocol LensOverlayResultsPagePresenterDelegate;
 @class LensResultPageViewController;
+@class LensOverlayContainerViewController;
 @class SceneState;
 
 // Presenter for the Lens results bottom sheet.
@@ -31,7 +32,8 @@
 @property(nonatomic, readonly) CGFloat presentedResultsPageHeight;
 
 // Creates a new instance of the presenter.
-- (instancetype)initWithBaseViewController:(UIViewController*)baseViewController
+- (instancetype)initWithBaseViewController:
+                    (LensOverlayContainerViewController*)baseViewController
                   resultPageViewController:
                       (LensResultPageViewController*)resultViewController;
 
@@ -41,6 +43,9 @@
                   startInTranslate:(BOOL)startInTranslate
                         completion:(void (^)(void))completion;
 
+// Readjusts the presentation if there was a change in window dimensions.
+- (void)readjustPresentationIfNeeded;
+
 // Dismisses the presented page from the base view controller.
 - (void)dismissResultsPageAnimated:(BOOL)animated
                         completion:(void (^)(void))completion;
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.mm b/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.mm
index ac572d7..258a5cd 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.mm
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter.mm
@@ -9,8 +9,10 @@
 #import "base/task/sequenced_task_runner.h"
 #import "ios/chrome/browser/lens_overlay/model/lens_overlay_detents_manager.h"
 #import "ios/chrome/browser/lens_overlay/model/lens_overlay_pan_tracker.h"
+#import "ios/chrome/browser/lens_overlay/model/lens_overlay_presentation_type.h"
 #import "ios/chrome/browser/lens_overlay/ui/info_message/lens_translate_error_view_controller.h"
 #import "ios/chrome/browser/lens_overlay/ui/info_message/lens_translate_indication_view_controller.h"
+#import "ios/chrome/browser/lens_overlay/ui/lens_overlay_container_view_controller.h"
 #import "ios/chrome/browser/lens_overlay/ui/lens_overlay_results_page_presenter_delegate.h"
 #import "ios/chrome/browser/lens_overlay/ui/lens_result_page_view_controller.h"
 #import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
@@ -55,7 +57,7 @@
 
 @implementation LensOverlayResultsPagePresenter {
   /// The base on top of which the results view controller is presented.
-  __weak UIViewController* _baseViewController;
+  __weak LensOverlayContainerViewController* _baseViewController;
 
   /// The results view controller to present.
   __weak LensResultPageViewController* _resultViewController;
@@ -89,7 +91,8 @@
   UINavigationController* _presentationNavigationController;
 }
 
-- (instancetype)initWithBaseViewController:(UIViewController*)baseViewController
+- (instancetype)initWithBaseViewController:
+                    (LensOverlayContainerViewController*)baseViewController
                   resultPageViewController:
                       (LensResultPageViewController*)resultViewController {
   self = [super init];
@@ -109,6 +112,10 @@
 }
 
 - (BOOL)isResultPageVisible {
+  if (_baseViewController.sidePanelPresented) {
+    return YES;
+  }
+
   return _baseViewController.presentedViewController != nil &&
          _baseViewController.presentedViewController ==
              _presentationNavigationController;
@@ -170,35 +177,54 @@
     return;
   }
 
-  _resultViewController.delegate = self;
+  [self resultsPagePresentationWillAppear];
+
+  __weak __typeof(self) weakSelf = self;
+  auto presentationComplete = ^{
+    [weakSelf resultsPagePresentationDidAppear];
+    if (completion) {
+      completion();
+    }
+  };
+
+  BOOL presentInSidePanel =
+      lens::ResultPagePresentationFor(_baseViewController) ==
+      lens::ResultPagePresentationType::kSidePanel;
+  if (presentInSidePanel) {
+    [self presentSidePanelAnimated:animated completion:presentationComplete];
+  } else {
+    [self presentBottomSheetAnimated:animated
+                       maximizeSheet:maximizeSheet
+                    startInTranslate:startInTranslate
+                          completion:presentationComplete];
+  }
+}
+
+- (void)presentSidePanelAnimated:(BOOL)animated
+                      completion:(void (^)(void))completion {
+  [_baseViewController
+      presentViewControllerInSidePanel:_presentationNavigationController
+                              animated:animated
+                            completion:completion];
+}
+
+- (void)presentBottomSheetAnimated:(BOOL)animated
+                     maximizeSheet:(BOOL)maximizeSheet
+                  startInTranslate:(BOOL)startInTranslate
+                        completion:(void (^)(void))completion {
   UISheetPresentationController* sheet =
       _presentationNavigationController.sheetPresentationController;
   sheet.prefersEdgeAttachedInCompactHeight = YES;
   sheet.preferredCornerRadius = kPreferredCornerRadius;
 
-  _windowPanTracker =
-      [[LensOverlayPanTracker alloc] initWithView:self.presentationWindow];
-  _windowPanTracker.delegate = self;
-  [_windowPanTracker startTracking];
-
-  _basePanTracker =
-      [[LensOverlayPanTracker alloc] initWithView:_baseViewController.view];
-  [_basePanTracker startTracking];
+  [self setupGestureTrackers];
 
   SheetDetentPresentationStategy strategy =
       startInTranslate ? SheetDetentPresentationStategyTranslate
                        : SheetDetentPresentationStategySelection;
-  _detentsManager = [[LensOverlayDetentsManager alloc]
-       initWithBottomSheet:sheet
-                    window:self.presentationWindow
-      presentationStrategy:strategy];
-  _detentsManager.delegate = self;
-  [_detentsManager adjustDetentsForState:SheetDetentStateUnrestrictedMovement];
-
-  if (maximizeSheet) {
-    [_detentsManager requestMaximizeBottomSheet];
-  }
-
+  [self setupDetentsManagerForBottomSheet:sheet
+                                 strategy:strategy
+                            maximizeSheet:maximizeSheet];
   // Adjust the occlusion insets so that selections in the bottom half of the
   // screen are repositioned, to avoid being hidden by the bottom sheet.
   //
@@ -216,19 +242,21 @@
       [self panGestureRecognizersOnWindow];
 
   [self setUpVisibleAreaLayoutGuideIfNeeded];
-
-  _presentingAnimationInProgress = YES;
   [self monitorResultsBottomSheetPosition];
 
-  _presentationNavigationController.view.backgroundColor =
-      [UIColor colorNamed:kPrimaryBackgroundColor];
-
   __weak __typeof(self) weakSelf = self;
+
+  // If there is already a view controller presented (e.g. the overflow menu)
+  // dismiss it before presenting.
+  if (_baseViewController.presentedViewController) {
+    [_baseViewController dismissViewControllerAnimated:NO completion:nil];
+  }
+
   [_baseViewController
       presentViewController:_presentationNavigationController
                    animated:animated
                  completion:^{
-                   [weakSelf didFinishPresentingResultsPage];
+                   [weakSelf resultsPagePresentationDidAppear];
                    [weakSelf handlePanRecognizersAddedAfter:
                                  panRecognizersBeforePresenting];
                    if (completion) {
@@ -237,8 +265,34 @@
                  }];
 }
 
-- (void)didFinishPresentingResultsPage {
-  _presentingAnimationInProgress = NO;
+- (void)readjustPresentationIfNeeded {
+  if (!self.isResultPageVisible) {
+    return;
+  }
+
+  BOOL isAlreadySidePanel = _baseViewController.sidePanelPresented;
+  BOOL presentInSidePanel =
+      lens::ResultPagePresentationFor(_baseViewController) ==
+      lens::ResultPagePresentationType::kSidePanel;
+  // Refrain from rebuilding the presentation there was no change in the
+  // presentation type.
+  BOOL shouldRebuildPresentation = isAlreadySidePanel ^ presentInSidePanel;
+  if (!shouldRebuildPresentation) {
+    return;
+  }
+
+  __weak __typeof(self) weakSelf = self;
+  BOOL maximizeSheet =
+      _detentsManager.sheetDimension == SheetDimensionState::kLarge;
+  BOOL startInTranslate = _detentsManager.presentationStrategy ==
+                          SheetDetentPresentationStategyTranslate;
+  [self dismissResultsPageAnimated:NO
+                        completion:^{
+                          [weakSelf presentResultsPageAnimated:NO
+                                                 maximizeSheet:maximizeSheet
+                                              startInTranslate:startInTranslate
+                                                    completion:nil];
+                        }];
 }
 
 - (void)revealBottomSheetIfHidden {
@@ -279,23 +333,20 @@
 }
 
 - (void)hideBottomSheetWithCompletion:(void (^)(void))completion {
-  [_displayLink invalidate];
-  [self sheetPresentationHeightChanged:0];
-  [_windowPanTracker stopTracking];
-  [_basePanTracker stopTracking];
-  _detentsManager = nil;
-
+  [self resultsPagePresentationWillDismiss];
   UIViewController* presentedVC = _baseViewController.presentedViewController;
   [presentedVC dismissViewControllerAnimated:YES completion:completion];
 }
 
 - (void)dismissResultsPageAnimated:(BOOL)animated
                         completion:(void (^)(void))completion {
-  [_displayLink invalidate];
-  [_windowPanTracker stopTracking];
-  [_basePanTracker stopTracking];
-  _detentsManager = nil;
-  _resultViewController = nil;
+  [self resultsPagePresentationWillDismiss];
+
+  if (_baseViewController.sidePanelPresented) {
+    [_baseViewController dismissSidePanelAnimated:animated
+                                       completion:completion];
+    return;
+  }
 
   UIViewController* presentedVC = _baseViewController.presentedViewController;
   if (!presentedVC) {
@@ -306,7 +357,6 @@
   }
 
   [presentedVC dismissViewControllerAnimated:animated completion:completion];
-  _resultViewController = nil;
 }
 
 - (void)monitorResultsBottomSheetPosition {
@@ -398,6 +448,63 @@
                updateVerticalOcclusionOffset:offsetNeeded];
 }
 
+#pragma mark - Presentation lifecycle
+
+// Called before the results page is presented.
+- (void)resultsPagePresentationWillAppear {
+  _presentingAnimationInProgress = YES;
+  _resultViewController.delegate = self;
+  _presentationNavigationController.view.backgroundColor =
+      [UIColor colorNamed:kPrimaryBackgroundColor];
+  BOOL presentedInBottomSheet =
+      lens::ResultPagePresentationFor(_baseViewController) ==
+      lens::ResultPagePresentationType::kEdgeAttachedBottomSheet;
+  [_resultViewController setBottomSheetGrabberVisible:presentedInBottomSheet];
+}
+
+// Called after the results page has appeared.
+- (void)resultsPagePresentationDidAppear {
+  _presentingAnimationInProgress = NO;
+}
+
+// Called before the results page is dismissed.
+- (void)resultsPagePresentationWillDismiss {
+  [_displayLink invalidate];
+  [self sheetPresentationHeightChanged:0];
+  [_windowPanTracker stopTracking];
+  [_basePanTracker stopTracking];
+  _detentsManager = nil;
+}
+
+// Sets up the required gesture trackers for the bottom sheet presentation.
+- (void)setupGestureTrackers {
+  _windowPanTracker =
+      [[LensOverlayPanTracker alloc] initWithView:self.presentationWindow];
+  _windowPanTracker.delegate = self;
+  [_windowPanTracker startTracking];
+
+  _basePanTracker =
+      [[LensOverlayPanTracker alloc] initWithView:_baseViewController.view];
+  [_basePanTracker startTracking];
+}
+
+// Sets up the detents manager for the bottom sheet presentation.
+- (void)setupDetentsManagerForBottomSheet:(UISheetPresentationController*)sheet
+                                 strategy:
+                                     (SheetDetentPresentationStategy)strategy
+                            maximizeSheet:(BOOL)maximizeSheet {
+  _detentsManager = [[LensOverlayDetentsManager alloc]
+       initWithBottomSheet:sheet
+                    window:self.presentationWindow
+      presentationStrategy:strategy];
+  _detentsManager.delegate = self;
+  [_detentsManager adjustDetentsForState:SheetDetentStateUnrestrictedMovement];
+
+  if (maximizeSheet) {
+    [_detentsManager requestMaximizeBottomSheet];
+  }
+}
+
 #pragma mark - UIPanGestureRecognizer handlers
 
 - (NSSet<UIPanGestureRecognizer*>*)panGestureRecognizersOnWindow {
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_result_page_view_controller.h b/ios/chrome/browser/lens_overlay/ui/lens_result_page_view_controller.h
index 88d03a10..efa25774 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_result_page_view_controller.h
+++ b/ios/chrome/browser/lens_overlay/ui/lens_result_page_view_controller.h
@@ -31,6 +31,9 @@
 /// Mutator of the lens result page.
 @property(nonatomic, weak) id<LensResultPageMutator> mutator;
 
+/// Sets the bottom sheet grabber visible.
+- (void)setBottomSheetGrabberVisible:(BOOL)bottomSheetGrabberVisible;
+
 /// Sets the omnibox edit view.
 - (void)setEditView:(UIView<TextFieldViewContaining>*)editView;
 
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_result_page_view_controller.mm b/ios/chrome/browser/lens_overlay/ui/lens_result_page_view_controller.mm
index 3077bdb..1114d215 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_result_page_view_controller.mm
+++ b/ios/chrome/browser/lens_overlay/ui/lens_result_page_view_controller.mm
@@ -101,6 +101,10 @@
   /// When set, the omnibox tap target continues to "eat" the touches, but they
   /// are ignored, effectively preventing omnibox interaction.
   BOOL _ignoreOmniboxTaps;
+  /// The grabber indicator for the bottom sheet.
+  UIView* _bottomSheetGrabber;
+  /// Whether to show the bottom sheet grabber.
+  BOOL _bottomSheetGrabberVisible;
 }
 
 - (instancetype)init {
@@ -131,13 +135,15 @@
   [self.view addSubview:self.webViewContainer];
 
   // Bottom sheet grabber.
-  UIView* grabber = [self createSheetGrabber];
-  [self.view addSubview:grabber];
-  AddSameCenterXConstraint(grabber, self.view);
+  _bottomSheetGrabber = [self createSheetGrabber];
+  [self.view addSubview:_bottomSheetGrabber];
+  [self setBottomSheetGrabberVisible:_bottomSheetGrabberVisible];
+  AddSameCenterXConstraint(_bottomSheetGrabber, self.view);
   AddSameConstraintsToSidesWithInsets(
-      grabber, self.view, LayoutSides::kTop,
+      _bottomSheetGrabber, self.view, LayoutSides::kTop,
       NSDirectionalEdgeInsetsMake(kGrabberTopPadding, 0, 0, 0));
-  AddSizeConstraints(grabber, CGSizeMake(kGrabberWidth, kGrabberHeight));
+  AddSizeConstraints(_bottomSheetGrabber,
+                     CGSizeMake(kGrabberWidth, kGrabberHeight));
 
   // Omnibox popup container.
   _omniboxPopupContainer.translatesAutoresizingMaskIntoConstraints = NO;
@@ -320,6 +326,11 @@
   AddSameConstraints(_editView, _omniboxContainer);
 }
 
+- (void)setBottomSheetGrabberVisible:(BOOL)bottomSheetGrabberVisible {
+  _bottomSheetGrabberVisible = bottomSheetGrabberVisible;
+  _bottomSheetGrabber.hidden = !bottomSheetGrabberVisible;
+}
+
 - (void)setMutator:(id<LensResultPageMutator>)mutator {
   _mutator = mutator;
   [self updateMutatorDarkMode];
diff --git a/ios/chrome/browser/metrics/model/ios_family_link_user_metrics_provider_unittest.mm b/ios/chrome/browser/metrics/model/ios_family_link_user_metrics_provider_unittest.mm
index 0fdd2ebd..d29f35a 100644
--- a/ios/chrome/browser/metrics/model/ios_family_link_user_metrics_provider_unittest.mm
+++ b/ios/chrome/browser/metrics/model/ios_family_link_user_metrics_provider_unittest.mm
@@ -63,6 +63,12 @@
 
     if (is_subject_to_parental_controls) {
       supervised_user::EnableParentalControls(*profile->GetPrefs());
+      // Note: in prod environment, prefs::kSupervisedUserSafeSites is set true
+      // in the managed pref store automatically after enabling parental
+      // controls (see how SupervisedUserService activates
+      // SupervisedUserSettingsService, which in turn activates
+      // SupervisedUserPrefStore)
+      profile->GetPrefs()->SetBoolean(prefs::kSupervisedUserSafeSites, true);
     }
   }
 
diff --git a/ios/chrome/browser/metrics/model/tab_usage_recorder_browser_agent.mm b/ios/chrome/browser/metrics/model/tab_usage_recorder_browser_agent.mm
index 827adbf..a7d87fb 100644
--- a/ios/chrome/browser/metrics/model/tab_usage_recorder_browser_agent.mm
+++ b/ios/chrome/browser/metrics/model/tab_usage_recorder_browser_agent.mm
@@ -25,7 +25,8 @@
 #import "ui/base/page_transition_types.h"
 
 TabUsageRecorderBrowserAgent::TabUsageRecorderBrowserAgent(Browser* browser)
-    : restore_start_time_(base::TimeTicks::Now()),
+    : BrowserUserData(browser),
+      restore_start_time_(base::TimeTicks::Now()),
       web_state_list_(browser->GetWebStateList()),
       prerender_service_(
           PrerenderServiceFactory::GetForProfile(browser->GetProfile())) {
diff --git a/ios/chrome/browser/metrics/model/web_state_list_metrics_browser_agent.mm b/ios/chrome/browser/metrics/model/web_state_list_metrics_browser_agent.mm
index d778393..17d1b71 100644
--- a/ios/chrome/browser/metrics/model/web_state_list_metrics_browser_agent.mm
+++ b/ios/chrome/browser/metrics/model/web_state_list_metrics_browser_agent.mm
@@ -29,7 +29,8 @@
 WebStateListMetricsBrowserAgent::WebStateListMetricsBrowserAgent(
     Browser* browser,
     SessionMetrics* session_metrics)
-    : web_state_list_(browser->GetWebStateList()),
+    : BrowserUserData(browser),
+      web_state_list_(browser->GetWebStateList()),
       session_metrics_(session_metrics) {
   DCHECK(web_state_list_);
   DCHECK(session_metrics_);
diff --git a/ios/chrome/browser/ntp/ui_bundled/BUILD.gn b/ios/chrome/browser/ntp/ui_bundled/BUILD.gn
index a3b973e..ef40369 100644
--- a/ios/chrome/browser/ntp/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/ntp/ui_bundled/BUILD.gn
@@ -110,9 +110,10 @@
     "//components/supervised_user/core/common:features",
     "//ios/chrome/app/profile",
     "//ios/chrome/app/strings",
+    "//ios/chrome/browser/authentication/ui_bundled/account_menu",
+    "//ios/chrome/browser/authentication/ui_bundled/account_menu:constants",
     "//ios/chrome/browser/authentication/ui_bundled/enterprise:enterprise_utils",
     "//ios/chrome/browser/authentication/ui_bundled/signin:signin_headers",
-    "//ios/chrome/browser/authentication/ui_bundled/signin/account_menu:constants",
     "//ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync",
     "//ios/chrome/browser/bubble/ui_bundled",
     "//ios/chrome/browser/content_suggestions/ui_bundled:coordinator",
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm
index 07c78711..b62b5833 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm
@@ -29,9 +29,11 @@
 #import "ios/chrome/app/profile/profile_init_stage.h"
 #import "ios/chrome/app/profile/profile_state.h"
 #import "ios/chrome/app/profile/profile_state_observer.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator_delegate.h"
 #import "ios/chrome/browser/authentication/ui_bundled/continuation.h"
 #import "ios/chrome/browser/authentication/ui_bundled/enterprise/enterprise_utils.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.h"
 #import "ios/chrome/browser/bubble/ui_bundled/bubble_view_controller_presenter.h"
@@ -131,7 +133,8 @@
 #import "ios/web/public/web_state_observer_bridge.h"
 #import "ui/base/l10n/l10n_util_mac.h"
 
-@interface NewTabPageCoordinator () <AuthenticationServiceObserving,
+@interface NewTabPageCoordinator () <AccountMenuCoordinatorDelegate,
+                                     AuthenticationServiceObserving,
                                      ContentSuggestionsDelegate,
                                      DiscoverFeedObserverBridgeDelegate,
                                      DiscoverFeedPreviewDelegate,
@@ -267,7 +270,7 @@
   // Indicates whether the fakebox was tapped as part of an omnibox focus event.
   BOOL _fakeboxTapped;
   // The account menu coordinator.
-  SigninCoordinator* _accountMenuCoordinator;
+  AccountMenuCoordinator* _accountMenuCoordinator;
   // The sign in and history sync coordinator displayed on top of the NTP.
   SigninCoordinator* _signinCoordinator;
 }
@@ -1524,10 +1527,21 @@
   }
 }
 
+#pragma mark - AccountMenuCoordinatorDelegate
+
+// Update the state, to take into account that the account menu coordinator is
+// stopped.
+- (void)accountMenuCoordinatorWantsToBeStopped:
+    (AccountMenuCoordinator*)coordinator {
+  CHECK_EQ(_accountMenuCoordinator, coordinator, base::NotFatalUntil::M140);
+  [self stopAccountMenuCoordinator];
+}
+
 #pragma mark - Private
 
 - (void)stopAccountMenuCoordinator {
   [_accountMenuCoordinator stop];
+  _accountMenuCoordinator.delegate = nil;
   _accountMenuCoordinator = nil;
 }
 
@@ -1537,19 +1551,13 @@
 }
 
 - (void)showAccountMenu:(UIView*)identityDisc {
-  _accountMenuCoordinator = [SigninCoordinator
-      accountMenuCoordinatorWithBaseViewController:self.NTPViewController
-                                           browser:self.browser
-                                      contextStyle:SigninContextStyle::kDefault
-                                        anchorView:identityDisc
-                                       accessPoint:AccountMenuAccessPoint::
-                                                       kNewTabPage
-                                               URL:GURL()];
-  __typeof(self) weakSelf = self;
-  _accountMenuCoordinator.signinCompletion =
-      ^(SigninCoordinatorResult, id<SystemIdentity>) {
-        [weakSelf showAccountMenuDidFinish];
-      };
+  _accountMenuCoordinator = [[AccountMenuCoordinator alloc]
+      initWithBaseViewController:self.NTPViewController
+                         browser:self.browser
+                      anchorView:identityDisc
+                     accessPoint:AccountMenuAccessPoint::kNewTabPage
+                             URL:GURL()];
+  _accountMenuCoordinator.delegate = self;
   [_accountMenuCoordinator start];
 }
 
@@ -1568,7 +1576,7 @@
 // Update the state, to take into account that the signin coordinator
 // coordinator is stopped.
 - (void)showSigninCommandDidFinish {
-  CHECK(_signinCoordinator, base::NotFatalUntil::M135);
+  CHECK(_signinCoordinator, base::NotFatalUntil::M140);
   [self stopSigninCoordinator];
 }
 
@@ -1744,7 +1752,8 @@
     }
     // Check if feed is visible before reporting NTP visibility as the feed
     // needs to be visible in order to use for metrics.
-    // TODO(crbug.com/40871863) Move isFeedVisible check to the metrics recorder
+    // TODO(crbug.com/40871863) Move isFeedVisible check to the metrics
+    // recorder
     if ([self isFeedVisible]) {
       [self.feedMetricsRecorder recordNTPDidChangeVisibility:visible];
     }
diff --git a/ios/chrome/browser/omnibox/model/omnibox_position/omnibox_position_browser_agent.mm b/ios/chrome/browser/omnibox/model/omnibox_position/omnibox_position_browser_agent.mm
index b5a3a45..4a069896 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_position/omnibox_position_browser_agent.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_position/omnibox_position_browser_agent.mm
@@ -4,7 +4,8 @@
 
 #import "ios/chrome/browser/omnibox/model/omnibox_position/omnibox_position_browser_agent.h"
 
-OmniboxPositionBrowserAgent::OmniboxPositionBrowserAgent(Browser* browser) {}
+OmniboxPositionBrowserAgent::OmniboxPositionBrowserAgent(Browser* browser)
+    : BrowserUserData(browser) {}
 
 OmniboxPositionBrowserAgent::~OmniboxPositionBrowserAgent() = default;
 
diff --git a/ios/chrome/browser/overlays/model/overlay_browser_agent_base_unittest.mm b/ios/chrome/browser/overlays/model/overlay_browser_agent_base_unittest.mm
index 9c18da4..e5739601 100644
--- a/ios/chrome/browser/overlays/model/overlay_browser_agent_base_unittest.mm
+++ b/ios/chrome/browser/overlays/model/overlay_browser_agent_base_unittest.mm
@@ -47,7 +47,8 @@
  private:
   friend class BrowserUserData<FakeOverlayBrowserAgent>;
 
-  FakeOverlayBrowserAgent(Browser* browser) : OverlayBrowserAgentBase(browser) {
+  FakeOverlayBrowserAgent(Browser* browser)
+      : OverlayBrowserAgentBase(browser), BrowserUserData(browser) {
     // Add a fake callback installer for kModality that supports requests
     // configured with SupportedConfig and dispatched responses with
     // DispatchInfo.
diff --git a/ios/chrome/browser/passwords/model/BUILD.gn b/ios/chrome/browser/passwords/model/BUILD.gn
index 43d1300..6d595de 100644
--- a/ios/chrome/browser/passwords/model/BUILD.gn
+++ b/ios/chrome/browser/passwords/model/BUILD.gn
@@ -277,6 +277,7 @@
     "//components/autofill/ios/common",
     "//components/autofill/ios/form_util",
     "//components/autofill/ios/form_util:form_util_feature",
+    "//components/enterprise/connectors/core",
     "//components/infobars/core",
     "//components/password_manager/core/browser",
     "//components/password_manager/core/browser:test_support",
@@ -298,6 +299,7 @@
     "//ios/chrome/browser/autofill/model/bottom_sheet",
     "//ios/chrome/browser/autofill/ui_bundled:coordinator",
     "//ios/chrome/browser/autofill/ui_bundled/form_input_accessory",
+    "//ios/chrome/browser/enterprise/connectors/reporting",
     "//ios/chrome/browser/infobars/model",
     "//ios/chrome/browser/passwords/model:store_factory",
     "//ios/chrome/browser/passwords/model/test",
diff --git a/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client.mm b/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client.mm
index 7ac1bb76..46de566 100644
--- a/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client.mm
+++ b/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client.mm
@@ -231,7 +231,6 @@
   return false;
 }
 
-// TODO(crbug.com/409047852): Add unit test to confirm the event trigger.
 void IOSChromePasswordManagerClient::MaybeReportEnterpriseLoginEvent(
     const GURL& url,
     bool is_federated,
diff --git a/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client_unittest.mm b/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client_unittest.mm
index fe73545..5811ec7c 100644
--- a/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client_unittest.mm
+++ b/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client_unittest.mm
@@ -11,6 +11,9 @@
 #import "base/test/scoped_feature_list.h"
 #import "components/autofill/ios/browser/autofill_client_ios.h"
 #import "components/autofill/ios/browser/test_autofill_client_ios.h"
+#import "components/enterprise/connectors/core/features.h"
+#import "components/enterprise/connectors/core/reporting_event_router.h"
+#import "components/keyed_service/core/keyed_service.h"
 #import "components/password_manager/core/browser/mock_password_form_manager_for_ui.h"
 #import "components/password_manager/core/browser/password_form.h"
 #import "components/password_manager/core/browser/password_form_manager.h"
@@ -19,6 +22,9 @@
 #import "components/password_manager/core/common/password_manager_pref_names.h"
 #import "components/prefs/testing_pref_service.h"
 #import "ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.h"
+#import "ios/chrome/browser/enterprise/connectors/reporting/ios_realtime_reporting_client.h"
+#import "ios/chrome/browser/enterprise/connectors/reporting/ios_realtime_reporting_client_factory.h"
+#import "ios/chrome/browser/enterprise/connectors/reporting/ios_reporting_event_router_factory.h"
 #import "ios/chrome/browser/infobars/model/infobar_manager_impl.h"
 #import "ios/chrome/browser/passwords/model/features.h"
 #import "ios/chrome/browser/passwords/model/password_controller.h"
@@ -27,6 +33,7 @@
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
 #import "ios/chrome/browser/shared/public/commands/credential_provider_promo_commands.h"
 #import "ios/chrome/browser/web/model/chrome_web_client.h"
+#import "ios/web/public/browser_state.h"
 #import "ios/web/public/test/scoped_testing_web_client.h"
 #import "ios/web/public/test/web_state_test_util.h"
 #import "ios/web/public/test/web_task_environment.h"
@@ -41,9 +48,30 @@
 using password_manager::PasswordFormManagerForUI;
 using password_manager::PasswordManagerClient;
 using password_manager::prefs::kCredentialsEnableService;
+using ::testing::_;
 using testing::NiceMock;
 using testing::Return;
 
+class MockRouter : public enterprise_connectors::ReportingEventRouter {
+ public:
+  MockRouter(enterprise_connectors::IOSRealtimeReportingClient* client)
+      : ReportingEventRouter(client) {}
+  MOCK_METHOD(void,
+              OnLoginEvent,
+              (const GURL& url,
+               bool is_federated,
+               const url::SchemeHostPort& federated_origin,
+               const std::u16string& username),
+              (override));
+};
+
+std::unique_ptr<KeyedService> MakeMockRouter(web::BrowserState* browser_state) {
+  auto* profile = ProfileIOS::FromBrowserState(browser_state);
+  return std::make_unique<MockRouter>(
+      enterprise_connectors::IOSRealtimeReportingClientFactory::GetForProfile(
+          profile));
+}
+
 // TODO(crbug.com/41456340): this file is initiated because of needing test for
 // ios policy. More unit test of the client should be added.
 class IOSChromePasswordManagerClientTest : public PlatformTest {
@@ -52,7 +80,14 @@
       : web_client_(std::make_unique<ChromeWebClient>()),
         store_(new testing::NiceMock<
                password_manager::MockPasswordStoreInterface>()) {
-    profile_ = TestProfileIOS::Builder().Build();
+    TestProfileIOS::Builder builder;
+    builder.AddTestingFactory(
+        enterprise_connectors::IOSReportingEventRouterFactory::GetInstance(),
+        base::BindRepeating(&MakeMockRouter));
+    profile_ = std::move(builder).Build();
+    reporting_event_router_ = static_cast<MockRouter*>(
+        enterprise_connectors::IOSReportingEventRouterFactory::GetForProfile(
+            profile_.get()));
     browser_ = std::make_unique<TestBrowser>(profile_.get());
 
     web::WebState::CreateParams params(profile_.get());
@@ -86,6 +121,7 @@
   std::unique_ptr<TestProfileIOS> profile_;
   std::unique_ptr<Browser> browser_;
   std::unique_ptr<web::WebState> web_state_;
+  raw_ptr<MockRouter> reporting_event_router_;
 
   // PasswordController for testing.
   PasswordController* passwordController_;
@@ -195,3 +231,16 @@
   // the autofill client, so the expected teardown order is respected.
   web_state_.reset();
 }
+
+// Tests that MaybeReportEnterpriseLoginEvent invoked router->OnLoginEvent as
+// expected.
+TEST_F(IOSChromePasswordManagerClientTest, OnLogInInvoked) {
+  base::test::ScopedFeatureList scoped_feature_list{
+      enterprise_connectors::kEnterpriseRealtimeEventReportingOnIOS};
+
+  PasswordManagerClient* client = passwordController_.passwordManagerClient;
+  EXPECT_CALL(*reporting_event_router_, OnLoginEvent(_, _, _, _)).Times(1);
+  client->MaybeReportEnterpriseLoginEvent(GURL("https://www.example.com/"),
+                                          url::SchemeHostPort().IsValid(),
+                                          url::SchemeHostPort(), u"Fakeuser");
+}
diff --git a/ios/chrome/browser/policy/model/policy_watcher_browser_agent.mm b/ios/chrome/browser/policy/model/policy_watcher_browser_agent.mm
index 8d4e391..c779139 100644
--- a/ios/chrome/browser/policy/model/policy_watcher_browser_agent.mm
+++ b/ios/chrome/browser/policy/model/policy_watcher_browser_agent.mm
@@ -32,7 +32,7 @@
 #import "ios/web/public/thread/web_task_traits.h"
 
 PolicyWatcherBrowserAgent::PolicyWatcherBrowserAgent(Browser* browser)
-    : browser_(browser) {
+    : BrowserUserData(browser), browser_(browser) {
   DCHECK(!browser->GetProfile()->IsOffTheRecord());
   prefs_change_observer_.Init(GetApplicationContext()->GetLocalState());
   profile_prefs_change_observer_.Init(browser->GetProfile()->GetPrefs());
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 5fec5287..c4fba5b 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
@@ -59,6 +59,7 @@
 #import "ios/chrome/browser/popup_menu/ui_bundled/overflow_menu/overflow_menu_swift.h"
 #import "ios/chrome/browser/popup_menu/ui_bundled/popup_menu_constants.h"
 #import "ios/chrome/browser/reader_mode/model/features.h"
+#import "ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.h"
 #import "ios/chrome/browser/reading_list/model/offline_url_utils.h"
 #import "ios/chrome/browser/reading_list/ui_bundled/reading_list_utils.h"
 #import "ios/chrome/browser/search_engines/model/search_engine_observer_bridge.h"
@@ -699,7 +700,7 @@
   }
 
   if (IsReaderModeAvailable()) {
-    self.readerModeAction = [self openReaderModeAction];
+    self.readerModeAction = [self toggleReaderModeAction];
   }
 
   if (send_tab_to_self::
@@ -742,20 +743,24 @@
   ];
 }
 
-- (OverflowMenuAction*)openReaderModeAction {
-  // TODO(crbug.com/409935686): Dynamically update the Reader mode overflow menu
-  // string to "Exit Reader Mode" if Reader mode is already enabled.
+- (OverflowMenuAction*)toggleReaderModeAction {
+  ReaderModeTabHelper* tabHelper =
+      ReaderModeTabHelper::FromWebState(self.webState);
+  BOOL isReaderModeActive = tabHelper->IsActive();
+  int nameID = isReaderModeActive ? IDS_IOS_TOOLS_MENU_HIDE_READER_MODE
+                                  : IDS_IOS_TOOLS_MENU_READER_MODE;
   __weak __typeof(self) weakSelf = self;
   return [self
-      createOverflowMenuActionWithNameID:IDS_IOS_TOOLS_MENU_READER_MODE
+      createOverflowMenuActionWithNameID:nameID
                               actionType:overflow_menu::ActionType::ReaderMode
                               symbolName:kReaderModeSymbol
                             systemSymbol:YES
                         monochromeSymbol:NO
-                         accessibilityID:kToolsMenuOpenReaderMode
+                         accessibilityID:kToolsMenuReaderMode
                             hideItemText:nil
                                  handler:^{
-                                   [weakSelf toggleReaderMode];
+                                   [weakSelf setReaderModeVisibility:
+                                                 !isReaderModeActive];
                                  }];
 }
 
@@ -2129,7 +2134,7 @@
     case overflow_menu::ActionType::SetTabReminder:
       return [self newSetTabReminderAction];
     case overflow_menu::ActionType::ReaderMode:
-      return [self openReaderModeAction];
+      return [self toggleReaderModeAction];
   }
 }
 
@@ -2360,10 +2365,14 @@
       showSetTabReminderUI:SetTabReminderEntryPoint::kOverflowMenu];
 }
 
-// Opens or closes the Reader mode UI.
-- (void)toggleReaderMode {
+// Sets the Reader mode UI visibility.
+- (void)setReaderModeVisibility:(BOOL)visible {
   [self dismissMenu];
-  [self.readerModeHandler toggleReaderMode];
+  if (visible) {
+    [self.readerModeHandler showReaderMode];
+  } else {
+    [self.readerModeHandler hideReaderMode];
+  }
 }
 
 #pragma mark - Destinations Handlers
diff --git a/ios/chrome/browser/popup_menu/ui_bundled/popup_menu_constants.h b/ios/chrome/browser/popup_menu/ui_bundled/popup_menu_constants.h
index dc72d344..a564c44a 100644
--- a/ios/chrome/browser/popup_menu/ui_bundled/popup_menu_constants.h
+++ b/ios/chrome/browser/popup_menu/ui_bundled/popup_menu_constants.h
@@ -61,7 +61,7 @@
 // Open ai prototype item accessibility Identifier.
 extern NSString* const kToolsMenuOpenAIPrototype;
 // Open Reader mode item accessibility Identifier.
-extern NSString* const kToolsMenuOpenReaderMode;
+extern NSString* const kToolsMenuReaderMode;
 // EditBookmark item accessibility Identifier.
 extern NSString* const kToolsMenuEditBookmark;
 // SiteInformation item accessibility Identifier.
diff --git a/ios/chrome/browser/popup_menu/ui_bundled/popup_menu_constants.mm b/ios/chrome/browser/popup_menu/ui_bundled/popup_menu_constants.mm
index e856d78..82a801c 100644
--- a/ios/chrome/browser/popup_menu/ui_bundled/popup_menu_constants.mm
+++ b/ios/chrome/browser/popup_menu/ui_bundled/popup_menu_constants.mm
@@ -33,7 +33,7 @@
 NSString* const kToolsMenuAddToBookmarks = @"kToolsMenuAddToBookmarks";
 NSString* const kToolsMenuOpenLensOverlay = @"kToolsMenuOpenLensOverlay";
 NSString* const kToolsMenuOpenAIPrototype = @"kToolsMenuOpenAIPrototype";
-NSString* const kToolsMenuOpenReaderMode = @"kToolsMenuOpenReaderMode";
+NSString* const kToolsMenuReaderMode = @"kToolsMenuReaderMode";
 NSString* const kToolsMenuEditBookmark = @"kToolsMenuEditBookmark";
 NSString* const kToolsMenuSiteInformation = @"kToolsMenuSiteInformation";
 NSString* const kToolsMenuIncognitoSearch = @"kToolsMenuIncognitoSearch";
diff --git a/ios/chrome/browser/promos_manager/ui_bundled/BUILD.gn b/ios/chrome/browser/promos_manager/ui_bundled/BUILD.gn
index e2f7e9cb..cfcb41d 100644
--- a/ios/chrome/browser/promos_manager/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/promos_manager/ui_bundled/BUILD.gn
@@ -102,6 +102,7 @@
     "//ios/chrome/browser/docking_promo/ui",
     "//ios/chrome/browser/first_run/ui_bundled:features",
     "//ios/chrome/browser/first_run/ui_bundled/welcome_back/ui",
+    "//ios/chrome/browser/intelligence/features",
     "//ios/chrome/browser/intelligence/glic/ui",
     "//ios/chrome/browser/post_restore_signin/ui_bundled",
     "//ios/chrome/browser/promos_manager/model:constants",
diff --git a/ios/chrome/browser/promos_manager/ui_bundled/DEPS b/ios/chrome/browser/promos_manager/ui_bundled/DEPS
index c5dea75a..89afe0e 100644
--- a/ios/chrome/browser/promos_manager/ui_bundled/DEPS
+++ b/ios/chrome/browser/promos_manager/ui_bundled/DEPS
@@ -5,6 +5,8 @@
   "+ios/chrome/browser/default_promo/ui_bundled",
   "+ios/chrome/browser/docking_promo/ui/docking_promo_display_handler.h",
   "+ios/chrome/browser/feature_engagement/model/tracker_factory.h",
+  "+ios/chrome/browser/intelligence/features",
+  "+ios/chrome/browser/intelligence/glic/ui",
   "+ios/chrome/browser/post_restore_signin/ui_bundled",
   "+ios/chrome/browser/signin/model",
   "+ios/chrome/browser/sync/model/sync_service_factory.h",
diff --git a/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm b/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm
index 8e8b823..78c3af9 100644
--- a/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm
+++ b/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm
@@ -37,6 +37,8 @@
 #import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
 #import "ios/chrome/browser/first_run/ui_bundled/features.h"
 #import "ios/chrome/browser/first_run/ui_bundled/welcome_back/ui/welcome_back_display_handler.h"
+#import "ios/chrome/browser/intelligence/features/features.h"
+#import "ios/chrome/browser/intelligence/glic/ui/glic_promo_display_handler.h"
 #import "ios/chrome/browser/post_restore_signin/ui_bundled/post_restore_signin_provider.h"
 #import "ios/chrome/browser/promos_manager/model/features.h"
 #import "ios/chrome/browser/promos_manager/model/promo_config.h"
@@ -618,6 +620,12 @@
     _displayHandlerPromos[promos_manager::Promo::WelcomeBack] =
         [[WelcomeBackDisplayHandler alloc] init];
   }
+
+  // GLIC promo handler.
+  if (IsPageActionMenuEnabled()) {
+    _displayHandlerPromos[promos_manager::Promo::GLICPromo] =
+        [[GLICPromoDisplayHandler alloc] init];
+  }
 }
 
 - (void)registerStandardPromoViewProviderPromos {
diff --git a/ios/chrome/browser/reader_mode/model/BUILD.gn b/ios/chrome/browser/reader_mode/model/BUILD.gn
index ad8b2669..84838d5 100644
--- a/ios/chrome/browser/reader_mode/model/BUILD.gn
+++ b/ios/chrome/browser/reader_mode/model/BUILD.gn
@@ -6,6 +6,9 @@
 
 source_set("model") {
   sources = [
+    "reader_mode_content_delegate.h",
+    "reader_mode_content_tab_helper.h",
+    "reader_mode_content_tab_helper.mm",
     "reader_mode_distiller_page.h",
     "reader_mode_distiller_page.mm",
     "reader_mode_java_script_feature.h",
@@ -28,6 +31,7 @@
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features:system_flags",
     "//ios/third_party/material_components_ios",
+    "//ios/web/navigation:wk_navigation_util",
     "//ios/web/public",
     "//ios/web/public/js_messaging",
     "//services/metrics/public/cpp:ukm_builders",
@@ -67,6 +71,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "reader_mode_content_tab_helper_unittest.mm",
     "reader_mode_distiller_page_unittest.mm",
     "reader_mode_java_script_feature_unittest.mm",
     "reader_mode_tab_helper_unittest.mm",
diff --git a/ios/chrome/browser/reader_mode/model/DEPS b/ios/chrome/browser/reader_mode/model/DEPS
index c1c7528..8dcf5f38 100644
--- a/ios/chrome/browser/reader_mode/model/DEPS
+++ b/ios/chrome/browser/reader_mode/model/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+ios/chrome/browser/dom_distiller/model",
   "+third_party/dom_distiller_js",
+  "+ios/web/navigation/wk_navigation_util.h"
 ]
 specific_include_rules = {
   "^reader_mode_tab_helper_unittest.mm" : [
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_content_delegate.h b/ios/chrome/browser/reader_mode/model/reader_mode_content_delegate.h
new file mode 100644
index 0000000..2e371f7
--- /dev/null
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_content_delegate.h
@@ -0,0 +1,27 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_READER_MODE_MODEL_READER_MODE_CONTENT_DELEGATE_H_
+#define IOS_CHROME_BROWSER_READER_MODE_MODEL_READER_MODE_CONTENT_DELEGATE_H_
+
+#import <Foundation/Foundation.h>
+
+#import "ios/web/public/navigation/web_state_policy_decider.h"
+
+class ReaderModeContentTabHelper;
+
+// Delegate for the content presented in Reader mode.
+class ReaderModeContentDelegate {
+ public:
+  virtual ~ReaderModeContentDelegate() = default;
+
+  // Called when the content just denied a request to navigate away from the
+  // current page.
+  virtual void ReaderModeContentDidCancelRequest(
+      ReaderModeContentTabHelper* reader_mode_content_tab_helper,
+      NSURLRequest* request,
+      web::WebStatePolicyDecider::RequestInfo request_info) = 0;
+};
+
+#endif  // IOS_CHROME_BROWSER_READER_MODE_MODEL_READER_MODE_CONTENT_DELEGATE_H_
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_content_tab_helper.h b/ios/chrome/browser/reader_mode/model/reader_mode_content_tab_helper.h
new file mode 100644
index 0000000..a11f86b1
--- /dev/null
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_content_tab_helper.h
@@ -0,0 +1,45 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_READER_MODE_MODEL_READER_MODE_CONTENT_TAB_HELPER_H_
+#define IOS_CHROME_BROWSER_READER_MODE_MODEL_READER_MODE_CONTENT_TAB_HELPER_H_
+
+#import "base/scoped_observation.h"
+#import "ios/web/public/navigation/web_state_policy_decider.h"
+#import "ios/web/public/web_state.h"
+#import "ios/web/public/web_state_observer.h"
+#import "ios/web/public/web_state_user_data.h"
+
+class ReaderModeContentDelegate;
+
+// Tab helper for the WebState rendering Reader mode content. Blocks navigations
+// and forwards them to its delegate instead.
+class ReaderModeContentTabHelper
+    : public web::WebStateUserData<ReaderModeContentTabHelper>,
+      public web::WebStatePolicyDecider {
+ public:
+  explicit ReaderModeContentTabHelper(web::WebState* web_state);
+  ~ReaderModeContentTabHelper() override;
+
+  // Sets the delegate. `delegate` can be `nullptr`.
+  void SetDelegate(ReaderModeContentDelegate* delegate);
+  // Loads the `content_data` in the WebState using `content_url` as URL. Does
+  // nothing if the WebState was destroyed or is being destroyed.
+  void LoadContent(GURL content_url, NSData* content_data);
+
+  // WebStatePolicyDecider overrides:
+  void ShouldAllowRequest(NSURLRequest* request,
+                          RequestInfo request_info,
+                          PolicyDecisionCallback callback) override;
+
+ private:
+  // URL of original document.
+  GURL content_url_;
+  // Whether request to navigate to content URL was allowed.
+  bool content_url_request_allowed_ = false;
+  // Delegate.
+  raw_ptr<ReaderModeContentDelegate> delegate_ = nullptr;
+};
+
+#endif  // IOS_CHROME_BROWSER_READER_MODE_MODEL_READER_MODE_CONTENT_TAB_HELPER_H_
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_content_tab_helper.mm b/ios/chrome/browser/reader_mode/model/reader_mode_content_tab_helper.mm
new file mode 100644
index 0000000..d9a7248f
--- /dev/null
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_content_tab_helper.mm
@@ -0,0 +1,62 @@
+// 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_content_tab_helper.h"
+
+#import "ios/chrome/browser/reader_mode/model/reader_mode_content_delegate.h"
+#import "ios/web/public/navigation/navigation_context.h"
+#import "ios/web/public/navigation/navigation_item.h"
+#import "ios/web/public/navigation/navigation_manager.h"
+#import "net/base/apple/url_conversions.h"
+
+ReaderModeContentTabHelper::ReaderModeContentTabHelper(web::WebState* web_state)
+    : web::WebStatePolicyDecider(web_state) {}
+
+ReaderModeContentTabHelper::~ReaderModeContentTabHelper() = default;
+
+void ReaderModeContentTabHelper::SetDelegate(
+    ReaderModeContentDelegate* delegate) {
+  delegate_ = delegate;
+}
+
+void ReaderModeContentTabHelper::LoadContent(GURL content_url,
+                                             NSData* content_data) {
+  if (!web_state() || web_state()->IsBeingDestroyed()) {
+    return;
+  }
+  content_url_ = content_url;
+  content_url_request_allowed_ = false;
+  web::NavigationManager* const navigation_manager =
+      web_state()->GetNavigationManager();
+  if (!navigation_manager->GetLastCommittedItem()) {
+    // `LoadData` requires an already committed navigation item.
+    std::vector<std::unique_ptr<web::NavigationItem>> navigation_items;
+    navigation_items.push_back(web::NavigationItem::Create());
+    navigation_manager->Restore(0, std::move(navigation_items));
+  }
+  web_state()->LoadData(content_data, @"text/html", std::move(content_url));
+}
+
+#pragma mark - WebStatePolicyDecider
+
+void ReaderModeContentTabHelper::ShouldAllowRequest(
+    NSURLRequest* request,
+    RequestInfo request_info,
+    PolicyDecisionCallback callback) {
+  const GURL request_url = net::GURLWithNSURL(request.URL);
+  if (request_url.EqualsIgnoringRef(content_url_) &&
+      !content_url_request_allowed_) {
+    // If the requested URL is the content URL and the request was not
+    // previously allowed, allow the request.
+    content_url_request_allowed_ = true;
+    std::move(callback).Run(PolicyDecision::Allow());
+    return;
+  }
+  // If the requested URL is NOT the content URL or it has been allowed already,
+  // cancel the request
+  std::move(callback).Run(PolicyDecision::Cancel());
+  if (delegate_) {
+    delegate_->ReaderModeContentDidCancelRequest(this, request, request_info);
+  }
+}
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_content_tab_helper_unittest.mm b/ios/chrome/browser/reader_mode/model/reader_mode_content_tab_helper_unittest.mm
new file mode 100644
index 0000000..7d74378d
--- /dev/null
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_content_tab_helper_unittest.mm
@@ -0,0 +1,143 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/reader_mode/model/reader_mode_content_tab_helper.h"
+
+#import "ios/chrome/browser/reader_mode/model/reader_mode_content_delegate.h"
+#import "ios/web/public/test/fakes/fake_navigation_manager.h"
+#import "ios/web/public/test/fakes/fake_web_state.h"
+#import "net/base/apple/url_conversions.h"
+#import "testing/gtest_mac.h"
+#import "testing/platform_test.h"
+
+namespace {
+
+// A delegate that records calls to ReaderModeContentDidCancelRequest.
+class TestReaderModeContentDelegate : public ReaderModeContentDelegate {
+ public:
+  ~TestReaderModeContentDelegate() override = default;
+
+  // ReaderModeContentDelegate implementation:
+  void ReaderModeContentDidCancelRequest(
+      ReaderModeContentTabHelper* reader_mode_content_tab_helper,
+      NSURLRequest* request,
+      web::WebStatePolicyDecider::RequestInfo request_info) override {
+    did_cancel_request_called_ = true;
+    last_canceled_request_ = request;
+  }
+
+  bool did_cancel_request_called() const { return did_cancel_request_called_; }
+  NSURLRequest* last_canceled_request() const { return last_canceled_request_; }
+
+  void Reset() {
+    did_cancel_request_called_ = false;
+    last_canceled_request_ = nil;
+  }
+
+ private:
+  bool did_cancel_request_called_ = false;
+  NSURLRequest* last_canceled_request_ = nil;
+};
+
+class FakeNavigationManagerWithRestore : public web::FakeNavigationManager {
+ public:
+  void Restore(
+      int last_committed_item_index,
+      std::vector<std::unique_ptr<web::NavigationItem>> items) override {}
+};
+
+}  // namespace
+
+class ReaderModeContentTabHelperTest : public PlatformTest {
+ public:
+  ReaderModeContentTabHelperTest() = default;
+  ~ReaderModeContentTabHelperTest() override = default;
+
+  void SetUp() override {
+    PlatformTest::SetUp();
+    delegate_ = std::make_unique<TestReaderModeContentDelegate>();
+    web_state_ = std::make_unique<web::FakeWebState>();
+    web_state_->SetNavigationManager(
+        std::make_unique<FakeNavigationManagerWithRestore>());
+    ReaderModeContentTabHelper::CreateForWebState(web_state_.get());
+    tab_helper_ = ReaderModeContentTabHelper::FromWebState(web_state_.get());
+    tab_helper_->SetDelegate(delegate_.get());
+  }
+
+  ReaderModeContentTabHelper* tab_helper() { return tab_helper_; }
+
+  // Invokes `ShouldAllowRequest(request, request_info)` on `tab_helper()` and
+  // returns its policy decision for that `request`.
+  std::optional<web::WebStatePolicyDecider::PolicyDecision>
+  GetContentRequestPolicyDecision(
+      NSURLRequest* request,
+      web::WebStatePolicyDecider::RequestInfo request_info) {
+    __block std::optional<web::WebStatePolicyDecider::PolicyDecision>
+        policy_decision;
+    tab_helper()->ShouldAllowRequest(
+        request, request_info,
+        base::BindOnce(^(web::WebStatePolicyDecider::PolicyDecision decision) {
+          policy_decision = decision;
+        }));
+    return policy_decision;
+  }
+
+ protected:
+  std::unique_ptr<TestReaderModeContentDelegate> delegate_;
+  std::unique_ptr<web::FakeWebState> web_state_;
+  raw_ptr<ReaderModeContentTabHelper> tab_helper_;
+};
+
+// Tests that the tab helper only allows requests to the content URL provided to
+// `LoadContent` and only once.
+TEST_F(ReaderModeContentTabHelperTest, AllowsContentURLRequestOnce) {
+  NSURL* content_url = [NSURL URLWithString:@"https://test1.url/"];
+  NSURLRequest* content_request = [NSURLRequest requestWithURL:content_url];
+  NSURL* non_content_url = [NSURL URLWithString:@"https://test2.url/"];
+  NSURLRequest* non_content_request =
+      [NSURLRequest requestWithURL:non_content_url];
+  std::string content = "<div>Hello world</div>";
+  NSData* data = [NSData dataWithBytes:content.data() length:content.length()];
+  web::WebStatePolicyDecider::RequestInfo request_info(
+      ui::PAGE_TRANSITION_FIRST, false, false, false, false, false);
+  std::optional<web::WebStatePolicyDecider::PolicyDecision> policy_decision;
+
+  // Any requests performed before content is loaded should be canceled.
+  policy_decision =
+      GetContentRequestPolicyDecision(content_request, request_info);
+  EXPECT_TRUE(policy_decision && policy_decision->ShouldCancelNavigation());
+  EXPECT_TRUE(delegate_->did_cancel_request_called());
+  EXPECT_NSEQ(content_request, delegate_->last_canceled_request());
+  delegate_->Reset();
+  policy_decision =
+      GetContentRequestPolicyDecision(non_content_request, request_info);
+  EXPECT_TRUE(policy_decision && policy_decision->ShouldCancelNavigation());
+  EXPECT_TRUE(delegate_->did_cancel_request_called());
+  EXPECT_NSEQ(non_content_request, delegate_->last_canceled_request());
+  delegate_->Reset();
+
+  // Load the content.
+  tab_helper()->LoadContent(net::GURLWithNSURL(content_url), data);
+
+  // Request for content URL is allowed only once after `LoadContent` is called.
+  policy_decision =
+      GetContentRequestPolicyDecision(content_request, request_info);
+  EXPECT_TRUE(policy_decision && policy_decision->ShouldAllowNavigation());
+  EXPECT_FALSE(delegate_->did_cancel_request_called());
+  EXPECT_NSEQ(nil, delegate_->last_canceled_request());
+  delegate_->Reset();
+  policy_decision =
+      GetContentRequestPolicyDecision(content_request, request_info);
+  EXPECT_TRUE(policy_decision && policy_decision->ShouldCancelNavigation());
+  EXPECT_TRUE(delegate_->did_cancel_request_called());
+  EXPECT_NSEQ(content_request, delegate_->last_canceled_request());
+  delegate_->Reset();
+
+  // Requests for non-content URL should always be canceled.
+  policy_decision =
+      GetContentRequestPolicyDecision(non_content_request, request_info);
+  EXPECT_TRUE(policy_decision && policy_decision->ShouldCancelNavigation());
+  EXPECT_TRUE(delegate_->did_cancel_request_called());
+  EXPECT_NSEQ(non_content_request, delegate_->last_canceled_request());
+}
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 b729d0b..d49a9c61 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
@@ -10,6 +10,7 @@
 #import "ios/chrome/browser/dom_distiller/model/distiller_service.h"
 #import "ios/chrome/browser/dom_distiller/model/distiller_viewer.h"
 #import "ios/chrome/browser/reader_mode/model/constants.h"
+#import "ios/chrome/browser/reader_mode/model/reader_mode_content_delegate.h"
 #import "ios/web/public/web_state_observer.h"
 #import "ios/web/public/web_state_user_data.h"
 
@@ -18,7 +19,8 @@
 
 // Observes changes to the web state to perform reader mode operations.
 class ReaderModeTabHelper : public web::WebStateObserver,
-                            public web::WebStateUserData<ReaderModeTabHelper> {
+                            public web::WebStateUserData<ReaderModeTabHelper>,
+                            public ReaderModeContentDelegate {
  public:
   ReaderModeTabHelper(web::WebState* web_state,
                       DistillerService* distiller_service);
@@ -52,12 +54,20 @@
   // web::WebStateObserver overrides:
   void DidStartNavigation(web::WebState* web_state,
                           web::NavigationContext* navigation_context) override;
+  void DidFinishNavigation(web::WebState* web_state,
+                           web::NavigationContext* navigation_context) override;
   void PageLoaded(
       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(
+      ReaderModeContentTabHelper* reader_mode_content_tab_helper,
+      NSURLRequest* request,
+      web::WebStatePolicyDecider::RequestInfo request_info) override;
+
   // Trigger the heuristic to determine reader mode eligibility.
   void TriggerReaderModeHeuristic();
 
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 593ac9c..0102670e 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
@@ -14,16 +14,19 @@
 #import "components/ukm/ios/ukm_url_recorder.h"
 #import "ios/chrome/browser/dom_distiller/model/distiller_viewer.h"
 #import "ios/chrome/browser/reader_mode/model/features.h"
+#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/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/public/commands/reader_mode_commands.h"
 #import "ios/chrome/browser/shared/public/commands/snackbar_commands.h"
 #import "ios/chrome/browser/shared/public/features/system_flags.h"
+#import "ios/web/navigation/wk_navigation_util.h"
 #import "ios/web/public/js_messaging/web_frames_manager.h"
 #import "ios/web/public/navigation/navigation_context.h"
 #import "ios/web/public/navigation/navigation_item.h"
 #import "ios/web/public/navigation/navigation_manager.h"
+#import "net/base/apple/url_conversions.h"
 #import "services/metrics/public/cpp/ukm_builders.h"
 
 namespace {
@@ -179,6 +182,9 @@
         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();
@@ -242,7 +248,15 @@
   if (trigger_reader_mode_timer_.IsRunning()) {
     trigger_reader_mode_timer_.Stop();
   }
-  HideReaderMode();
+}
+
+void ReaderModeTabHelper::DidFinishNavigation(
+    web::WebState* web_state,
+    web::NavigationContext* navigation_context) {
+  if (!navigation_context->IsSameDocument() ||
+      navigation_context->HasUserGesture()) {
+    HideReaderMode();
+  }
 }
 
 void ReaderModeTabHelper::WebStateDestroyed(web::WebState* web_state) {
@@ -257,6 +271,24 @@
   HideReaderMode();
 }
 
+void ReaderModeTabHelper::ReaderModeContentDidCancelRequest(
+    ReaderModeContentTabHelper* reader_mode_content_tab_helper,
+    NSURLRequest* request,
+    web::WebStatePolicyDecider::RequestInfo request_info) {
+  // When the Reader mode content cancels a request to navigate, load the
+  // requested URL in the host WebState instead.
+  web::NavigationManager::WebLoadParams params(net::GURLWithNSURL(request.URL));
+  NSString* referrer_value = [request
+      valueForHTTPHeaderField:web::wk_navigation_util::kReferrerHeaderName];
+  if (referrer_value) {
+    NSURL* referrer_url = [NSURL URLWithString:referrer_value];
+    params.referrer.url = net::GURLWithNSURL(referrer_url);
+    params.referrer.policy = web::ReferrerPolicyDefault;
+  }
+  params.transition_type = request_info.transition_type;
+  web_state_->GetNavigationManager()->LoadURLWithParams(params);
+}
+
 void ReaderModeTabHelper::HandleReaderModeHeuristicResult(
     const GURL& url,
     ReaderModeHeuristicResult result) {
@@ -379,16 +411,12 @@
 
   if (IsReaderModeAvailable()) {
     if (is_distillable_page) {
-      // `LoadData` requires an already committed navigation item.
-      std::vector<std::unique_ptr<web::NavigationItem>> navigation_items;
-      navigation_items.push_back(web::NavigationItem::Create());
-      reader_mode_web_state_->GetNavigationManager()->Restore(
-          0, std::move(navigation_items));
-      reader_mode_web_state_->GetNavigationManager()->LoadIfNecessary();
-      // Load the Reader mode content in the Reader mode WebState.
-      reader_mode_web_state_->LoadData(
-          [NSData dataWithBytes:html.data() length:html.length()], @"text/html",
-          web_state_->GetLastCommittedURL());
+      // Load the Reader mode content in the Reader mode content WebState.
+      const GURL content_url = web_state_->GetLastCommittedURL();
+      NSData* content_data = [NSData dataWithBytes:html.data()
+                                            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 {
diff --git a/ios/chrome/browser/reading_list/model/reading_list_browser_agent.mm b/ios/chrome/browser/reading_list/model/reading_list_browser_agent.mm
index b61163aa..0d50d9c 100644
--- a/ios/chrome/browser/reading_list/model/reading_list_browser_agent.mm
+++ b/ios/chrome/browser/reading_list/model/reading_list_browser_agent.mm
@@ -33,7 +33,8 @@
 #import "services/metrics/public/cpp/ukm_builders.h"
 #import "ui/base/l10n/l10n_util.h"
 
-ReadingListBrowserAgent::ReadingListBrowserAgent(Browser* browser) {
+ReadingListBrowserAgent::ReadingListBrowserAgent(Browser* browser)
+    : BrowserUserData(browser) {
   browser_ = browser;
 }
 
diff --git a/ios/chrome/browser/send_tab_to_self/model/send_tab_to_self_browser_agent.mm b/ios/chrome/browser/send_tab_to_self/model/send_tab_to_self_browser_agent.mm
index e9adf2f..3ed11416 100644
--- a/ios/chrome/browser/send_tab_to_self/model/send_tab_to_self_browser_agent.mm
+++ b/ios/chrome/browser/send_tab_to_self/model/send_tab_to_self_browser_agent.mm
@@ -29,7 +29,8 @@
 #import "ios/web/public/web_state.h"
 
 SendTabToSelfBrowserAgent::SendTabToSelfBrowserAgent(Browser* browser)
-    : browser_(browser),
+    : BrowserUserData(browser),
+      browser_(browser),
       model_(
           SendTabToSelfSyncServiceFactory::GetForProfile(browser_->GetProfile())
               ->GetSendTabToSelfModel()) {
diff --git a/ios/chrome/browser/sessions/model/live_tab_context_browser_agent.mm b/ios/chrome/browser/sessions/model/live_tab_context_browser_agent.mm
index 749cbbc..6b60363 100644
--- a/ios/chrome/browser/sessions/model/live_tab_context_browser_agent.mm
+++ b/ios/chrome/browser/sessions/model/live_tab_context_browser_agent.mm
@@ -22,7 +22,8 @@
 #import "ui/base/mojom/window_show_state.mojom.h"
 
 LiveTabContextBrowserAgent::LiveTabContextBrowserAgent(Browser* browser)
-    : profile_(browser->GetProfile()),
+    : BrowserUserData(browser),
+      profile_(browser->GetProfile()),
       web_state_list_(browser->GetWebStateList()),
       session_id_(SessionID::NewUnique()) {}
 
diff --git a/ios/chrome/browser/sessions/model/session_restoration_browser_agent.mm b/ios/chrome/browser/sessions/model/session_restoration_browser_agent.mm
index e3bf483..80c1d9f 100644
--- a/ios/chrome/browser/sessions/model/session_restoration_browser_agent.mm
+++ b/ios/chrome/browser/sessions/model/session_restoration_browser_agent.mm
@@ -282,7 +282,8 @@
     SessionServiceIOS* session_service,
     bool enable_pinned_web_states,
     bool enable_tab_groups)
-    : session_service_(session_service),
+    : BrowserUserData(browser),
+      session_service_(session_service),
       browser_(browser),
       session_window_ios_factory_([[SessionWindowIOSFactory alloc]
           initWithWebStateList:browser_->GetWebStateList()]),
diff --git a/ios/chrome/browser/settings/ui_bundled/google_services/BUILD.gn b/ios/chrome/browser/settings/ui_bundled/google_services/BUILD.gn
index 50d8f107..205f610 100644
--- a/ios/chrome/browser/settings/ui_bundled/google_services/BUILD.gn
+++ b/ios/chrome/browser/settings/ui_bundled/google_services/BUILD.gn
@@ -24,12 +24,12 @@
     "manage_sync_settings_table_view_controller.h",
     "manage_sync_settings_table_view_controller.mm",
     "manage_sync_settings_table_view_controller_model_delegate.h",
-    "sync_error_settings_command_handler.h",
   ]
   deps = [
     ":constants",
     ":features",
     ":personalize_google_services",
+    ":sync_error_settings_command_handler",
     "//base",
     "//components/browser_sync",
     "//components/google/core/common",
@@ -52,12 +52,13 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/authentication/ui_bundled",
     "//ios/chrome/browser/authentication/ui_bundled:authentication_constants",
+    "//ios/chrome/browser/authentication/ui_bundled/account_menu",
+    "//ios/chrome/browser/authentication/ui_bundled/account_menu:constants",
     "//ios/chrome/browser/authentication/ui_bundled/authentication_flow",
     "//ios/chrome/browser/authentication/ui_bundled/cells",
     "//ios/chrome/browser/authentication/ui_bundled/enterprise:enterprise_utils",
     "//ios/chrome/browser/authentication/ui_bundled/history_sync",
     "//ios/chrome/browser/authentication/ui_bundled/signin:signin_headers",
-    "//ios/chrome/browser/authentication/ui_bundled/signin/account_menu:constants",
     "//ios/chrome/browser/authentication/ui_bundled/signout_action_sheet",
     "//ios/chrome/browser/authentication/ui_bundled/trusted_vault_reauthentication",
     "//ios/chrome/browser/commerce/model:model",
@@ -116,6 +117,12 @@
   ]
 }
 
+source_set("sync_error_settings_command_handler") {
+  sources = [ "sync_error_settings_command_handler.h" ]
+  deps = []
+  frameworks = [ "UIKit.framework" ]
+}
+
 source_set("personalize_google_services") {
   sources = [
     "personalize_google_services_command_handler.h",
@@ -210,7 +217,7 @@
     "//components/variations",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/authentication/ui_bundled:eg_test_support+eg2",
-    "//ios/chrome/browser/authentication/ui_bundled/signin/account_menu:constants",
+    "//ios/chrome/browser/authentication/ui_bundled/account_menu:constants",
     "//ios/chrome/browser/authentication/ui_bundled/views:views_constants",
     "//ios/chrome/browser/bookmarks/model:bookmark_storage_type",
     "//ios/chrome/browser/bookmarks/ui_bundled:eg_test_support+eg2",
diff --git a/ios/chrome/browser/settings/ui_bundled/google_services/manage_accounts/BUILD.gn b/ios/chrome/browser/settings/ui_bundled/google_services/manage_accounts/BUILD.gn
index 0959aa8..2d3d686b 100644
--- a/ios/chrome/browser/settings/ui_bundled/google_services/manage_accounts/BUILD.gn
+++ b/ios/chrome/browser/settings/ui_bundled/google_services/manage_accounts/BUILD.gn
@@ -187,7 +187,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/authentication/ui_bundled/cells",
     "//ios/chrome/browser/settings/ui_bundled/cells",
-    "//ios/chrome/browser/settings/ui_bundled/google_services",
     "//ios/chrome/browser/settings/ui_bundled/google_services:constants",
     "//ios/chrome/browser/settings/ui_bundled/google_services/manage_accounts",
     "//ios/chrome/browser/shared/model/application_context",
diff --git a/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_coordinator.mm b/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_coordinator.mm
index 7113e98..39b44cdc 100644
--- a/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_coordinator.mm
+++ b/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_coordinator.mm
@@ -18,7 +18,9 @@
 #import "components/sync/service/sync_user_settings.h"
 #import "components/trusted_vault/trusted_vault_server_constants.h"
 #import "ios/chrome/app/application_delegate/app_state.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator_delegate.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signout_action_sheet/signout_action_sheet_coordinator.h"
 #import "ios/chrome/browser/authentication/ui_bundled/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator.h"
@@ -72,6 +74,7 @@
 using DismissViewCallback = SystemIdentityManager::DismissViewCallback;
 
 @interface ManageSyncSettingsCoordinator () <
+    AccountMenuCoordinatorDelegate,
     BulkUploadCoordinatorDelegate,
     ManageAccountsCoordinatorDelegate,
     ManageSyncSettingsCommandHandler,
@@ -94,7 +97,7 @@
   SyncEncryptionPassphraseTableViewController*
       _syncEncryptionPassphraseTableViewController;
   // Account menu coordinator.
-  SigninCoordinator* _accountMenuCoordinator;
+  AccountMenuCoordinator* _accountMenuCoordinator;
   TrustedVaultReauthenticationCoordinator*
       _trustedVaultReauthenticationCoordinator;
 }
@@ -197,7 +200,7 @@
   [self.mediator disconnect];
   [self stopBulkUpload];
   [self stopManageAccountsCoordinator];
-  [self stopAccountMenuCoordinatorAnimated:YES];
+  [self stopAccountMenuCoordinator];
   [self stopTrustedVaultReauthenticationCoordinator];
   self.mediator = nil;
   self.viewController = nil;
@@ -260,8 +263,9 @@
   _personalizeGoogleServicesCoordinator = nil;
 }
 
-- (void)stopAccountMenuCoordinatorAnimated:(BOOL)animated {
-  [_accountMenuCoordinator stopAnimated:animated];
+- (void)stopAccountMenuCoordinator {
+  [_accountMenuCoordinator stop];
+  _accountMenuCoordinator.delegate = nil;
   _accountMenuCoordinator = nil;
 }
 
@@ -498,21 +502,13 @@
 }
 
 - (void)openAccountMenu {
-  _accountMenuCoordinator = [SigninCoordinator
-      accountMenuCoordinatorWithBaseViewController:self.viewController
-                                           browser:self.browser
-                                      contextStyle:SigninContextStyle::kDefault
-                                        anchorView:nil
-                                       accessPoint:AccountMenuAccessPoint::
-                                                       kSettings
-                                               URL:GURL()];
-
-  __weak __typeof(self) weakself = self;
-  _accountMenuCoordinator.signinCompletion =
-      ^(SigninCoordinatorResult result, id<SystemIdentity> identity) {
-        [weakself stopAccountMenuCoordinatorAnimated:YES];
-      };
-
+  _accountMenuCoordinator = [[AccountMenuCoordinator alloc]
+      initWithBaseViewController:self.viewController
+                         browser:self.browser
+                      anchorView:_viewController.view
+                     accessPoint:AccountMenuAccessPoint::kNewTabPage
+                             URL:GURL()];
+  _accountMenuCoordinator.delegate = self;
   [_accountMenuCoordinator start];
 }
 
@@ -673,4 +669,12 @@
   [self stopTrustedVaultReauthenticationCoordinator];
 }
 
+#pragma mark - AccountMenuCoordinatorDelegate
+
+- (void)accountMenuCoordinatorWantsToBeStopped:
+    (AccountMenuCoordinator*)coordinator {
+  CHECK_EQ(_accountMenuCoordinator, coordinator, base::NotFatalUntil::M140);
+  [self stopAccountMenuCoordinator];
+}
+
 @end
diff --git a/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_egtest.mm b/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_egtest.mm
index 6de4c63..a6160d349 100644
--- a/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_egtest.mm
+++ b/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_egtest.mm
@@ -10,7 +10,7 @@
 #import "components/strings/grit/components_strings.h"
 #import "components/sync/base/features.h"
 #import "components/sync/base/user_selectable_type.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey_app_interface.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey_ui_test_util.h"
diff --git a/ios/chrome/browser/settings/ui_bundled/password/passwords_coordinator.mm b/ios/chrome/browser/settings/ui_bundled/password/passwords_coordinator.mm
index c7b90e0..ff74cca 100644
--- a/ios/chrome/browser/settings/ui_bundled/password/passwords_coordinator.mm
+++ b/ios/chrome/browser/settings/ui_bundled/password/passwords_coordinator.mm
@@ -390,10 +390,8 @@
 - (void)performReauthenticationForRetrievingTrustedVaultKey {
   trusted_vault::SecurityDomainId securityDomainID =
       trusted_vault::SecurityDomainId::kChromeSync;
-  // TODO(crbug.com/407605858): Add to `TrustedVaultUserActionTriggerForUMA` a
-  // separate enum entry for representing the GPM management UI.
   syncer::TrustedVaultUserActionTriggerForUMA trigger =
-      syncer::TrustedVaultUserActionTriggerForUMA::kSettings;
+      syncer::TrustedVaultUserActionTriggerForUMA::kPasswordManagerSettings;
   CHECK(!_trustedVaultReauthenticationCoordinator, base::NotFatalUntil::M145);
   _trustedVaultReauthenticationCoordinator =
       [[TrustedVaultReauthenticationCoordinator alloc]
diff --git a/ios/chrome/browser/shared/coordinator/scene/BUILD.gn b/ios/chrome/browser/shared/coordinator/scene/BUILD.gn
index ffe30634..87794ed09 100644
--- a/ios/chrome/browser/shared/coordinator/scene/BUILD.gn
+++ b/ios/chrome/browser/shared/coordinator/scene/BUILD.gn
@@ -101,12 +101,13 @@
     "//ios/chrome/browser/appearance/ui_bundled",
     "//ios/chrome/browser/authentication/ui_bundled",
     "//ios/chrome/browser/authentication/ui_bundled:continuation",
+    "//ios/chrome/browser/authentication/ui_bundled/account_menu",
+    "//ios/chrome/browser/authentication/ui_bundled/account_menu:constants",
     "//ios/chrome/browser/authentication/ui_bundled/authentication_flow",
     "//ios/chrome/browser/authentication/ui_bundled/change_profile",
     "//ios/chrome/browser/authentication/ui_bundled/enterprise:enterprise_utils",
     "//ios/chrome/browser/authentication/ui_bundled/signin",
     "//ios/chrome/browser/authentication/ui_bundled/signin:features",
-    "//ios/chrome/browser/authentication/ui_bundled/signin/account_menu:constants",
     "//ios/chrome/browser/authentication/ui_bundled/signin/promo",
     "//ios/chrome/browser/authentication/ui_bundled/signout_action_sheet",
     "//ios/chrome/browser/blocking_overlay/ui_bundled",
@@ -130,6 +131,8 @@
     "//ios/chrome/browser/incognito_interstitial/ui_bundled:coordinator",
     "//ios/chrome/browser/incognito_reauth/ui_bundled:incognito_reauth_scene_agent",
     "//ios/chrome/browser/infobars/model",
+    "//ios/chrome/browser/intelligence/features",
+    "//ios/chrome/browser/intelligence/glic/coordinator",
     "//ios/chrome/browser/intents/model:user_activity_browser_agent",
     "//ios/chrome/browser/lens/ui_bundled:lens_entrypoint",
     "//ios/chrome/browser/lens_overlay/model",
diff --git a/ios/chrome/browser/shared/coordinator/scene/DEPS b/ios/chrome/browser/shared/coordinator/scene/DEPS
index b52a2d92..13f61548 100644
--- a/ios/chrome/browser/shared/coordinator/scene/DEPS
+++ b/ios/chrome/browser/shared/coordinator/scene/DEPS
@@ -17,6 +17,8 @@
   "+ios/chrome/browser/incognito_interstitial/ui_bundled",
   "+ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent.h",
   "+ios/chrome/browser/infobars/model",
+  "+ios/chrome/browser/intelligence/features",
+  "+ios/chrome/browser/intelligence/glic/coordinator",
   "+ios/chrome/browser/intents/model",
   "+ios/chrome/browser/lens/ui_bundled",
   "+ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h",
diff --git a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
index bcd1a1b..4db142e2 100644
--- a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
+++ b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
@@ -55,11 +55,13 @@
 #import "ios/chrome/browser/app_store_rating/ui_bundled/app_store_rating_scene_agent.h"
 #import "ios/chrome/browser/app_store_rating/ui_bundled/features.h"
 #import "ios/chrome/browser/appearance/ui_bundled/appearance_customization.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_coordinator_delegate.h"
 #import "ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow.h"
 #import "ios/chrome/browser/authentication/ui_bundled/change_profile/change_profile_authentication_continuation.h"
 #import "ios/chrome/browser/authentication/ui_bundled/change_profile/change_profile_load_url.h"
 #import "ios/chrome/browser/authentication/ui_bundled/continuation.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/features.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/promo/signin_fullscreen_promo_scene_agent.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
@@ -91,6 +93,8 @@
 #import "ios/chrome/browser/incognito_interstitial/ui_bundled/incognito_interstitial_coordinator_delegate.h"
 #import "ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent.h"
 #import "ios/chrome/browser/infobars/model/infobar_manager_impl.h"
+#import "ios/chrome/browser/intelligence/features/features.h"
+#import "ios/chrome/browser/intelligence/glic/coordinator/glic_promo_scene_agent.h"
 #import "ios/chrome/browser/intents/model/user_activity_browser_agent.h"
 #import "ios/chrome/browser/lens/ui_bundled/lens_entrypoint.h"
 #import "ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h"
@@ -369,7 +373,8 @@
 
 }  // namespace
 
-@interface SceneController () <HistoryCoordinatorDelegate,
+@interface SceneController () <AccountMenuCoordinatorDelegate,
+                               HistoryCoordinatorDelegate,
                                IncognitoInterstitialCoordinatorDelegate,
                                PasswordCheckupCoordinatorDelegate,
                                PolicyWatcherBrowserAgentObserving,
@@ -406,6 +411,7 @@
   // Fetches the Family Link member role asynchronously from KidsManagement API.
   std::unique_ptr<supervised_user::ListFamilyMembersFetcher>
       _familyMembersFetcher;
+  AccountMenuCoordinator* _accountMenuCoordinator;
 }
 
 // Navigation View controller for the settings.
@@ -863,6 +869,12 @@
 
 #pragma mark - private
 
+- (void)stopAccountMenu {
+  [_accountMenuCoordinator stop];
+  _accountMenuCoordinator.delegate = nil;
+  _accountMenuCoordinator = nil;
+}
+
 - (void)handleURLContextsToOpen {
   if (self.sceneState.URLContextsToOpen.count == 0) {
     return;
@@ -1346,6 +1358,11 @@
                           syncService:SyncServiceFactory::GetForProfile(profile)
                           prefService:prefService]];
   }
+
+  if (IsPageActionMenuEnabled()) {
+    [sceneState addAgent:[[GLICPromoSceneAgent alloc]
+                             initWithPromosManager:promosManager]];
+  }
 }
 
 // Determines the mode (normal or incognito) the initial UI should be in.
@@ -1506,6 +1523,8 @@
   [_mainCoordinator stop];
   _mainCoordinator = nil;
 
+  [self stopAccountMenu];
+
   _incognitoWebStateObserver.reset();
   _mainWebStateObserver.reset();
   _policyWatcherObserver.reset();
@@ -2160,112 +2179,10 @@
     return;
   }
   Browser* mainBrowser = self.mainInterface.browser;
-
-  switch (command.operation) {
-    case AuthenticationOperation::kPrimaryAccountReauth:
-      self.signinCoordinator = [SigninCoordinator
-          primaryAccountReauthCoordinatorWithBaseViewController:
-              baseViewController
-                                                        browser:mainBrowser
-                                                   contextStyle:
-                                                       command.contextStyle
-
-                                                    accessPoint:command
-                                                                    .accessPoint
-                                                    promoAction:command
-                                                                    .promoAction
-                                           continuationProvider:
-                                               command
-                                                   .changeProfileContinuationProvider];
-      break;
-    case AuthenticationOperation::kResignin:
-      self.signinCoordinator = [SigninCoordinator
-          signinAndSyncReauthCoordinatorWithBaseViewController:
-              baseViewController
-                                                       browser:mainBrowser
-                                                  contextStyle:command
-                                                                   .contextStyle
-                                                   accessPoint:command
-                                                                   .accessPoint
-                                                   promoAction:command
-                                                                   .promoAction
-                                          continuationProvider:
-                                              command
-                                                  .changeProfileContinuationProvider];
-      break;
-    case AuthenticationOperation::kSigninOnly: {
-      auto& provider = command.changeProfileContinuationProvider;
-      self.signinCoordinator = [SigninCoordinator
-          consistencyPromoSigninCoordinatorWithBaseViewController:
-              baseViewController
-                                                          browser:mainBrowser
-                                                     contextStyle:
-                                                         command.contextStyle
-                                                      accessPoint:
-                                                          command.accessPoint
-                                             prepareChangeProfile:
-                                                 command.prepareChangeProfile
-                                             continuationProvider:provider];
-      break;
-    }
-    case AuthenticationOperation::kAddAccount:
-      self.signinCoordinator = [SigninCoordinator
-          addAccountCoordinatorWithBaseViewController:baseViewController
+  self.signinCoordinator =
+      [SigninCoordinator signinCoordinatorWithCommand:command
                                               browser:mainBrowser
-                                         contextStyle:command.contextStyle
-                                          accessPoint:command.accessPoint
-                                 continuationProvider:
-                                     command.changeProfileContinuationProvider];
-      break;
-    case AuthenticationOperation::kForcedSigninAndSync:
-      self.signinCoordinator = [SigninCoordinator
-          fullscreenSigninCoordinatorWithBaseViewController:baseViewController
-                                                    browser:mainBrowser
-                                               contextStyle:command.contextStyle
-                                                accessPoint:command.accessPoint
-                          changeProfileContinuationProvider:
-                              command.changeProfileContinuationProvider];
-      break;
-    case AuthenticationOperation::kInstantSignin:
-      self.signinCoordinator = [SigninCoordinator
-          instantSigninCoordinatorWithBaseViewController:baseViewController
-                                                 browser:mainBrowser
-                                                identity:command.identity
-                                            contextStyle:command.contextStyle
-                                             accessPoint:command.accessPoint
-                                             promoAction:command.promoAction
-                                    continuationProvider:
-                                        command
-                                            .changeProfileContinuationProvider];
-      break;
-    case AuthenticationOperation::kSheetSigninAndHistorySync: {
-      auto& provider = command.changeProfileContinuationProvider;
-      self.signinCoordinator = [SigninCoordinator
-          signinAndHistorySyncCoordinatorWithBaseViewController:
-              baseViewController
-                                                        browser:mainBrowser
-                                                   contextStyle:
-                                                       command.contextStyle
-                                                    accessPoint:command
-                                                                    .accessPoint
-                                                    promoAction:command
-                                                                    .promoAction
-                                            optionalHistorySync:
-                                                command.optionalHistorySync
-                                                fullscreenPromo:
-                                                    command.fullScreenPromo
-                                           continuationProvider:provider];
-      break;
-    }
-    case AuthenticationOperation::kHistorySync:
-      self.signinCoordinator = [SigninCoordinator
-          historySyncCoordinatorWithBaseViewController:baseViewController
-                                               browser:mainBrowser
-                                          contextStyle:command.contextStyle
-                                           accessPoint:command.accessPoint
-                                           promoAction:command.promoAction];
-      break;
-  }
+                                   baseViewController:baseViewController];
   [self startSigninCoordinatorWithCompletion:command.completion];
 }
 
@@ -2279,17 +2196,16 @@
       << base::SysNSStringToUTF8([self.signinCoordinator description]);
   Browser* browser = self.mainInterface.browser;
   UIViewController* baseViewController = self.mainInterface.viewController;
-  SigninCoordinator* accountMenuCoordinator = [SigninCoordinator
-      accountMenuCoordinatorWithBaseViewController:baseViewController
-                                           browser:browser
-                                      contextStyle:SigninContextStyle::kDefault
-                                        anchorView:nil
-                                       accessPoint:accessPoint
-                                               URL:url];
-  self.signinCoordinator = accountMenuCoordinator;
+  _accountMenuCoordinator = [[AccountMenuCoordinator alloc]
+      initWithBaseViewController:baseViewController
+                         browser:browser
+                      anchorView:nil
+                     accessPoint:AccountMenuAccessPoint::kWeb
+                             URL:url];
+  _accountMenuCoordinator.delegate = self;
   // TODO(crbug.com/336719423): Record signin metrics based on the
   // selected action from the account switcher.
-  [self startSigninCoordinatorWithCompletion:nil];
+  [_accountMenuCoordinator start];
 }
 
 - (void)showWebSigninPromoFromViewController:
@@ -4585,4 +4501,14 @@
   [self closeHistoryWithCompletion:nil];
 }
 
+#pragma mark - AccountMenuCoordinatorDelegate
+
+// Update the state, to take into account that the account menu coordinator is
+// stopped.
+- (void)accountMenuCoordinatorWantsToBeStopped:
+    (AccountMenuCoordinator*)coordinator {
+  CHECK_EQ(_accountMenuCoordinator, coordinator, base::NotFatalUntil::M140);
+  [self stopAccountMenu];
+}
+
 @end
diff --git a/ios/chrome/browser/shared/public/commands/BUILD.gn b/ios/chrome/browser/shared/public/commands/BUILD.gn
index 63f9370..79a4725e 100644
--- a/ios/chrome/browser/shared/public/commands/BUILD.gn
+++ b/ios/chrome/browser/shared/public/commands/BUILD.gn
@@ -32,6 +32,7 @@
     "generate_qr_code_command.h",
     "generate_qr_code_command.mm",
     "google_one_commands.h",
+    "guided_tour_commands.h",
     "help_commands.h",
     "lens_commands.h",
     "lens_overlay_commands.h",
diff --git a/ios/chrome/browser/shared/public/commands/guided_tour_commands.h b/ios/chrome/browser/shared/public/commands/guided_tour_commands.h
new file mode 100644
index 0000000..866d6ec5
--- /dev/null
+++ b/ios/chrome/browser/shared/public/commands/guided_tour_commands.h
@@ -0,0 +1,32 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_GUIDED_TOUR_COMMANDS_H_
+#define IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_GUIDED_TOUR_COMMANDS_H_
+
+#import <UIKit/UIKit.h>
+
+#import "base/ios/block_types.h"
+
+// Different steps of the Guided Tour.
+typedef NS_ENUM(NSUInteger, GuidedTourStep) {
+  GuidedTourStepNTP,
+  GuidedTourStepTabGridIncognito,
+  GuidedTourStepTabGridLongPress,
+  GuidedTourStepTabGridTabGroup
+};
+
+// Commands related to the Guided Tour.
+@protocol GuidedTourCommands
+
+// Indicates to the receiver that the spotlit view for `step` should be
+// highlighted.
+- (void)highlightViewInStep:(GuidedTourStep)step;
+
+// Indicates to the receiver that the `step` has completed.
+- (void)stepCompleted:(GuidedTourStep)step;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_GUIDED_TOUR_COMMANDS_H_
diff --git a/ios/chrome/browser/shared/public/commands/lens_overlay_commands.h b/ios/chrome/browser/shared/public/commands/lens_overlay_commands.h
index ada8d6fc..302a58b 100644
--- a/ios/chrome/browser/shared/public/commands/lens_overlay_commands.h
+++ b/ios/chrome/browser/shared/public/commands/lens_overlay_commands.h
@@ -28,9 +28,12 @@
                  completion:(void (^)(BOOL))completion;
 
 /// Responds to a search image with Lens request by creating a new Lens UI with
-/// the given image. The completion is called once the UI is presented.
+/// the given image. The overlay will be shown over the specified
+/// initial presentation base.
+/// The completion is called once the UI is presented.
 - (void)searchWithLensImageMetadata:(id<LensImageMetadata>)metadata
                          entrypoint:(LensOverlayEntrypoint)entrypoint
+            initialPresentationBase:(UIViewController*)initialPresentationBase
                          completion:(void (^)(BOOL))completion;
 
 /// Display the lens overlay, if it exists.
diff --git a/ios/chrome/browser/signin/model/BUILD.gn b/ios/chrome/browser/signin/model/BUILD.gn
index 5018f939..69ac5df 100644
--- a/ios/chrome/browser/signin/model/BUILD.gn
+++ b/ios/chrome/browser/signin/model/BUILD.gn
@@ -77,7 +77,7 @@
     "//google_apis",
     "//ios/chrome/app:change_profile_commands",
     "//ios/chrome/app:tests_hook",
-    "//ios/chrome/browser/authentication/ui_bundled/signin/account_menu:constants",
+    "//ios/chrome/browser/authentication/ui_bundled/account_menu:constants",
     "//ios/chrome/browser/bookmarks/model:model_utils",
     "//ios/chrome/browser/content_settings/model",
     "//ios/chrome/browser/crash_report/model",
@@ -483,7 +483,7 @@
     "//components/sync_preferences:test_support",
     "//google_apis",
     "//ios/chrome/app:change_profile_commands",
-    "//ios/chrome/browser/authentication/ui_bundled/signin/account_menu:constants",
+    "//ios/chrome/browser/authentication/ui_bundled/account_menu:constants",
     "//ios/chrome/browser/content_settings/model",
     "//ios/chrome/browser/lens/model",
     "//ios/chrome/browser/policy/model:policy_util",
diff --git a/ios/chrome/browser/signin/model/DEPS b/ios/chrome/browser/signin/model/DEPS
index 94f7cfe..ebd7c56f 100644
--- a/ios/chrome/browser/signin/model/DEPS
+++ b/ios/chrome/browser/signin/model/DEPS
@@ -10,7 +10,7 @@
   "+ios/chrome/browser/browsing_data/model",
   "+ios/chrome/browser/content_settings/model",
   "+ios/chrome/browser/crash_report/model",
-  "+ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h",
+  "+ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h",
   "+ios/chrome/browser/policy/model",
   "+ios/chrome/browser/sync/model",
   "+ios/chrome/browser/web/model",
diff --git a/ios/chrome/browser/signin/model/account_consistency_browser_agent.mm b/ios/chrome/browser/signin/model/account_consistency_browser_agent.mm
index 89b136f..57437e7b 100644
--- a/ios/chrome/browser/signin/model/account_consistency_browser_agent.mm
+++ b/ios/chrome/browser/signin/model/account_consistency_browser_agent.mm
@@ -8,7 +8,7 @@
 
 #import "components/signin/core/browser/account_reconcilor.h"
 #import "components/signin/ios/browser/account_consistency_service.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/profile/features.h"
 #import "ios/chrome/browser/shared/model/profile/profile_attributes_storage_ios.h"
@@ -26,7 +26,9 @@
 AccountConsistencyBrowserAgent::AccountConsistencyBrowserAgent(
     Browser* browser,
     UIViewController* base_view_controller)
-    : base_view_controller_(base_view_controller), browser_(browser) {
+    : BrowserUserData(browser),
+      base_view_controller_(base_view_controller),
+      browser_(browser) {
   installation_observer_ =
       std::make_unique<WebStateDependencyInstallationObserver>(
           browser->GetWebStateList(), this);
diff --git a/ios/chrome/browser/signin/model/account_consistency_browser_agent_unittest.mm b/ios/chrome/browser/signin/model/account_consistency_browser_agent_unittest.mm
index b6f4e71b..b127579 100644
--- a/ios/chrome/browser/signin/model/account_consistency_browser_agent_unittest.mm
+++ b/ios/chrome/browser/signin/model/account_consistency_browser_agent_unittest.mm
@@ -6,7 +6,7 @@
 
 #import "base/memory/raw_ptr.h"
 #import "base/test/scoped_feature_list.h"
-#import "ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_constants.h"
+#import "ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_constants.h"
 #import "ios/chrome/browser/lens/model/lens_browser_agent.h"
 #import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
 #import "ios/chrome/browser/shared/model/profile/features.h"
diff --git a/ios/chrome/browser/snapshots/model/snapshot_browser_agent.mm b/ios/chrome/browser/snapshots/model/snapshot_browser_agent.mm
index 972c8e68..c0d673ee 100644
--- a/ios/chrome/browser/snapshots/model/snapshot_browser_agent.mm
+++ b/ios/chrome/browser/snapshots/model/snapshot_browser_agent.mm
@@ -23,7 +23,7 @@
 }  // anonymous namespace
 
 SnapshotBrowserAgent::SnapshotBrowserAgent(Browser* browser)
-    : browser_(browser) {
+    : BrowserUserData(browser), browser_(browser) {
   browser->AddObserver(this);
   browser->GetWebStateList()->AddObserver(this);
 }
diff --git a/ios/chrome/browser/start_surface/ui_bundled/start_surface_recent_tab_browser_agent.mm b/ios/chrome/browser/start_surface/ui_bundled/start_surface_recent_tab_browser_agent.mm
index be17aa8..bf50294 100644
--- a/ios/chrome/browser/start_surface/ui_bundled/start_surface_recent_tab_browser_agent.mm
+++ b/ios/chrome/browser/start_surface/ui_bundled/start_surface_recent_tab_browser_agent.mm
@@ -15,7 +15,9 @@
 
 StartSurfaceRecentTabBrowserAgent::StartSurfaceRecentTabBrowserAgent(
     Browser* browser)
-    : favicon_driver_observer_(this), browser_(browser) {
+    : BrowserUserData(browser),
+      favicon_driver_observer_(this),
+      browser_(browser) {
   browser_->AddObserver(this);
   browser_->GetWebStateList()->AddObserver(this);
 }
diff --git a/ios/chrome/browser/sync/model/sync_error_browser_agent.mm b/ios/chrome/browser/sync/model/sync_error_browser_agent.mm
index 8ef9b0b..7f8649c 100644
--- a/ios/chrome/browser/sync/model/sync_error_browser_agent.mm
+++ b/ios/chrome/browser/sync/model/sync_error_browser_agent.mm
@@ -85,7 +85,7 @@
 }  // namespace
 
 SyncErrorBrowserAgent::SyncErrorBrowserAgent(Browser* browser)
-    : browser_(browser) {
+    : BrowserUserData(browser), browser_(browser) {
   DCHECK(browser_);
   browser->AddObserver(this);
   browser->GetWebStateList()->AddObserver(this);
diff --git a/ios/chrome/browser/tab_insertion/model/tab_insertion_browser_agent.mm b/ios/chrome/browser/tab_insertion/model/tab_insertion_browser_agent.mm
index b9f6056..3362589 100644
--- a/ios/chrome/browser/tab_insertion/model/tab_insertion_browser_agent.mm
+++ b/ios/chrome/browser/tab_insertion/model/tab_insertion_browser_agent.mm
@@ -46,7 +46,7 @@
 }  // namespace
 
 TabInsertionBrowserAgent::TabInsertionBrowserAgent(Browser* browser)
-    : browser_(browser) {
+    : BrowserUserData(browser), browser_(browser) {
   DCHECK(browser_);
 }
 
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/BUILD.gn b/ios/chrome/browser/tab_switcher/ui_bundled/BUILD.gn
index b699b4a..17483ce 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/BUILD.gn
@@ -89,6 +89,7 @@
     "//ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_context_menu:tab_item",
     "//ios/chrome/browser/tabs/model",
     "//ios/chrome/common/ui/colors",
+    "//ios/chrome/common/ui/favicon",
     "//ios/web/public",
   ]
 
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_utils.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_utils.mm
index 7c55ff5e..b8c407d 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_utils.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_group_utils.mm
@@ -13,11 +13,17 @@
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ios/chrome/browser/snapshots/model/snapshot_tab_helper.h"
 #import "ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/group_tab_info.h"
+#import "ios/chrome/common/ui/favicon/favicon_attributes.h"
 #import "ios/web/public/web_state.h"
 
 namespace {
+
+// Size for the default favicon.
 const CGFloat kFaviconSize = 16;
-}
+// The minimum size of tab favicons.
+constexpr CGFloat kFaviconMinimumSize = 8.0;
+
+}  // namespace
 
 @implementation TabGroupUtils
 
@@ -82,8 +88,21 @@
     completion(groupTabInfo);
     return;
   }
-  // TODO(crbug.com/400966281): Fetch favicon on Google server.
-  completion(groupTabInfo);
+
+  // Asynchronously fetch the favicon.
+  faviconLoader->FaviconForPageUrl(
+      webState->GetVisibleURL(), kFaviconSize, kFaviconMinimumSize,
+      /*fallback_to_google_server=*/true, ^(FaviconAttributes* attributes) {
+        // Synchronously returned default favicon.
+        if (attributes.usesDefaultImage) {
+          return;
+        }
+        // Asynchronously returned favicon.
+        if (attributes.faviconImage) {
+          groupTabInfo.favicon = attributes.faviconImage;
+        }
+        completion(groupTabInfo);
+      });
 }
 
 @end
diff --git a/ios/chrome/browser/tabs/model/closing_web_state_observer_browser_agent.mm b/ios/chrome/browser/tabs/model/closing_web_state_observer_browser_agent.mm
index 6fa4733..0e47ac4 100644
--- a/ios/chrome/browser/tabs/model/closing_web_state_observer_browser_agent.mm
+++ b/ios/chrome/browser/tabs/model/closing_web_state_observer_browser_agent.mm
@@ -24,7 +24,7 @@
 
 ClosingWebStateObserverBrowserAgent::ClosingWebStateObserverBrowserAgent(
     Browser* browser)
-    : browser_(browser) {
+    : BrowserUserData(browser), browser_(browser) {
   DCHECK(!browser_->GetProfile()->IsOffTheRecord());
   browser_->AddObserver(this);
   browser_->GetWebStateList()->AddObserver(this);
diff --git a/ios/chrome/browser/tabs/model/synced_window_delegate_browser_agent.mm b/ios/chrome/browser/tabs/model/synced_window_delegate_browser_agent.mm
index 5d57c4b2..fcdd592a 100644
--- a/ios/chrome/browser/tabs/model/synced_window_delegate_browser_agent.mm
+++ b/ios/chrome/browser/tabs/model/synced_window_delegate_browser_agent.mm
@@ -14,7 +14,8 @@
 
 SyncedWindowDelegateBrowserAgent::SyncedWindowDelegateBrowserAgent(
     Browser* browser)
-    : web_state_list_(browser->GetWebStateList()),
+    : BrowserUserData(browser),
+      web_state_list_(browser->GetWebStateList()),
       session_id_(SessionID::NewUnique()) {
   browser->AddObserver(this);
   for (int index = 0; index < web_state_list_->count(); ++index) {
diff --git a/ios/chrome/browser/toolbar/ui_bundled/adaptive_toolbar_view_controller.h b/ios/chrome/browser/toolbar/ui_bundled/adaptive_toolbar_view_controller.h
index af037fe..50cbac0 100644
--- a/ios/chrome/browser/toolbar/ui_bundled/adaptive_toolbar_view_controller.h
+++ b/ios/chrome/browser/toolbar/ui_bundled/adaptive_toolbar_view_controller.h
@@ -69,6 +69,9 @@
 - (void)triggerToolbarSlideInAnimationFromBelow:(BOOL)fromBelow;
 // Shows the animation when transitioning to a prerendered page.
 - (void)showPrerenderingAnimation;
+// Highlights the tab grid button if `highlight` is YES, resets to original
+// color if NO.
+- (void)IPHHighlightTabGridButton:(BOOL)highlight;
 
 @end
 
diff --git a/ios/chrome/browser/toolbar/ui_bundled/adaptive_toolbar_view_controller.mm b/ios/chrome/browser/toolbar/ui_bundled/adaptive_toolbar_view_controller.mm
index 2a213fa..21697a87 100644
--- a/ios/chrome/browser/toolbar/ui_bundled/adaptive_toolbar_view_controller.mm
+++ b/ios/chrome/browser/toolbar/ui_bundled/adaptive_toolbar_view_controller.mm
@@ -28,6 +28,7 @@
 #import "ios/chrome/browser/toolbar/ui_bundled/public/toolbar_constants.h"
 #import "ios/chrome/browser/toolbar/ui_bundled/public/toolbar_utils.h"
 #import "ios/chrome/common/material_timing.h"
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/util/ui_util.h"
 #import "ui/base/device_form_factor.h"
 
@@ -126,6 +127,13 @@
   return self.locationBarViewController != nil;
 }
 
+- (void)IPHHighlightTabGridButton:(BOOL)highlight {
+  self.view.tabGridButton.iphHighlighted = highlight;
+  // Needed so spotlightView can have correct frame.
+  [self.view.tabGridButton setNeedsLayout];
+  [self.view.tabGridButton layoutIfNeeded];
+}
+
 #pragma mark - UIViewController
 
 - (void)viewDidAppear:(BOOL)animated {
diff --git a/ios/chrome/browser/toolbar/ui_bundled/buttons/toolbar_tab_grid_button.mm b/ios/chrome/browser/toolbar/ui_bundled/buttons/toolbar_tab_grid_button.mm
index b575a74..51a4445 100644
--- a/ios/chrome/browser/toolbar/ui_bundled/buttons/toolbar_tab_grid_button.mm
+++ b/ios/chrome/browser/toolbar/ui_bundled/buttons/toolbar_tab_grid_button.mm
@@ -49,12 +49,10 @@
     (ToolbarTabGridButtonImageLoader)tabGroupStateImageLoader {
   CHECK(tabGroupStateImageLoader);
   self = [super
-      initWithImageLoader:^{
-        return [[UIImage alloc] init];
-      }
-      IPHHighlightedImageLoader:^{
-        return [[UIImage alloc] init];
-      }];
+            initWithImageLoader:^{
+              return [[UIImage alloc] init];
+            }
+      IPHHighlightedImageLoader:nil];
   if (self) {
     _normalStateImageLoader = ^{
       return tabGroupStateImageLoader(ToolbarTabGroupState::kNormal);
@@ -90,6 +88,11 @@
   [self updateTabCountLabelTextColor];
 }
 
+- (void)setIphHighlighted:(BOOL)iphHighlighted {
+  [super setIphHighlighted:iphHighlighted];
+  [self updateTabCountLabelTextColor];
+}
+
 #pragma mark - UIAccessibility
 
 - (NSString*)accessibilityIdentifier {
diff --git a/ios/chrome/browser/toolbar/ui_bundled/primary_toolbar_coordinator.mm b/ios/chrome/browser/toolbar/ui_bundled/primary_toolbar_coordinator.mm
index c6dcb44..503eaf01 100644
--- a/ios/chrome/browser/toolbar/ui_bundled/primary_toolbar_coordinator.mm
+++ b/ios/chrome/browser/toolbar/ui_bundled/primary_toolbar_coordinator.mm
@@ -19,6 +19,7 @@
 #import "ios/chrome/browser/shared/model/browser/browser.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/guided_tour_commands.h"
 #import "ios/chrome/browser/shared/public/commands/omnibox_commands.h"
 #import "ios/chrome/browser/shared/public/commands/popup_menu_commands.h"
 #import "ios/chrome/browser/shared/public/commands/settings_commands.h"
@@ -151,6 +152,20 @@
       agentFromApp:self.browser->GetSceneState().profileState.appState];
 }
 
+#pragma mark - GuidedTourCommands
+
+- (void)highlightViewInStep:(GuidedTourStep)step {
+  if (!IsSplitToolbarMode(self.viewController) && step == GuidedTourStepNTP) {
+    [self.viewController IPHHighlightTabGridButton:YES];
+  }
+}
+
+- (void)stepCompleted:(GuidedTourStep)step {
+  if (!IsSplitToolbarMode(self.viewController) && step == GuidedTourStepNTP) {
+    [self.viewController IPHHighlightTabGridButton:NO];
+  }
+}
+
 #pragma mark - ToolbarCommands
 
 - (void)triggerToolbarSlideInAnimation {
diff --git a/ios/chrome/browser/toolbar/ui_bundled/secondary_toolbar_coordinator.mm b/ios/chrome/browser/toolbar/ui_bundled/secondary_toolbar_coordinator.mm
index 66060be..1800b22 100644
--- a/ios/chrome/browser/toolbar/ui_bundled/secondary_toolbar_coordinator.mm
+++ b/ios/chrome/browser/toolbar/ui_bundled/secondary_toolbar_coordinator.mm
@@ -10,9 +10,11 @@
 #import "ios/chrome/browser/shared/model/browser/browser.h"
 #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
+#import "ios/chrome/browser/shared/public/commands/guided_tour_commands.h"
 #import "ios/chrome/browser/shared/public/commands/omnibox_commands.h"
 #import "ios/chrome/browser/shared/public/commands/popup_menu_commands.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
+#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/toolbar/ui_bundled/adaptive_toolbar_coordinator+subclassing.h"
 #import "ios/chrome/browser/toolbar/ui_bundled/buttons/toolbar_button_factory.h"
 #import "ios/chrome/browser/toolbar/ui_bundled/buttons/toolbar_configuration.h"
@@ -62,6 +64,19 @@
   [super stop];
 }
 
+#pragma mark - GuidedTourCommands
+
+- (void)highlightViewInStep:(GuidedTourStep)step {
+  if (IsSplitToolbarMode(self.viewController) && step == GuidedTourStepNTP) {
+    [self.viewController IPHHighlightTabGridButton:YES];
+  }
+}
+- (void)stepCompleted:(GuidedTourStep)step {
+  if (IsSplitToolbarMode(self.viewController) && step == GuidedTourStepNTP) {
+    [self.viewController IPHHighlightTabGridButton:NO];
+  }
+}
+
 #pragma mark - ToolbarCommands
 
 - (void)triggerToolbarSlideInAnimation {
diff --git a/ios/chrome/browser/toolbar/ui_bundled/toolbar_coordinator.mm b/ios/chrome/browser/toolbar/ui_bundled/toolbar_coordinator.mm
index 1832b39a..0af6234 100644
--- a/ios/chrome/browser/toolbar/ui_bundled/toolbar_coordinator.mm
+++ b/ios/chrome/browser/toolbar/ui_bundled/toolbar_coordinator.mm
@@ -22,6 +22,7 @@
 #import "ios/chrome/browser/shared/public/commands/application_commands.h"
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
 #import "ios/chrome/browser/shared/public/commands/find_in_page_commands.h"
+#import "ios/chrome/browser/shared/public/commands/guided_tour_commands.h"
 #import "ios/chrome/browser/shared/public/commands/help_commands.h"
 #import "ios/chrome/browser/shared/public/commands/popup_menu_commands.h"
 #import "ios/chrome/browser/shared/public/commands/text_zoom_commands.h"
@@ -41,7 +42,8 @@
 #import "ios/chrome/common/ui/util/ui_util.h"
 #import "ios/components/webui/web_ui_url_constants.h"
 
-@interface ToolbarCoordinator () <PrimaryToolbarViewControllerDelegate,
+@interface ToolbarCoordinator () <GuidedTourCommands,
+                                  PrimaryToolbarViewControllerDelegate,
                                   ToolbarCommands,
                                   ToolbarMediatorDelegate> {
   raw_ptr<PrerenderService> _prerenderService;
@@ -121,6 +123,12 @@
       startDispatchingToTarget:self
                    forProtocol:@protocol(FakeboxFocuser)];
 
+  if (IsBestOfAppGuidedTourEnabled()) {
+    [self.browser->GetCommandDispatcher()
+        startDispatchingToTarget:self
+                     forProtocol:@protocol(GuidedTourCommands)];
+  }
+
   segmentation_platform::DeviceSwitcherResultDispatcher* deviceSwitcherResult =
       nullptr;
   if (!browser->GetProfile()->IsOffTheRecord()) {
@@ -540,6 +548,20 @@
   [self.locationBarCoordinator.locationBarViewController.view setHidden:NO];
 }
 
+#pragma mark - GuidedTourCommands
+
+- (void)highlightViewInStep:(GuidedTourStep)step {
+  for (id<GuidedTourCommands> coordinator in self.coordinators) {
+    [coordinator highlightViewInStep:step];
+  }
+}
+
+- (void)stepCompleted:(GuidedTourStep)step {
+  for (id<GuidedTourCommands> coordinator in self.coordinators) {
+    [coordinator stepCompleted:step];
+  }
+}
+
 #pragma mark - ToolbarCommands
 
 - (void)triggerToolbarSlideInAnimation {
diff --git a/ios/chrome/browser/url_loading/model/url_loading_browser_agent.mm b/ios/chrome/browser/url_loading/model/url_loading_browser_agent.mm
index dffc4d4..811b5d88 100644
--- a/ios/chrome/browser/url_loading/model/url_loading_browser_agent.mm
+++ b/ios/chrome/browser/url_loading/model/url_loading_browser_agent.mm
@@ -125,7 +125,8 @@
 }  // namespace
 
 UrlLoadingBrowserAgent::UrlLoadingBrowserAgent(Browser* browser)
-    : browser_(browser),
+    : BrowserUserData(browser),
+      browser_(browser),
       notifier_(UrlLoadingNotifierBrowserAgent::FromBrowser(browser_)) {
   DCHECK(notifier_);
 }
diff --git a/ios/chrome/browser/url_loading/model/url_loading_notifier_browser_agent.mm b/ios/chrome/browser/url_loading/model/url_loading_notifier_browser_agent.mm
index 8fcdbee..5a10f50 100644
--- a/ios/chrome/browser/url_loading/model/url_loading_notifier_browser_agent.mm
+++ b/ios/chrome/browser/url_loading/model/url_loading_notifier_browser_agent.mm
@@ -6,8 +6,8 @@
 
 #import "ios/chrome/browser/url_loading/model/url_loading_observer.h"
 
-UrlLoadingNotifierBrowserAgent::UrlLoadingNotifierBrowserAgent(
-    Browser* browser) {}
+UrlLoadingNotifierBrowserAgent::UrlLoadingNotifierBrowserAgent(Browser* browser)
+    : BrowserUserData(browser) {}
 
 UrlLoadingNotifierBrowserAgent::~UrlLoadingNotifierBrowserAgent() {}
 
diff --git a/ios/chrome/browser/view_source/model/view_source_browser_agent.mm b/ios/chrome/browser/view_source/model/view_source_browser_agent.mm
index 733e9422..a089ee93 100644
--- a/ios/chrome/browser/view_source/model/view_source_browser_agent.mm
+++ b/ios/chrome/browser/view_source/model/view_source_browser_agent.mm
@@ -14,7 +14,7 @@
 #import "ios/web/public/web_state.h"
 
 ViewSourceBrowserAgent::ViewSourceBrowserAgent(Browser* browser)
-    : browser_(browser) {}
+    : BrowserUserData(browser), browser_(browser) {}
 
 ViewSourceBrowserAgent::~ViewSourceBrowserAgent() {}
 
diff --git a/ios/chrome/browser/web/model/page_placeholder_browser_agent.mm b/ios/chrome/browser/web/model/page_placeholder_browser_agent.mm
index 093011cf..ca594719eb 100644
--- a/ios/chrome/browser/web/model/page_placeholder_browser_agent.mm
+++ b/ios/chrome/browser/web/model/page_placeholder_browser_agent.mm
@@ -15,7 +15,7 @@
 #import "ios/web/public/web_state.h"
 
 PagePlaceholderBrowserAgent::PagePlaceholderBrowserAgent(Browser* browser)
-    : browser_(browser) {
+    : BrowserUserData(browser), browser_(browser) {
   // All the BrowserAgent are attached to the Browser during the creation,
   // the WebStateList must be empty at this point.
   DCHECK(browser_->GetWebStateList()->empty())
diff --git a/ios/chrome/browser/web/model/web_navigation_browser_agent.mm b/ios/chrome/browser/web/model/web_navigation_browser_agent.mm
index 2d8c0fd..da8a0d4 100644
--- a/ios/chrome/browser/web/model/web_navigation_browser_agent.mm
+++ b/ios/chrome/browser/web/model/web_navigation_browser_agent.mm
@@ -22,7 +22,9 @@
 #import "ios/web/public/web_state.h"
 
 WebNavigationBrowserAgent::WebNavigationBrowserAgent(Browser* browser)
-    : web_state_list_(browser->GetWebStateList()), browser_(browser) {}
+    : BrowserUserData(browser),
+      web_state_list_(browser->GetWebStateList()),
+      browser_(browser) {}
 
 WebNavigationBrowserAgent::~WebNavigationBrowserAgent() {}
 
diff --git a/ios/chrome/browser/web/model/web_state_delegate_browser_agent.mm b/ios/chrome/browser/web/model/web_state_delegate_browser_agent.mm
index dd2d344..e370b2f 100644
--- a/ios/chrome/browser/web/model/web_state_delegate_browser_agent.mm
+++ b/ios/chrome/browser/web/model/web_state_delegate_browser_agent.mm
@@ -95,7 +95,8 @@
 WebStateDelegateBrowserAgent::WebStateDelegateBrowserAgent(
     Browser* browser,
     TabInsertionBrowserAgent* tab_insertion_agent)
-    : web_state_list_(browser->GetWebStateList()),
+    : BrowserUserData(browser),
+      web_state_list_(browser->GetWebStateList()),
       tab_insertion_agent_(tab_insertion_agent) {
   DCHECK(tab_insertion_agent_);
   browser_ = browser;
diff --git a/ios/chrome/browser/web/model/web_state_update_browser_agent.mm b/ios/chrome/browser/web/model/web_state_update_browser_agent.mm
index 1aa1a719..2896e4fd 100644
--- a/ios/chrome/browser/web/model/web_state_update_browser_agent.mm
+++ b/ios/chrome/browser/web/model/web_state_update_browser_agent.mm
@@ -8,7 +8,7 @@
 #import "ios/web/public/ui/crw_web_view_scroll_view_proxy.h"
 
 WebStateUpdateBrowserAgent::WebStateUpdateBrowserAgent(Browser* browser)
-    : web_state_list_(browser->GetWebStateList()) {
+    : BrowserUserData(browser), web_state_list_(browser->GetWebStateList()) {
   web_state_list_observation_.Observe(web_state_list_.get());
 
   // All the BrowserAgent are attached to the Browser during the creation,
diff --git a/ios/chrome/browser/web_state_list/model/web_usage_enabler/web_usage_enabler_browser_agent.mm b/ios/chrome/browser/web_state_list/model/web_usage_enabler/web_usage_enabler_browser_agent.mm
index c25726e..d9044403 100644
--- a/ios/chrome/browser/web_state_list/model/web_usage_enabler/web_usage_enabler_browser_agent.mm
+++ b/ios/chrome/browser/web_state_list/model/web_usage_enabler/web_usage_enabler_browser_agent.mm
@@ -8,7 +8,7 @@
 #import "ios/web/public/navigation/navigation_manager.h"
 
 WebUsageEnablerBrowserAgent::WebUsageEnablerBrowserAgent(Browser* browser)
-    : browser_(browser) {
+    : BrowserUserData(browser), browser_(browser) {
   browser_observation_.Observe(browser_.get());
 
   WebStateList* web_state_list = browser_->GetWebStateList();
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index 8aeb527..67a6f134 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -207,12 +207,12 @@
     "//ios/chrome/browser/app_launcher/model:unit_tests",
     "//ios/chrome/browser/app_store_rating/ui_bundled:unit_tests",
     "//ios/chrome/browser/authentication/ui_bundled:unit_tests",
+    "//ios/chrome/browser/authentication/ui_bundled/account_menu:unit_tests",
     "//ios/chrome/browser/authentication/ui_bundled/authentication_flow:unit_tests",
     "//ios/chrome/browser/authentication/ui_bundled/cells:unit_tests",
     "//ios/chrome/browser/authentication/ui_bundled/history_sync:unit_tests",
     "//ios/chrome/browser/authentication/ui_bundled/identity_chooser:unit_tests",
     "//ios/chrome/browser/authentication/ui_bundled/signin:unit_tests",
-    "//ios/chrome/browser/authentication/ui_bundled/signin/account_menu:unit_tests",
     "//ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin:unit_tests",
     "//ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin:unit_tests",
     "//ios/chrome/browser/authentication/ui_bundled/signin/promo:unit_tests",
diff --git a/ios/chrome/test/earl_grey2/BUILD.gn b/ios/chrome/test/earl_grey2/BUILD.gn
index 889d09e..eecef36 100644
--- a/ios/chrome/test/earl_grey2/BUILD.gn
+++ b/ios/chrome/test/earl_grey2/BUILD.gn
@@ -170,8 +170,8 @@
 
   deps = [
     "//ios/chrome/browser/authentication/ui_bundled:eg2_tests",
+    "//ios/chrome/browser/authentication/ui_bundled/account_menu:eg2_tests",
     "//ios/chrome/browser/authentication/ui_bundled/signin:eg2_tests",
-    "//ios/chrome/browser/authentication/ui_bundled/signin/account_menu:eg2_tests",
     "//ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin:eg2_tests",
     "//ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/test:eg2_tests",
     "//ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin:eg2_tests",
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index 08db82b..1a3c640b 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -628,10 +628,7 @@
 
   sources = [ "js_messaging/resources/message.ts" ]
 
-  deps = [
-    "//ios/web/public/js_messaging:frame_id",
-    "//ios/web/public/js_messaging:gcrweb",
-  ]
+  deps = [ "//ios/web/public/js_messaging:gcrweb" ]
 }
 
 bundle_data("navigation_resources") {
diff --git a/ios/web/js_messaging/BUILD.gn b/ios/web/js_messaging/BUILD.gn
index d3bb70f..34c9f0be 100644
--- a/ios/web/js_messaging/BUILD.gn
+++ b/ios/web/js_messaging/BUILD.gn
@@ -161,7 +161,7 @@
 
   sources = [ "resources/setup_frame.ts" ]
 
-  deps = [ "//ios/web/public/js_messaging:frame_id" ]
+  deps = [ "//ios/web/public/js_messaging:gcrweb" ]
 }
 
 optimize_ts("frame_listeners_js") {
diff --git a/ios/web/js_messaging/resources/message.ts b/ios/web/js_messaging/resources/message.ts
index a08ebab..4a18dc3 100644
--- a/ios/web/js_messaging/resources/message.ts
+++ b/ios/web/js_messaging/resources/message.ts
@@ -7,8 +7,7 @@
  * the native code.
  */
 
-import {registerFrame} from '//ios/web/public/js_messaging/resources/frame_id.js';
-import {gCrWebLegacy} from '//ios/web/public/js_messaging/resources/gcrweb.js';
+import {gCrWeb, gCrWebLegacy} from '//ios/web/public/js_messaging/resources/gcrweb.js';
 
 /**
  * Registers this frame with the native code and forwards the message to any
@@ -22,7 +21,7 @@
  *                         function with a pageshow event listener.
  */
 function getExistingFrames() {
-  registerFrame();
+  gCrWeb.registerFrame();
 
   const framecount = window.frames.length;
   for (let i = 0; i < framecount; i++) {
diff --git a/ios/web/js_messaging/resources/setup_frame.ts b/ios/web/js_messaging/resources/setup_frame.ts
index 6d573d8..64868f9c 100644
--- a/ios/web/js_messaging/resources/setup_frame.ts
+++ b/ios/web/js_messaging/resources/setup_frame.ts
@@ -7,11 +7,11 @@
  * must be included at document end time.
  */
 
-import {registerFrame} from '//ios/web/public/js_messaging/resources/frame_id.js';
+import {gCrWeb} from '//ios/web/public/js_messaging/resources/gcrweb.js';
 
 // Requires message.js to already have been injected.
 
 // Frame registration must be delayed until Document End script injection time.
 // (This file is injected at that time, but the message API is defined at
 // Document Start time.)
-registerFrame();
+gCrWeb.registerFrame();
diff --git a/ios/web/public/js_messaging/BUILD.gn b/ios/web/public/js_messaging/BUILD.gn
index ff5016419..faa559c 100644
--- a/ios/web/public/js_messaging/BUILD.gn
+++ b/ios/web/public/js_messaging/BUILD.gn
@@ -37,15 +37,6 @@
   sources = [ "content_world.h" ]
 }
 
-compile_ts("frame_id") {
-  sources = [ "resources/frame_id.ts" ]
-
-  deps = [
-    ":gcrweb",
-    ":util_scripts",
-  ]
-}
-
 compile_ts("util_scripts") {
   sources = [ "resources/utils.ts" ]
 }
diff --git a/ios/web/public/js_messaging/resources/frame_id.ts b/ios/web/public/js_messaging/resources/frame_id.ts
deleted file mode 100644
index bf254850..0000000
--- a/ios/web/public/js_messaging/resources/frame_id.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {gCrWeb} from '//ios/web/public/js_messaging/resources/gcrweb.js';
-import {sendWebKitMessage} from '//ios/web/public/js_messaging/resources/utils.js';
-
-/**
- * Registers this frame by sending its frameId to the native application.
- */
-export function registerFrame() {
-  sendWebKitMessage(
-    'FrameBecameAvailable', {'crwFrameId': gCrWeb.getFrameId()});
-}
diff --git a/ios/web/public/js_messaging/resources/gcrweb.ts b/ios/web/public/js_messaging/resources/gcrweb.ts
index 4a6439a..ce27afc 100644
--- a/ios/web/public/js_messaging/resources/gcrweb.ts
+++ b/ios/web/public/js_messaging/resources/gcrweb.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {generateRandomId} from '//ios/web/public/js_messaging/resources/utils.js';
+import {generateRandomId, sendWebKitMessage} from '//ios/web/public/js_messaging/resources/utils.js';
 
 /**
  * @fileoverview This file exports `gCrWeb` API to be used by other
@@ -43,6 +43,14 @@
     }
     return this.frameId;
   }
+
+  /**
+   * Registers and frame by sending its frameId to the native application.
+   */
+  registerFrame() {
+    sendWebKitMessage(
+      'FrameBecameAvailable', {'crwFrameId': this.getFrameId()});
+  }
 }
 
 export class CrWebApi {
diff --git a/ios_internal b/ios_internal
index c32f192..6753a19 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit c32f19290092f9592d3be32943becfab3f52a9cd
+Subproject commit 6753a19d2e211ea27988a2603073395d0469a202
diff --git a/media/renderers/video_resource_updater.cc b/media/renderers/video_resource_updater.cc
index 9e8ed53..bb62b7b7 100644
--- a/media/renderers/video_resource_updater.cc
+++ b/media/renderers/video_resource_updater.cc
@@ -180,17 +180,6 @@
 #endif
 }
 
-viz::SharedImageFormat GetSingleChannel8BitFormat(
-    const gpu::Capabilities& caps,
-    const gpu::SharedImageCapabilities& shared_image_caps) {
-  if (caps.texture_rg && !shared_image_caps.disable_r8_shared_images) {
-    return viz::SinglePlaneFormat::kR_8;
-  }
-
-  DCHECK(shared_image_caps.supports_luminance_shared_images);
-  return viz::SinglePlaneFormat::kLUMINANCE_8;
-}
-
 // Returns true if the input VideoFrame format can be stored directly in the
 // provided output shared image format.
 bool HasCompatibleRGBFormat(VideoPixelFormat input_format,
@@ -214,20 +203,24 @@
          frame_format == PIXEL_FORMAT_ABGR || frame_format == PIXEL_FORMAT_ARGB;
 }
 
-bool IsFormat16BitFloat(viz::SharedImageFormat format) {
-  // Assume multiplanar SharedImageFormat with ChannelFormat::k16F is always
-  // used as LUMINANCEF16.
-  CHECK(format.is_multi_plane());
-  return format.channel_format() == viz::SharedImageFormat::ChannelFormat::k16F;
-}
-
 viz::SharedImageFormat::ChannelFormat SupportedMultiPlaneChannelFormat(
-    viz::SharedImageFormat format) {
-  if (format == viz::SinglePlaneFormat::kR_16) {
+    const gpu::Capabilities& caps,
+    const gpu::SharedImageCapabilities& shared_image_caps,
+    int bits_per_channel) {
+  if (bits_per_channel <= 8) {
+    // Must support texture_rg or 8-bits luminance.
+    DCHECK(shared_image_caps.supports_luminance_shared_images ||
+           caps.texture_rg);
+    return viz::SharedImageFormat::ChannelFormat::k8;
+  }
+  // Can support R_16 formats.
+  if (caps.texture_norm16 && shared_image_caps.supports_r16_shared_images) {
     return viz::SharedImageFormat::ChannelFormat::k16;
   }
-  if (format == viz::SinglePlaneFormat::kLUMINANCE_F16 ||
-      format == viz::SinglePlaneFormat::kR_F16) {
+  // Can support R_F16 or LUMINANCE_F16 formats.
+  if (shared_image_caps.is_r16f_supported ||
+      (caps.texture_half_float_linear &&
+       shared_image_caps.supports_luminance_shared_images)) {
     return viz::SharedImageFormat::ChannelFormat::k16F;
   }
   return viz::SharedImageFormat::ChannelFormat::k8;
@@ -857,30 +850,6 @@
   return external_resource;
 }
 
-viz::SharedImageFormat VideoResourceUpdater::YuvSharedImageFormat(
-    int bits_per_channel) {
-  DCHECK(context_provider_);
-  const auto& caps = context_provider_->ContextCapabilities();
-  const auto& shared_image_caps =
-      context_provider_->SharedImageInterface()->GetCapabilities();
-  if (bits_per_channel <= 8) {
-    DCHECK(shared_image_caps.supports_luminance_shared_images ||
-           caps.texture_rg);
-    return GetSingleChannel8BitFormat(caps, shared_image_caps);
-  }
-  if (caps.texture_norm16 && shared_image_caps.supports_r16_shared_images) {
-    return viz::SinglePlaneFormat::kR_16;
-  }
-  if (shared_image_caps.is_r16f_supported) {
-    return viz::SinglePlaneFormat::kR_F16;
-  }
-  if (caps.texture_half_float_linear &&
-      shared_image_caps.supports_luminance_shared_images) {
-    return viz::SinglePlaneFormat::kLUMINANCE_F16;
-  }
-  return GetSingleChannel8BitFormat(caps, shared_image_caps);
-}
-
 viz::SharedImageFormat VideoResourceUpdater::GetSoftwareOutputFormat(
     VideoPixelFormat input_frame_format,
     int bits_per_channel,
@@ -906,6 +875,8 @@
     return PaintCanvasVideoRenderer::GetRGBPixelsOutputFormat();
   }
 
+  const auto& shared_image_caps =
+      context_provider_->SharedImageInterface()->GetCapabilities();
   // Get the multiplanar shared image format for `input_frame_format`.
   auto yuv_si_format =
       VideoPixelFormatToMultiPlanarSharedImageFormat(input_frame_format);
@@ -914,8 +885,6 @@
     // Only 8-bit formats are supported with UV planes for software decoding.
     CHECK_EQ(yuv_si_format.channel_format(),
              viz::SharedImageFormat::ChannelFormat::k8);
-    const auto& shared_image_caps =
-        context_provider_->SharedImageInterface()->GetCapabilities();
     // Two channel formats are supported only with texture_rg.
     if (!caps.texture_rg || shared_image_caps.disable_r8_shared_images) {
       texture_needs_rgb_conversion_out = true;
@@ -924,8 +893,8 @@
   }
 
   // Get the supported channel format for `yuv_si_format`'s first plane.
-  auto channel_format =
-      SupportedMultiPlaneChannelFormat(YuvSharedImageFormat(bits_per_channel));
+  auto channel_format = SupportedMultiPlaneChannelFormat(
+      caps, shared_image_caps, bits_per_channel);
   if (yuv_si_format.channel_format() != channel_format) {
     // If the requested channel format is not supported, use the supported
     // channel format and downsample later if needed.
@@ -1076,12 +1045,14 @@
     const bool needs_bit_upshifting =
         bits_per_channel > 8 && bits_per_channel < resource_bit_depth;
 
+    const bool is_16bit_float = yuv_si_format.channel_format() ==
+                                viz::SharedImageFormat::ChannelFormat::k16F;
+
     // We need to convert the incoming data if we're transferring to half
     // float, if there is need for bit downshift or if the strides need to
     // be reconciled.
-    const bool needs_conversion = IsFormat16BitFloat(yuv_si_format) ||
-                                  needs_bit_downshifting ||
-                                  needs_bit_upshifting;
+    const bool needs_conversion =
+        is_16bit_float || needs_bit_downshifting || needs_bit_upshifting;
     const uint8_t* pixels;
     int pixels_stride_in_bytes;
     if (!needs_conversion) {
@@ -1098,7 +1069,7 @@
         }
       }
 
-      if (IsFormat16BitFloat(yuv_si_format)) {
+      if (is_16bit_float) {
         int max_value = 1 << bits_per_channel;
         // Use 1.0/max_value to be consistent with multiplanar shared images
         // which create TextureDrawQuads and don't take in a multiplier, offset.
diff --git a/net/cert/ev_root_ca_metadata_unittest.cc b/net/cert/ev_root_ca_metadata_unittest.cc
index 9d15aeb4..faa1f90 100644
--- a/net/cert/ev_root_ca_metadata_unittest.cc
+++ b/net/cert/ev_root_ca_metadata_unittest.cc
@@ -25,6 +25,12 @@
     {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa,
      0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
      0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}};
+// This root must be in the Chrome Root Store. This must be kept in sync with
+// the test in google3, and with the contents of the Chrome Root Store.
+//
+// Failure to update this test first before removing the below root from the
+// Chrome Root Store wil break the sync of the Chrome Root Store between google3
+// and the chromium repository.
 const SHA256HashValue kAmazonFingerprint = {
     {0x1b, 0xa5, 0xb2, 0xaa, 0x8c, 0x65, 0x40, 0x1a, 0x82, 0x96, 0x01,
      0x18, 0xf8, 0x0b, 0xec, 0x4f, 0x62, 0x30, 0x4d, 0x83, 0xce, 0xc4,
diff --git a/net/first_party_sets/first_party_set_entry.cc b/net/first_party_sets/first_party_set_entry.cc
index 8066e31..213b369 100644
--- a/net/first_party_sets/first_party_set_entry.cc
+++ b/net/first_party_sets/first_party_set_entry.cc
@@ -41,13 +41,11 @@
     SchemefulSite primary,
     SiteType site_type,
     std::optional<FirstPartySetEntry::SiteIndex> site_index)
-    : primary_(std::move(primary)),
-      site_type_(site_type),
-      site_index_(site_index) {
+    : primary_(std::move(primary)), site_type_(site_type) {
   switch (site_type_) {
     case SiteType::kPrimary:
     case SiteType::kService:
-      CHECK(!site_index_.has_value());
+      CHECK(!site_index.has_value());
       break;
     case SiteType::kAssociated:
       break;
@@ -105,13 +103,7 @@
 
 std::ostream& operator<<(std::ostream& os, const FirstPartySetEntry& entry) {
   os << "{" << entry.primary() << ", " << static_cast<int>(entry.site_type())
-     << ", ";
-  if (entry.site_index().has_value()) {
-    os << entry.site_index().value();
-  } else {
-    os << "{}";
-  }
-  os << "}";
+     << "}";
   return os;
 }
 
diff --git a/net/first_party_sets/first_party_set_entry.h b/net/first_party_sets/first_party_set_entry.h
index c626a40..17eee21 100644
--- a/net/first_party_sets/first_party_set_entry.h
+++ b/net/first_party_sets/first_party_set_entry.h
@@ -71,17 +71,11 @@
 
   SiteType site_type() const { return site_type_; }
 
-  const std::optional<SiteIndex>& site_index() const { return site_index_; }
-
  private:
   // The primary site associated with this site's set.
   SchemefulSite primary_;
   // The type associated with this site.
   SiteType site_type_;
-  // The index of this site in the set declaration, if a meaningful index
-  // exists. Primary sites do not have indices, nor do sites that were defined
-  // or affected by an enterprise policy set.
-  std::optional<SiteIndex> site_index_;
 };
 
 NET_EXPORT std::ostream& operator<<(
diff --git a/net/http/transport_security_state_static.pins b/net/http/transport_security_state_static.pins
index 4deb6d46..9b72e13 100644
--- a/net/http/transport_security_state_static.pins
+++ b/net/http/transport_security_state_static.pins
@@ -43,9 +43,9 @@
 #   hash function for preloaded entries again (we have already done so once).
 #
 
-# Last updated: 2025-05-06 12:54 UTC
+# Last updated: 2025-05-07 12:54 UTC
 PinsListTimestamp
-1746536071
+1746622449
 
 TestSPKI
 sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
diff --git a/net/http/transport_security_state_static_pins.json b/net/http/transport_security_state_static_pins.json
index a854f4c..61df73a 100644
--- a/net/http/transport_security_state_static_pins.json
+++ b/net/http/transport_security_state_static_pins.json
@@ -31,7 +31,7 @@
 // the 'static_spki_hashes' and 'bad_static_spki_hashes' fields in 'pinsets'
 // refer to, and the timestamp at which the pins list was last updated.
 //
-// Last updated: 2025-05-06 12:54 UTC
+// Last updated: 2025-05-07 12:54 UTC
 //
 {
   "pinsets": [
diff --git a/services/audio/output_controller.cc b/services/audio/output_controller.cc
index 633485d2..8133b3b 100644
--- a/services/audio/output_controller.cc
+++ b/services/audio/output_controller.cc
@@ -13,6 +13,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/containers/contains.h"
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/logging.h"
@@ -31,6 +32,13 @@
 
 namespace {
 
+// Requests data before reading in OutputController::OnMoreData, which may
+// reduce audio output latency but may increase the probability of audio
+// glitches.
+BASE_FEATURE(kAudioOutputControllerRequestBeforeRead,
+             "AudioOutputControllerRequestBeforeRead",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Time in seconds between two successive measurements of audio power levels.
 constexpr base::TimeDelta kPowerMonitorLogInterval = base::Seconds(15);
 
@@ -135,7 +143,9 @@
       state_(kEmpty),
       sync_reader_(sync_reader),
       power_monitor_(params.sample_rate(),
-                     base::Milliseconds(kPowerMeasurementTimeConstantMillis)) {
+                     base::Milliseconds(kPowerMeasurementTimeConstantMillis)),
+      request_before_read_(base::FeatureList::IsEnabled(
+          kAudioOutputControllerRequestBeforeRead)) {
   DCHECK(audio_manager);
   DCHECK(handler_);
   DCHECK(sync_reader_);
@@ -285,8 +295,10 @@
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(state_ == kCreated || state_ == kPaused);
 
-  // Ask for first packet.
-  sync_reader_->RequestMoreData(base::TimeDelta(), base::TimeTicks(), {});
+  if (!request_before_read_) {
+    // Ask for first packet.
+    sync_reader_->RequestMoreData(base::TimeDelta(), base::TimeTicks(), {});
+  }
 
   state_ = kPlaying;
   SendLogMessage("%s => (state=%s)", __func__, StateToString(state_));
@@ -410,6 +422,10 @@
 
   stats_tracker_->OnMoreDataCalled();
 
+  if (request_before_read_) {
+    sync_reader_->RequestMoreData(delay, delay_timestamp, glitch_info);
+  }
+
   const bool received_data = sync_reader_->Read(dest, is_mixing);
 
   const base::TimeTicks reference_time = delay_timestamp + delay;
@@ -428,10 +444,12 @@
 
   const int frames =
       dest->is_bitstream_format() ? dest->GetBitstreamFrames() : dest->frames();
-  delay +=
-      media::AudioTimestampHelper::FramesToTime(frames, params_.sample_rate());
+  if (!request_before_read_) {
+    delay += media::AudioTimestampHelper::FramesToTime(frames,
+                                                       params_.sample_rate());
 
-  sync_reader_->RequestMoreData(delay, delay_timestamp, glitch_info);
+    sync_reader_->RequestMoreData(delay, delay_timestamp, glitch_info);
+  }
 
 #if !BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
   constexpr bool is_bitstream = false;
diff --git a/services/audio/output_controller.h b/services/audio/output_controller.h
index d0c81203..7dc9b27 100644
--- a/services/audio/output_controller.h
+++ b/services/audio/output_controller.h
@@ -354,6 +354,9 @@
   // and destroyed when a stream stops. Also reset every time there is a stream
   // being created due to device changes.
   std::optional<ErrorStatisticsTracker> stats_tracker_;
+
+  // Request and read data in the same OnMoreData call, to reduce latency.
+  const bool request_before_read_;
 };
 
 }  // namespace audio
diff --git a/services/network/chunked_data_pipe_upload_data_stream_unittest.cc b/services/network/chunked_data_pipe_upload_data_stream_unittest.cc
index 10cd487a..981e9f8 100644
--- a/services/network/chunked_data_pipe_upload_data_stream_unittest.cc
+++ b/services/network/chunked_data_pipe_upload_data_stream_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/390223051): Remove C-library calls to fix the errors.
-#pragma allow_unsafe_libc_calls
-#endif
-
 #include "services/network/chunked_data_pipe_upload_data_stream.h"
 
 #include <stdint.h>
diff --git a/services/network/orb/orb_impl_unittest.cc b/services/network/orb/orb_impl_unittest.cc
index 44df397c..fc94db6e 100644
--- a/services/network/orb/orb_impl_unittest.cc
+++ b/services/network/orb/orb_impl_unittest.cc
@@ -4,11 +4,6 @@
 
 #include "base/strings/to_string.h"
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/390223051): Remove C-library calls to fix the errors.
-#pragma allow_unsafe_libc_calls
-#endif
-
 // This file contains the ResponseAnalyzerTests (which test the response
 // analyzer's behavior in several parameterized test scenarios) and at the end
 // includes the CrossOriginReadBlockingTests, which are more typical unittests.
diff --git a/services/network/p2p/socket_manager.cc b/services/network/p2p/socket_manager.cc
index ddb506f..9ce493e 100644
--- a/services/network/p2p/socket_manager.cc
+++ b/services/network/p2p/socket_manager.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "services/network/p2p/socket_manager.h"
 
 #include <stddef.h>
@@ -270,8 +265,8 @@
     NOTREACHED();
   }
 
-  std::vector<uint8_t> header_buffer(rtp_packet.data(),
-                                     rtp_packet.data() + header_size);
+  std::vector<uint8_t> header_buffer(rtp_packet.begin(),
+                                     rtp_packet.begin() + header_size);
   trusted_socket_manager_client_->DumpPacket(header_buffer, rtp_packet.size(),
                                              incoming);
 }
diff --git a/services/network/p2p/socket_tcp.cc b/services/network/p2p/socket_tcp.cc
index bed6c3c8..ba68bdcf 100644
--- a/services/network/p2p/socket_tcp.cc
+++ b/services/network/p2p/socket_tcp.cc
@@ -2,16 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "services/network/p2p/socket_tcp.h"
 
 #include <stddef.h>
 
 #include <utility>
+#include <vector>
 
 #include "base/containers/span.h"
 #include "base/containers/span_writer.h"
@@ -466,10 +462,11 @@
     CHECK_EQ(writer.remaining(), 0u);
   }
 
-  webrtc::ApplyPacketOptions(
-      send_buffer.buffer->bytes() + kPacketHeaderSize,
-      send_buffer.buffer->BytesRemaining() - kPacketHeaderSize,
-      options.packet_time_params, webrtc::TimeMicros());
+  base::span<uint8_t> send_buffer_without_header =
+      send_buffer.buffer->span().subspan(kPacketHeaderSize);
+  webrtc::ApplyPacketOptions(send_buffer_without_header.data(),
+                             send_buffer_without_header.size(),
+                             options.packet_time_params, webrtc::TimeMicros());
 
   WriteOrQueue(send_buffer);
 }
@@ -527,28 +524,25 @@
 
   // Add any pad bytes to the total size.
   int buffer_size = data.size() + pad_bytes;
+  std::vector<uint8_t> buffer;
+  buffer.reserve(buffer_size);
+  buffer.assign(data.begin(), data.end());
+  if (pad_bytes) {
+    DCHECK_LE(pad_bytes, 4);
+    buffer.insert(buffer.end(), pad_bytes, 0);
+  }
 
   SendBuffer send_buffer(
       options.packet_id,
       base::MakeRefCounted<net::DrainableIOBuffer>(
-          base::MakeRefCounted<net::IOBufferWithSize>(buffer_size),
+          base::MakeRefCounted<net::VectorIOBuffer>(std::move(buffer)),
           buffer_size));
-  memcpy(send_buffer.buffer->data(), data.data(), data.size());
 
   webrtc::ApplyPacketOptions(send_buffer.buffer->bytes(), data.size(),
                              options.packet_time_params, webrtc::TimeMicros());
 
-  if (pad_bytes) {
-    char padding[4] = {};
-    DCHECK_LE(pad_bytes, 4);
-    memcpy(send_buffer.buffer->data() + data.size(), padding, pad_bytes);
-  }
-
   // WriteOrQueue may free the memory, so dump it first.
-  delegate_->DumpPacket(
-      base::span(reinterpret_cast<const uint8_t*>(send_buffer.buffer->data()),
-                 data.size()),
-      false);
+  delegate_->DumpPacket(send_buffer.buffer->span(), false);
 
   WriteOrQueue(send_buffer);
 }
diff --git a/services/network/p2p/socket_udp.cc b/services/network/p2p/socket_udp.cc
index 670cb5d..d28d89ce 100644
--- a/services/network/p2p/socket_udp.cc
+++ b/services/network/p2p/socket_udp.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "services/network/p2p/socket_udp.h"
 
 #include <tuple>
@@ -115,12 +110,10 @@
     const webrtc::AsyncSocketPacketOptions& options,
     uint64_t id)
     : to(to),
-      data(base::MakeRefCounted<net::IOBufferWithSize>(content.size())),
+      data(base::MakeRefCounted<net::VectorIOBuffer>(content)),
       size(content.size()),
       packet_options(options),
-      id(id) {
-  memcpy(data->data(), content.data(), content.size());
-}
+      id(id) {}
 
 P2PPendingPacket::P2PPendingPacket(const P2PPendingPacket& other) = default;
 P2PPendingPacket::~P2PPendingPacket() = default;
@@ -298,9 +291,7 @@
 
 bool P2PSocketUdp::HandleReadResult(int result) {
   if (result > 0) {
-    auto data =
-        base::span(reinterpret_cast<const uint8_t*>(recv_buffer_->data()),
-                   static_cast<size_t>(result));
+    auto data = recv_buffer_->first(static_cast<size_t>(result));
 
     if (!base::Contains(connected_peers_, recv_address_)) {
       P2PSocket::StunMessageType type;
@@ -364,10 +355,7 @@
   // messages are sent in correct order.
   if (!base::Contains(connected_peers_, packet.to)) {
     P2PSocket::StunMessageType type = P2PSocket::StunMessageType();
-    bool stun = GetStunPacketType(
-        base::span(reinterpret_cast<const uint8_t*>(packet.data->data()),
-                   packet.size),
-        &type);
+    bool stun = GetStunPacketType(packet.data->first(packet.size), &type);
     if (!stun || type == STUN_DATA_INDICATION) {
       LOG(ERROR) << "Page tried to send a data packet to "
                  << packet.to.ToString() << " before STUN binding is finished.";
@@ -437,10 +425,7 @@
     }
   }
 
-  delegate_->DumpPacket(
-      base::span(reinterpret_cast<const uint8_t*>(packet.data->data()),
-                 packet.size),
-      false);
+  delegate_->DumpPacket(packet.data->first(packet.size), false);
 
   return true;
 }
diff --git a/services/network/proxy_resolving_socket_mojo_unittest.cc b/services/network/proxy_resolving_socket_mojo_unittest.cc
index 6cbd35d5..26f933d 100644
--- a/services/network/proxy_resolving_socket_mojo_unittest.cc
+++ b/services/network/proxy_resolving_socket_mojo_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/390223051): Remove C-library calls to fix the errors.
-#pragma allow_unsafe_libc_calls
-#endif
-
 #include <string>
 #include <string_view>
 #include <utility>
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 93fa4d9..a01d0ec6 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
@@ -555,28 +555,26 @@
   mojom::IntegrityAlgorithm type;
 };
 
-// Parse a hash-source, return false on error.
-bool ParseHash(std::string_view expression, mojom::CSPHashSource* hash) {
+// Parse a hash-source without quotes around it. Return false on error.
+bool ParseUnquotedHash(std::string_view expression,
+                       mojom::CSPHashSource* hash) {
   static const SupportedPrefixesStruct SupportedPrefixes[] = {
-      {"'sha256-", 8, mojom::IntegrityAlgorithm::kSha256},
-      {"'sha384-", 8, mojom::IntegrityAlgorithm::kSha384},
-      {"'sha512-", 8, mojom::IntegrityAlgorithm::kSha512},
-      {"'sha-256-", 9, mojom::IntegrityAlgorithm::kSha256},
-      {"'sha-384-", 9, mojom::IntegrityAlgorithm::kSha384},
-      {"'sha-512-", 9, mojom::IntegrityAlgorithm::kSha512},
-      {"'ed25519-", 9, mojom::IntegrityAlgorithm::kEd25519}};
+      {"sha256-", 7, mojom::IntegrityAlgorithm::kSha256},
+      {"sha384-", 7, mojom::IntegrityAlgorithm::kSha384},
+      {"sha512-", 7, mojom::IntegrityAlgorithm::kSha512},
+      {"sha-256-", 8, mojom::IntegrityAlgorithm::kSha256},
+      {"sha-384-", 8, mojom::IntegrityAlgorithm::kSha384},
+      {"sha-512-", 8, mojom::IntegrityAlgorithm::kSha512},
+      {"ed25519-", 8, mojom::IntegrityAlgorithm::kEd25519}};
 
   for (auto item : SupportedPrefixes) {
     if (base::StartsWith(expression, item.prefix,
                          base::CompareCase::INSENSITIVE_ASCII)) {
       std::string_view subexpression = expression.substr(
-          item.prefix_length, expression.length() - item.prefix_length - 1);
+          item.prefix_length, expression.length() - item.prefix_length);
       if (!IsBase64(subexpression))
         return false;
 
-      if (expression[expression.length() - 1] != '\'')
-        return false;
-
       hash->algorithm = item.type;
 
       // We lazily accept both base64url and base64-encoded data.
@@ -597,6 +595,16 @@
   return false;
 }
 
+bool ParseHash(std::string_view expression, mojom::CSPHashSource* hash) {
+  if (expression.size() < 2) {
+    return false;
+  }
+  if (expression[0] != '\'' || expression[expression.length() - 1] != '\'') {
+    return false;
+  }
+  return ParseUnquotedHash(expression.substr(1, expression.length() - 2), hash);
+}
+
 mojom::IntegrityAlgorithm StrongestHashAlgorithm(
     std::optional<mojom::IntegrityAlgorithm> previous,
     mojom::IntegrityAlgorithm current) {
@@ -606,6 +614,19 @@
   return current;
 }
 
+bool ParseURLHash(std::string_view expression, mojom::CSPHashSource* hash) {
+  constexpr char prefix[] = "'url-";
+  constexpr size_t prefix_length = 5u;
+  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),
+      hash);
+}
+
 // Parse source-list grammar.
 // https://www.w3.org/TR/CSP3/#grammardef-serialized-source-list
 // Append parsing errors to |parsing_errors|.
@@ -654,7 +675,14 @@
     auto csp_source = mojom::CSPSource::New();
     if (ParseSource(directive_name, expression, csp_source.get(),
                     parsing_errors)) {
-      directive->sources.push_back(std::move(csp_source));
+      if (directive_name != CSPDirectiveName::ScriptSrcV2) {
+        directive->sources.push_back(std::move(csp_source));
+      } else {
+        parsing_errors.emplace_back(base::StringPrintf(
+            "The Content-Security-Policy directive 'script-src-v2' doesn't "
+            "permit source expression %s. It will be ignored.",
+            std::string(expression).c_str()));
+      }
       continue;
     }
 
@@ -749,6 +777,20 @@
       continue;
     }
 
+    auto url_hash = mojom::CSPHashSource::New();
+    if (ParseURLHash(expression, url_hash.get())) {
+      if (directive_name == CSPDirectiveName::ScriptSrcV2) {
+        directive->url_hashes.push_back(std::move(url_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 33431684..466b1e79 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
@@ -1220,13 +1220,72 @@
                 ~mojom::WebSandboxFlags::kAutomaticFeatures);
 }
 
+TEST(ContentSecurityPolicy,
+     ParseSerializedSourceList_ScriptSrcWithScriptSrcV2Disabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(network::features::kCSPScriptSrcV2);
+  scoped_refptr<net::HttpResponseHeaders> headers(
+      new net::HttpResponseHeaders("HTTP/1.1 200 OK"));
+
+  std::string directive_value =
+      "'sha256-YWJj' 'nonce-cde' 'sha256-QUJD' 'url-sha256-Y2Q='";
+  headers->SetHeader("Content-Security-Policy",
+                     "script-src " + directive_value);
+  std::vector<mojom::ContentSecurityPolicyPtr> policies;
+  AddContentSecurityPolicyFromHeaders(*headers, GURL("https://example.com/"),
+                                      &policies);
+  auto expected_csp = mojom::CSPSourceList::New();
+  expected_csp->hashes.push_back(mojom::CSPHashSource::New(
+      mojom::IntegrityAlgorithm::kSha256, std::vector<uint8_t>{'a', 'b', 'c'}));
+  expected_csp->hashes.push_back(mojom::CSPHashSource::New(
+      mojom::IntegrityAlgorithm::kSha256, std::vector<uint8_t>{'A', 'B', 'C'}));
+  expected_csp->nonces.push_back("cde");
+
+  EXPECT_TRUE(expected_csp.Equals(
+      policies[0]->directives[mojom::CSPDirectiveName::ScriptSrc]));
+  EXPECT_EQ(
+      policies[0]->raw_directives[mojom::CSPDirectiveName::ScriptSrc],
+      std::string(base::TrimString(directive_value, " ", base::TRIM_ALL)));
+
+  EXPECT_EQ(
+      "The Content-Security-Policy directive 'script-src' contains "
+      "'url-sha256-Y2Q=' as a source expression that is permitted only for "
+      "'script-src-v2' directive. It will be ignored.",
+      policies[0]->parsing_errors[0]);
+}
+
+TEST(ContentSecurityPolicy,
+     ParseSerializedSourceList_ScriptSrcV2WithScriptSrcV2Disabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(network::features::kCSPScriptSrcV2);
+  scoped_refptr<net::HttpResponseHeaders> headers(
+      new net::HttpResponseHeaders("HTTP/1.1 200 OK"));
+
+  std::string directive_value =
+      "'sha256-YWJj' 'nonce-cde' 'sha256-QUJD' 'url-sha256-Y2Q='";
+  headers->SetHeader("Content-Security-Policy",
+                     "script-src-v2 " + directive_value);
+  std::vector<mojom::ContentSecurityPolicyPtr> policies;
+  AddContentSecurityPolicyFromHeaders(*headers, GURL("https://example.com/"),
+                                      &policies);
+  EXPECT_EQ(0u, policies[0]->directives.size());
+  EXPECT_EQ(0u, policies[0]->raw_directives.size());
+  EXPECT_EQ("Unrecognized Content-Security-Policy directive 'script-src-v2'.",
+            policies[0]->parsing_errors[0]);
+}
+
 TEST(ContentSecurityPolicy, ParseSerializedSourceList) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(network::features::kCSPScriptSrcV2);
+
   struct TestCase {
+    mojom::CSPDirectiveName directive_name;
     std::string directive_value;
     base::OnceCallback<mojom::CSPSourceListPtr()> expected;
     std::string expected_error;
   } cases[] = {
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'nonce-a' 'nonce-a=' 'nonce-a==' 'nonce-a===' 'nonce-==' 'nonce-' "
           "'nonce 'nonce-cde' 'nonce-cde=' 'nonce-cde==' 'nonce-cde==='",
           base::BindOnce([] {
@@ -1239,9 +1298,21 @@
             csp->nonces.push_back("cde==");
             return csp;
           }),
-          "",
+          "The source list for the Content Security Policy directive "
+          "'script-src' contains an invalid source: ''nonce-a===''. It will be "
+          "ignored.",
       },
       {
+          // Invalid hash:
+          mojom::CSPDirectiveName::ScriptSrc,
+          "'sha256-'",
+          base::BindOnce([] { return mojom::CSPSourceList::New(); }),
+          "The source list for the Content Security Policy directive "
+          "'script-src' contains an invalid source: ''sha256-''. It will be "
+          "ignored.",
+      },
+      {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'sha256-YWJj' 'nonce-cde' 'sha256-QUJD'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
@@ -1257,11 +1328,73 @@
           "",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
+          "'sha256-YWJj' 'nonce-cde' 'sha256-QUJD' 'url-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 "
+          "'url-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([] {
+            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->url_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([] {
+            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->url_hashes.push_back(
+                mojom::CSPHashSource::New(mojom::IntegrityAlgorithm::kSha256,
+                                          std::vector<uint8_t>{'c', 'd'}));
+            return csp;
+          }),
+          "The Content-Security-Policy directive 'script-src-v2' doesn't "
+          "permit source expression https://a.com/. It will be ignored.",
+      },
+      {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'none' ",
           base::BindOnce([] { return mojom::CSPSourceList::New(); }),
           "",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'none' 'self'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
@@ -1274,6 +1407,7 @@
           "otherwise it is ignored.",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'self' 'none'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
@@ -1286,6 +1420,7 @@
           "otherwise it is ignored.",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'none' 'report-sample'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
@@ -1295,6 +1430,7 @@
           "",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'none' 'self' 'report-sample'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
@@ -1308,33 +1444,46 @@
           "otherwise it is ignored.",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'none' 'report-sha256'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
             csp->report_hash_algorithm = mojom::IntegrityAlgorithm::kSha256;
             return csp;
           }),
-          "",
+          "The Content-Security-Policy directive 'script-src' contains the "
+          "keyword 'none' alongside with other source expressions. The keyword "
+          "'none' must be the only source expression in the directive value, "
+          "otherwise it is ignored.",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'none' 'report-sha384'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
             csp->report_hash_algorithm = mojom::IntegrityAlgorithm::kSha384;
             return csp;
           }),
-          "",
+          "The Content-Security-Policy directive 'script-src' contains the "
+          "keyword 'none' alongside with other source expressions. The keyword "
+          "'none' must be the only source expression in the directive value, "
+          "otherwise it is ignored.",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'none' 'report-sha512'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
             csp->report_hash_algorithm = mojom::IntegrityAlgorithm::kSha512;
             return csp;
           }),
-          "",
+          "The Content-Security-Policy directive 'script-src' contains the "
+          "keyword 'none' alongside with other source expressions. The keyword "
+          "'none' must be the only source expression in the directive value, "
+          "otherwise it is ignored.",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'self'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
@@ -1343,6 +1492,7 @@
           }),
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'wrong' *",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
@@ -1354,6 +1504,19 @@
           "ignored.",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrcV2,
+          "'wrong' *",
+          base::BindOnce([] {
+            auto csp = mojom::CSPSourceList::New();
+            csp->allow_star = true;
+            return csp;
+          }),
+          "The source list for the Content Security Policy directive "
+          "'script-src-v2' contains an invalid source: ''wrong''. It will be "
+          "ignored.",
+      },
+      {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'wrong' 'unsafe-inline'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
@@ -1365,6 +1528,7 @@
           "ignored.",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'wrong' 'unsafe-eval'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
@@ -1376,6 +1540,7 @@
           "ignored.",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'wrong' 'wasm-eval'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
@@ -1387,6 +1552,7 @@
           "ignored.",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'wrong' 'wasm-unsafe-eval'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
@@ -1398,6 +1564,7 @@
           "ignored.",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'wrong' 'strict-dynamic'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
@@ -1409,6 +1576,7 @@
           "ignored.",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'wrong' 'unsafe-hashes'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
@@ -1420,6 +1588,7 @@
           "ignored.",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
           "'wrong' 'report-sample'",
           base::BindOnce([] {
             auto csp = mojom::CSPSourceList::New();
@@ -1433,26 +1602,29 @@
   };
 
   for (auto& test : cases) {
-    SCOPED_TRACE(test.directive_value);
+    SCOPED_TRACE(ToString(test.directive_name) + " " + test.directive_value);
     scoped_refptr<net::HttpResponseHeaders> headers(
         new net::HttpResponseHeaders("HTTP/1.1 200 OK"));
-    headers->SetHeader("Content-Security-Policy",
-                       "script-src " + test.directive_value);
+    headers->SetHeader(
+        "Content-Security-Policy",
+        ToString(test.directive_name) + "  " + test.directive_value);
     std::vector<mojom::ContentSecurityPolicyPtr> policies;
     AddContentSecurityPolicyFromHeaders(*headers, GURL("https://example.com/"),
                                         &policies);
-    EXPECT_TRUE(
-        std::move(test.expected)
-            .Run()
-            .Equals(
-                policies[0]->directives[mojom::CSPDirectiveName::ScriptSrc]));
+    EXPECT_TRUE(std::move(test.expected)
+                    .Run()
+                    .Equals(policies[0]->directives[test.directive_name]));
 
-    EXPECT_EQ(policies[0]->raw_directives[mojom::CSPDirectiveName::ScriptSrc],
+    EXPECT_EQ(policies[0]->raw_directives[test.directive_name],
               std::string(
                   base::TrimString(test.directive_value, " ", base::TRIM_ALL)));
 
-    if (!test.expected_error.empty())
+    if (test.expected_error.empty()) {
+      EXPECT_TRUE(policies[0]->parsing_errors.empty());
+    } else {
+      ASSERT_FALSE(policies[0]->parsing_errors.empty());
       EXPECT_EQ(test.expected_error, policies[0]->parsing_errors[0]);
+    }
   }
 }
 
diff --git a/services/network/public/cpp/first_party_sets_mojom_traits.cc b/services/network/public/cpp/first_party_sets_mojom_traits.cc
index 1279d34..ed006bf9 100644
--- a/services/network/public/cpp/first_party_sets_mojom_traits.cc
+++ b/services/network/public/cpp/first_party_sets_mojom_traits.cc
@@ -76,11 +76,7 @@
   if (!entry.ReadSiteType(&site_type))
     return false;
 
-  std::optional<net::FirstPartySetEntry::SiteIndex> site_index;
-  if (!entry.ReadSiteIndex(&site_index))
-    return false;
-
-  *out = net::FirstPartySetEntry(primary, site_type, site_index);
+  *out = net::FirstPartySetEntry(primary, site_type, std::nullopt);
   return true;
 }
 
diff --git a/services/network/public/cpp/first_party_sets_mojom_traits.h b/services/network/public/cpp/first_party_sets_mojom_traits.h
index 1e7c1b2..06edbdf8 100644
--- a/services/network/public/cpp/first_party_sets_mojom_traits.h
+++ b/services/network/public/cpp/first_party_sets_mojom_traits.h
@@ -52,11 +52,6 @@
     return e.site_type();
   }
 
-  static const std::optional<net::FirstPartySetEntry::SiteIndex>& site_index(
-      const net::FirstPartySetEntry& e) {
-    return e.site_index();
-  }
-
   static bool Read(network::mojom::FirstPartySetEntryDataView entry,
                    net::FirstPartySetEntry* out);
 };
diff --git a/services/network/public/mojom/first_party_sets.mojom b/services/network/public/mojom/first_party_sets.mojom
index a718137..d38b51f 100644
--- a/services/network/public/mojom/first_party_sets.mojom
+++ b/services/network/public/mojom/first_party_sets.mojom
@@ -24,7 +24,6 @@
 struct FirstPartySetEntry {
   SchemefulSite primary;
   SiteType site_type;
-  SiteIndex? site_index;
 };
 
 // This struct must match the class fields defined in
diff --git a/services/network/shared_resource_checker.cc b/services/network/shared_resource_checker.cc
index 5ad0429..b9a00e1 100644
--- a/services/network/shared_resource_checker.cc
+++ b/services/network/shared_resource_checker.cc
@@ -122,7 +122,8 @@
 
 bool SharedResourceChecker::IsSharedResource(
     const ResourceRequest& request,
-    const std::optional<url::Origin>& top_frame_origin) {
+    const std::optional<url::Origin>& top_frame_origin,
+    base::optional_ref<const net::CookiePartitionKey> cookie_partition_key) {
   if (!enabled_) {
     return false;
   }
@@ -148,7 +149,7 @@
   // Disabled if third-party cookies are disabled.
   if (!cookie_settings_->IsFullCookieAccessAllowed(
           request.url, request.site_for_cookies, top_frame_origin,
-          net::CookieSettingOverrides())) {
+          net::CookieSettingOverrides(), cookie_partition_key)) {
     return false;
   }
 
diff --git a/services/network/shared_resource_checker.h b/services/network/shared_resource_checker.h
index 2f7a668..fb41797 100644
--- a/services/network/shared_resource_checker.h
+++ b/services/network/shared_resource_checker.h
@@ -12,6 +12,7 @@
 #include "base/component_export.h"
 #include "base/memory/raw_ref.h"
 #include "base/types/optional_ref.h"
+#include "net/cookies/cookie_partition_key.h"
 #include "url/origin.h"
 
 namespace content_settings {
@@ -45,8 +46,10 @@
   // fingerprinting.
   //
   // See https://chromestatus.com/feature/5202380930678784
-  bool IsSharedResource(const ResourceRequest& request,
-                        const std::optional<url::Origin>& top_frame_origin);
+  bool IsSharedResource(
+      const ResourceRequest& request,
+      const std::optional<url::Origin>& top_frame_origin,
+      base::optional_ref<const net::CookiePartitionKey> cookie_partition_key);
 
  private:
   const raw_ref<const content_settings::CookieSettingsBase> cookie_settings_;
diff --git a/services/network/shared_resource_checker_unittest.cc b/services/network/shared_resource_checker_unittest.cc
index 310f121..7292e18 100644
--- a/services/network/shared_resource_checker_unittest.cc
+++ b/services/network/shared_resource_checker_unittest.cc
@@ -164,10 +164,11 @@
     request.destination = destination;
     if (enabled() &&
         request.destination == mojom::RequestDestination::kScript) {
-      EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request, origin));
+      EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request, origin,
+                                                              std::nullopt));
     } else {
-      EXPECT_FALSE(
-          shared_resource_checker()->IsSharedResource(request, origin));
+      EXPECT_FALSE(shared_resource_checker()->IsSharedResource(request, origin,
+                                                               std::nullopt));
     }
   }
 }
@@ -177,8 +178,8 @@
   for (const char* url : kPatternMatches) {
     ResourceRequest request = CreateResourceRequest(url);
     std::optional<url::Origin> origin = url::Origin::Create(request.url);
-    EXPECT_EQ(expect,
-              shared_resource_checker()->IsSharedResource(request, origin));
+    EXPECT_EQ(expect, shared_resource_checker()->IsSharedResource(
+                          request, origin, std::nullopt));
   }
 }
 
@@ -186,7 +187,8 @@
   for (const char* url : kPatternMatchFails) {
     ResourceRequest request = CreateResourceRequest(url);
     std::optional<url::Origin> origin = url::Origin::Create(request.url);
-    EXPECT_FALSE(shared_resource_checker()->IsSharedResource(request, origin));
+    EXPECT_FALSE(shared_resource_checker()->IsSharedResource(request, origin,
+                                                             std::nullopt));
   }
 }
 
@@ -203,10 +205,12 @@
   ResourceRequest request = CreateResourceRequest(kPatternMatches[0]);
   std::optional<url::Origin> origin = url::Origin::Create(request.url);
   request.load_flags = net::LOAD_CAN_USE_SHARED_DICTIONARY;
-  EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request, origin));
+  EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request, origin,
+                                                          std::nullopt));
   for (auto flag : kBlockedFlags) {
     request.load_flags = net::LOAD_CAN_USE_SHARED_DICTIONARY | flag;
-    EXPECT_FALSE(shared_resource_checker()->IsSharedResource(request, origin));
+    EXPECT_FALSE(shared_resource_checker()->IsSharedResource(request, origin,
+                                                             std::nullopt));
   }
 }
 
@@ -216,9 +220,24 @@
   }
   ResourceRequest request = CreateResourceRequest(kPatternMatches[0]);
   std::optional<url::Origin> origin = url::Origin::Create(request.url);
-  EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request, origin));
+  EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request, origin,
+                                                          std::nullopt));
+  auto cookie_partition_key = net::CookiePartitionKey::FromURLForTesting(
+      request.url, net::CookiePartitionKey::AncestorChainBit::kCrossSite);
+  EXPECT_TRUE(shared_resource_checker()->IsSharedResource(
+      request, origin, cookie_partition_key));
+
+  // Should not be shared for a nonced partition key.
+  cookie_partition_key = net::CookiePartitionKey::FromURLForTesting(
+      request.url, net::CookiePartitionKey::AncestorChainBit::kCrossSite,
+      base::UnguessableToken::Create());
+  EXPECT_FALSE(shared_resource_checker()->IsSharedResource(
+      request, origin, cookie_partition_key));
+
+  // Should not be shared when cookies are blocked.
   BlockAllCookies();
-  EXPECT_FALSE(shared_resource_checker()->IsSharedResource(request, origin));
+  EXPECT_FALSE(shared_resource_checker()->IsSharedResource(request, origin,
+                                                           std::nullopt));
 }
 
 TEST_P(SharedResourceCheckerTest, PatternLimits) {
@@ -233,23 +252,31 @@
   ResourceRequest request4 = CreateResourceRequest(kUrlVariants[3]);
   std::optional<url::Origin> origin = url::Origin::Create(request1.url);
 
-  EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request1, origin));
-  EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request2, origin));
+  EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request1, origin,
+                                                          std::nullopt));
+  EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request2, origin,
+                                                          std::nullopt));
 
   // Only 2 URLs per pattern should match (in a given hour window).
-  EXPECT_FALSE(shared_resource_checker()->IsSharedResource(request3, origin));
+  EXPECT_FALSE(shared_resource_checker()->IsSharedResource(request3, origin,
+                                                           std::nullopt));
 
   // Freshen the timestamp on the first request.
   task_environment.AdvanceClock(base::Hours(1));
-  EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request1, origin));
+  EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request1, origin,
+                                                          std::nullopt));
   task_environment.AdvanceClock(base::Hours(1));
 
   // request3 should now be able to take request 2's spot (but request 1 is
   // still just inside the 1-hour window).
-  EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request3, origin));
-  EXPECT_FALSE(shared_resource_checker()->IsSharedResource(request2, origin));
-  EXPECT_FALSE(shared_resource_checker()->IsSharedResource(request4, origin));
-  EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request1, origin));
+  EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request3, origin,
+                                                          std::nullopt));
+  EXPECT_FALSE(shared_resource_checker()->IsSharedResource(request2, origin,
+                                                           std::nullopt));
+  EXPECT_FALSE(shared_resource_checker()->IsSharedResource(request4, origin,
+                                                           std::nullopt));
+  EXPECT_TRUE(shared_resource_checker()->IsSharedResource(request1, origin,
+                                                          std::nullopt));
 }
 
 INSTANTIATE_TEST_SUITE_P(/*no prefix*/,
diff --git a/services/network/tcp_bound_socket_unittest.cc b/services/network/tcp_bound_socket_unittest.cc
index 8e12b6b..a5d8570b 100644
--- a/services/network/tcp_bound_socket_unittest.cc
+++ b/services/network/tcp_bound_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/390223051): Remove C-library calls to fix the errors.
-#pragma allow_unsafe_libc_calls
-#endif
-
 #include <stdint.h>
 
 #include <string>
diff --git a/services/network/tcp_socket_unittest.cc b/services/network/tcp_socket_unittest.cc
index 3ad7d50..493826f 100644
--- a/services/network/tcp_socket_unittest.cc
+++ b/services/network/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/390223051): Remove C-library calls to fix the errors.
-#pragma allow_unsafe_libc_calls
-#endif
-
 #include <stdint.h>
 
 #include <utility>
diff --git a/services/network/udp_socket_unittest.cc b/services/network/udp_socket_unittest.cc
index ea468fdd..a94a2e84 100644
--- a/services/network/udp_socket_unittest.cc
+++ b/services/network/udp_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/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "services/network/udp_socket.h"
 
 #include <stddef.h>
@@ -19,6 +14,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/notreached.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
@@ -121,8 +117,7 @@
       const net::IPEndPoint& address,
       net::CompletionOnceCallback callback,
       const net::NetworkTrafficAnnotationTag& traffic_annotation) override {
-    EXPECT_EQ(expected_data_,
-              std::vector<unsigned char>(buf->data(), buf->data() + buf_len));
+    EXPECT_EQ(expected_data_, buf->first(base::checked_cast<size_t>(buf_len)));
     if (should_complete_requests_)
       return net::OK;
     pending_io_buffers_.push_back(buf);
@@ -354,8 +349,7 @@
   // and that buffer still contains the exact same data.
   net::IOBuffer* buf = socket_raw_ptr->pending_io_buffers()[0];
   int buf_len = socket_raw_ptr->pending_io_buffer_lengths()[0];
-  EXPECT_EQ(test_msg,
-            std::vector<unsigned char>(buf->data(), buf->data() + buf_len));
+  EXPECT_EQ(test_msg, buf->first(base::checked_cast<size_t>(buf_len)));
 }
 
 // Test that exercises the queuing of send requests and makes sure
diff --git a/services/network/url_loader_util.cc b/services/network/url_loader_util.cc
index 61cc342..c0080e2 100644
--- a/services/network/url_loader_util.cc
+++ b/services/network/url_loader_util.cc
@@ -577,9 +577,12 @@
       factory_params.isolation_info,
       factory_params.automatically_assign_isolation_info, request);
   if (isolation_info) {
-    url_request.set_is_shared_resource(shared_resource_checker.IsSharedResource(
-        request, isolation_info->top_frame_origin()));
+    // set_isolation_info sets the url_request's cookie_partition_key which can
+    // then be used when checking IsSharedResource().
     url_request.set_isolation_info(std::move(isolation_info).value());
+    url_request.set_is_shared_resource(shared_resource_checker.IsSharedResource(
+        request, url_request.isolation_info().top_frame_origin(),
+        url_request.cookie_partition_key()));
   }
 
   // When a service worker forwards a navigation request it uses the
diff --git a/services/network/web_bundle/web_bundle_chunked_buffer.h b/services/network/web_bundle/web_bundle_chunked_buffer.h
index ae69a5f..b059769 100644
--- a/services/network/web_bundle/web_bundle_chunked_buffer.h
+++ b/services/network/web_bundle/web_bundle_chunked_buffer.h
@@ -73,7 +73,8 @@
     uint64_t start_pos() const { return start_pos_; }
     uint64_t end_pos() const { return start_pos_ + bytes_->size(); }
     size_t size() const { return bytes_->size(); }
-    const uint8_t* data() const { return bytes_->data(); }
+    // Data can be accessed by constructing a span from this via
+    // base::span(chunk).
     auto begin() const { return bytes_->begin(); }
     auto end() const { return bytes_->end(); }
 
diff --git a/services/network/web_bundle/web_bundle_chunked_buffer_unittest.cc b/services/network/web_bundle/web_bundle_chunked_buffer_unittest.cc
index 00ef826..1be74d5 100644
--- a/services/network/web_bundle/web_bundle_chunked_buffer_unittest.cc
+++ b/services/network/web_bundle/web_bundle_chunked_buffer_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/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "services/network/web_bundle/web_bundle_chunked_buffer.h"
 
 #include "base/check.h"
@@ -72,7 +67,7 @@
   EXPECT_EQ(start_pos, chunk.start_pos());
   EXPECT_EQ(start_pos + kDataLength, chunk.end_pos());
   EXPECT_EQ(kDataLength, chunk.size());
-  EXPECT_EQ(0, memcmp(chunk.data(), kData, kDataLength));
+  EXPECT_EQ(base::span(chunk), base::byte_span_with_nul_from_cstring(kData));
 }
 
 TEST_F(WebBundleChunkedBufferTest, EmptyBuffer) {
@@ -291,8 +286,8 @@
                                       << " length: " << test_case.length);
     auto data = std::vector<unsigned char>(10, 'X');
     auto result = data_source->Read(
-        test_case.offset, base::span<char>(reinterpret_cast<char*>(data.data()),
-                                           test_case.length));
+        test_case.offset,
+        base::as_writable_chars(base::span(data)).first(test_case.length));
     EXPECT_EQ(test_case.expected_result, result.result);
     EXPECT_EQ(test_case.expected_bytes_read, result.bytes_read);
     EXPECT_EQ(test_case.expected_read_result,
diff --git a/testing/buildbot/client.v8.fyi.json b/testing/buildbot/client.v8.fyi.json
index 46ea20a1..015fb02 100644
--- a/testing/buildbot/client.v8.fyi.json
+++ b/testing/buildbot/client.v8.fyi.json
@@ -2257,7 +2257,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-14"
+            "os": "Mac-15"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -2286,7 +2286,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-14"
+            "os": "Mac-15"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 5
@@ -2314,7 +2314,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-14"
+            "os": "Mac-15"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 2
@@ -2341,7 +2341,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-14"
+            "os": "Mac-15"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
@@ -2373,7 +2373,7 @@
         "swarming": {
           "dimensions": {
             "cpu": "x86-64",
-            "os": "Mac-14"
+            "os": "Mac-15"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
           "shards": 18
diff --git a/testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter b/testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter
index ea4f9e6d..5a757f4 100644
--- a/testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter
+++ b/testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter
@@ -1,6 +1,5 @@
 # TODO(crbug.com/395595608) Failing due to per-surface scaling.
 -All/GetDisplayMediaHiDpiBrowserTest.Capture/1
--DesktopBrowserFrameAuraLinuxTest.NewWindowSize
 
 # Very flaky/failing on mutter (less flaky/passing on weston)
 -All/OmniboxSearchAggregatorHTTPErrorTest.HTTPErrorResponse/404
diff --git a/testing/buildbot/internal.optimization_guide.json b/testing/buildbot/internal.optimization_guide.json
index fda66e0..6deb169 100644
--- a/testing/buildbot/internal.optimization_guide.json
+++ b/testing/buildbot/internal.optimization_guide.json
@@ -1048,7 +1048,7 @@
           "--platform",
           "iPhone 14",
           "--version",
-          "18.0",
+          "18.2",
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xctest",
@@ -1058,7 +1058,7 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "optimization_guide_gpu_unittests iPhone 14 18.0",
+        "name": "optimization_guide_gpu_unittests iPhone 14 18.2",
         "swarming": {
           "cipd_packages": [
             {
@@ -1074,8 +1074,8 @@
           },
           "named_caches": [
             {
-              "name": "runtime_ios_18_0",
-              "path": "Runtime-ios-18.0"
+              "name": "runtime_ios_18_2",
+              "path": "Runtime-ios-18.2"
             },
             {
               "name": "xcode_ios_16c5032a",
@@ -1086,14 +1086,14 @@
         },
         "test": "optimization_guide_gpu_unittests",
         "test_id_prefix": "ninja://components/optimization_guide/internal:optimization_guide_gpu_unittests/",
-        "variant_id": "iPhone 14 18.0"
+        "variant_id": "iPhone 14 18.2"
       },
       {
         "args": [
           "--platform",
           "iPhone 14",
           "--version",
-          "18.0",
+          "18.2",
           "--out-dir",
           "${ISOLATED_OUTDIR}",
           "--xctest",
@@ -1103,7 +1103,7 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "optimization_guide_unittests iPhone 14 18.0",
+        "name": "optimization_guide_unittests iPhone 14 18.2",
         "swarming": {
           "cipd_packages": [
             {
@@ -1119,8 +1119,8 @@
           },
           "named_caches": [
             {
-              "name": "runtime_ios_18_0",
-              "path": "Runtime-ios-18.0"
+              "name": "runtime_ios_18_2",
+              "path": "Runtime-ios-18.2"
             },
             {
               "name": "xcode_ios_16c5032a",
@@ -1131,7 +1131,7 @@
         },
         "test": "optimization_guide_unittests",
         "test_id_prefix": "ninja://components/optimization_guide/internal:optimization_guide_unittests/",
-        "variant_id": "iPhone 14 18.0"
+        "variant_id": "iPhone 14 18.2"
       }
     ]
   },
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index af5dfce..dd9fc79 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -422,12 +422,12 @@
       },
     },
   },
-  'ios_runtime_cache_18_0': {
+  'ios_runtime_cache_18_2': {
     'swarming': {
       'named_caches': [
         {
-          'name': 'runtime_ios_18_0',
-          'path': 'Runtime-ios-18.0',
+          'name': 'runtime_ios_18_2',
+          'path': 'Runtime-ios-18.2',
         },
       ],
     },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 08a1776..140d8759 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -931,7 +931,7 @@
         'swarming': {
           'dimensions': {
             'cpu': 'x86-64',
-            'os': 'Mac-14',
+            'os': 'Mac-15',
           },
         },
         'additional_compile_targets': ['blink_tests'],
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 2819f4d90..9a285d856 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3546,26 +3546,6 @@
             ]
         }
     ],
-    "ButterForPrefsAndThemesAndSearchEngines": [
-        {
-            "platforms": [
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "EnablePreferencesAccountStorage",
-                        "SeparateLocalAndAccountSearchEngines",
-                        "SeparateLocalAndAccountThemes",
-                        "ThemesBatchUpload"
-                    ]
-                }
-            ]
-        }
-    ],
     "CCTEarlyNav": [
         {
             "platforms": [
@@ -6176,6 +6156,21 @@
             ]
         }
     ],
+    "CpssUseTfliteSignatureRunnerKillSwitch": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Disabled_CpssUseTfliteSignatureRunner",
+                    "disable_features": [
+                        "CpssUseTfliteSignatureRunner"
+                    ]
+                }
+            ]
+        }
+    ],
     "CrOSAudioSidetoneAndMicIndicator": [
         {
             "platforms": [
@@ -19609,8 +19604,7 @@
                 {
                     "name": "RestrictAbusePorts",
                     "params": {
-                        "monitor_ports": "30986",
-                        "restrict_ports": "30987,30988,30989"
+                        "PortsToRestrictForAbuse": "30987,30988,30989"
                     },
                     "enable_features": [
                         "RestrictAbusePorts"
@@ -25055,6 +25049,19 @@
             ]
         }
     ],
+    "WebRTC-NoSdpMangleUfragRestrictedAddresses": [
+        {
+            "platforms": [
+                "android",
+                "android_webview"
+            ],
+            "experiments": [
+                {
+                    "name": "127.0.0.1:12345|127.0.0.1:23456|127.0.0.1:34567|127.0.0.1:45678|127.0.0.1:56789"
+                }
+            ]
+        }
+    ],
     "WebRTC-SendBufferSizeBytes": [
         {
             "platforms": [
diff --git a/third_party/android_deps/fetch_all.py b/third_party/android_deps/fetch_all.py
index 6cae972..4eb470d5 100755
--- a/third_party/android_deps/fetch_all.py
+++ b/third_party/android_deps/fetch_all.py
@@ -322,7 +322,7 @@
 def _ParseSubprojects(subproject_path):
     """Parses listing of subproject build.gradle files. Returns list of paths."""
     if not os.path.exists(subproject_path):
-        return None
+        return {}
 
     subprojects = {}
     for subproject in open(subproject_path):
diff --git a/third_party/androidx/fetch_all_androidx.py b/third_party/androidx/fetch_all_androidx.py
index 88342a0..e022e62 100755
--- a/third_party/androidx/fetch_all_androidx.py
+++ b/third_party/androidx/fetch_all_androidx.py
@@ -73,7 +73,7 @@
     logging.info('URL for the latest build info: %s', androidx_artifacts_url)
     # Strip '/repository' from pattern.
     resolved_snapshot_repository_url_pattern = (
-        _build_snapshot_repository_url('([0-9]*)').rsplit('/', 1)[0])
+        fetch_util.make_androidx_maven_url('([0-9]*)').rsplit('/', 1)[0])
     match = re.match(resolved_snapshot_repository_url_pattern,
                      androidx_artifacts_url)
     assert match is not None
diff --git a/third_party/angle b/third_party/angle
index 8357b6a..2d61c57 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 8357b6a24cae20d24058cfef3277066c4993a1ae
+Subproject commit 2d61c576f0dd8a1a5ef5adeefe19101c5ea64eb7
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 0ef7b52..3a140c372 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -7959,7 +7959,7 @@
       LoadNetworkResourcePageResult resource
 
   # Sets Controls for third-party cookie access
-  # Page reload is required before the new cookie bahavior will be observed
+  # Page reload is required before the new cookie behavior will be observed
   experimental command setCookieControls
     parameters
       # Whether 3pc restriction is enabled.
diff --git a/third_party/blink/renderer/core/animation/BUILD.gn b/third_party/blink/renderer/core/animation/BUILD.gn
index 2d3b681..7fc03e4a 100644
--- a/third_party/blink/renderer/core/animation/BUILD.gn
+++ b/third_party/blink/renderer/core/animation/BUILD.gn
@@ -232,6 +232,7 @@
     "interpolation.h",
     "interpolation_effect.cc",
     "interpolation_effect.h",
+    "interpolation_type.cc",
     "interpolation_type.h",
     "interpolation_types_map.cc",
     "interpolation_types_map.h",
diff --git a/third_party/blink/renderer/core/animation/animation_test.cc b/third_party/blink/renderer/core/animation/animation_test.cc
index e3d8321..ee03a2f7 100644
--- a/third_party/blink/renderer/core/animation/animation_test.cc
+++ b/third_party/blink/renderer/core/animation/animation_test.cc
@@ -113,7 +113,9 @@
 
   KeyframeEffectModelBase* MakeSimpleEffectModel() {
     PropertyHandle PropertyHandleOpacity(GetCSSPropertyOpacity());
-    static CSSNumberInterpolationType opacity_type(PropertyHandleOpacity);
+    CSSNumberInterpolationType* opacity_type(
+        MakeGarbageCollected<CSSNumberInterpolationType>(
+            PropertyHandleOpacity));
     TransitionKeyframe* start_keyframe =
         MakeGarbageCollected<TransitionKeyframe>(PropertyHandleOpacity);
     start_keyframe->SetValue(MakeGarbageCollected<TypedInterpolationValue>(
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc
index 6e7c888..baaad0a 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -2523,9 +2523,9 @@
   InterpolationValue end = nullptr;
   bool discrete_interpolation = true;
 
-  for (const auto& interpolation_type : map.Get(property)) {
+  for (const auto& interpolation_type : *map.Get(property)) {
     start = interpolation_type->MaybeConvertUnderlyingValue(old_environment);
-    transition_type = interpolation_type.get();
+    transition_type = interpolation_type.Get();
     if (!start) {
       continue;
     }
@@ -2624,7 +2624,7 @@
   TransitionKeyframe* start_keyframe =
       MakeGarbageCollected<TransitionKeyframe>(property);
   start_keyframe->SetValue(MakeGarbageCollected<TypedInterpolationValue>(
-      *transition_type, start.interpolable_value->Clone(),
+      transition_type, start.interpolable_value->Clone(),
       start.non_interpolable_value));
   start_keyframe->SetOffset(0);
   keyframes.push_back(start_keyframe);
@@ -2632,7 +2632,7 @@
   TransitionKeyframe* end_keyframe =
       MakeGarbageCollected<TransitionKeyframe>(property);
   end_keyframe->SetValue(MakeGarbageCollected<TypedInterpolationValue>(
-      *transition_type, end.interpolable_value->Clone(),
+      transition_type, end.interpolable_value->Clone(),
       end.non_interpolable_value));
   end_keyframe->SetOffset(1);
   keyframes.push_back(end_keyframe);
diff --git a/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.cc
index cd918d0..e915847f 100644
--- a/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/animation/interpolable_aspect_ratio.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
diff --git a/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc
index 192d5cc..39060f0 100644
--- a/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.cc
@@ -10,6 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/values_equivalent.h"
 #include "third_party/blink/renderer/core/animation/basic_shape_interpolation_functions.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
@@ -207,7 +208,7 @@
   if (!basic_shape_interpolation_functions::ShapesAreCompatible(
           *underlying_value_owner.Value().non_interpolable_value,
           *value.non_interpolable_value)) {
-    underlying_value_owner.Set(*this, value);
+    underlying_value_owner.Set(this, value);
     return;
   }
 
diff --git a/third_party/blink/renderer/core/animation/css_border_image_length_box_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_border_image_length_box_interpolation_type.cc
index ac7d09d..59cd9a3f 100644
--- a/third_party/blink/renderer/core/animation/css_border_image_length_box_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_border_image_length_box_interpolation_type.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/animation/interpolable_length.h"
 #include "third_party/blink/renderer/core/animation/list_interpolation_functions.h"
 #include "third_party/blink/renderer/core/animation/side_index.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_math_function_value.h"
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
@@ -375,7 +376,7 @@
     const InterpolationValue& value,
     double interpolation_fraction) const {
   ListInterpolationFunctions::Composite(
-      underlying_value_owner, underlying_fraction, *this, value,
+      underlying_value_owner, underlying_fraction, this, value,
       ListInterpolationFunctions::LengthMatchingStrategy::kEqual,
       ListInterpolationFunctions::InterpolableValuesKnownCompatible,
       NonInterpolableSidesAreCompatible, CompositeSide);
diff --git a/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc
index 03f3060..3330d88 100644
--- a/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_clip_interpolation_type.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/animation/interpolable_length.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_quad_value.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
@@ -272,7 +273,7 @@
     underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd(
         underlying_fraction, *value.interpolable_value);
   else
-    underlying_value_owner.Set(*this, value);
+    underlying_value_owner.Set(this, value);
 }
 
 void CSSClipInterpolationType::ApplyStandardPropertyValue(
diff --git a/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc
index 2f2eb63..e7ea00e 100644
--- a/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/animation/color_property_functions.h"
 #include "third_party/blink/renderer/core/animation/interpolable_color.h"
 #include "third_party/blink/renderer/core/animation/interpolable_value.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_color.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h"
diff --git a/third_party/blink/renderer/core/animation/css_content_visibility_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_content_visibility_interpolation_type.cc
index 83d6e976..e50ca54 100644
--- a/third_party/blink/renderer/core/animation/css_content_visibility_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_content_visibility_interpolation_type.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value_mappings.h"
 #include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
@@ -201,7 +202,7 @@
     double underlying_fraction,
     const InterpolationValue& value,
     double interpolation_fraction) const {
-  underlying_value_owner.Set(*this, value);
+  underlying_value_owner.Set(this, value);
 }
 
 void CSSContentVisibilityInterpolationType::ApplyStandardPropertyValue(
diff --git a/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
index d4ec374..636f986 100644
--- a/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
@@ -6,6 +6,7 @@
 
 #include "third_party/blink/renderer/core/animation/interpolable_length.h"
 #include "third_party/blink/renderer/core/animation/underlying_length_checker.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -80,12 +81,12 @@
   // TODO(andruud): Make InterpolationType::Composite take an UnderlyingValue
   // rather than an UnderlyingValueOwner.
   UnderlyingValueOwner owner;
-  owner.Set(*this, underlying);
+  owner.Set(this, underlying);
 
   ConversionCheckers null_checkers;
 
   const CSSInterpolationType* interpolation_type =
-      inner_interpolation_type_.get();
+      inner_interpolation_type_.Get();
   auto composite_callback =
       [interpolation_type, composite, &null_checkers](
           UnderlyingValue& underlying_value, double underlying_fraction,
@@ -108,7 +109,7 @@
       };
 
   ListInterpolationFunctions::Composite(
-      owner, 1.0, *this, value,
+      owner, 1.0, this, value,
       ListInterpolationFunctions::LengthMatchingStrategy::kEqual,
       ListInterpolationFunctions::InterpolableValuesKnownCompatible,
       NonInterpolableValuesAreCompatible, composite_callback);
@@ -162,14 +163,14 @@
   // TODO(andruud): Make InterpolationType::Composite take an UnderlyingValue
   // rather than an UnderlyingValueOwner.
   const CSSInterpolationType* interpolation_type =
-      inner_interpolation_type_.get();
+      inner_interpolation_type_.Get();
   auto composite_callback =
       [interpolation_type, interpolation_fraction](
           UnderlyingValue& underlying_value, double underlying_fraction,
           const InterpolableValue& interpolable_value,
           const NonInterpolableValue* non_interpolable_value) {
         UnderlyingValueOwner owner;
-        owner.Set(*interpolation_type,
+        owner.Set(interpolation_type,
                   InterpolationValue(
                       underlying_value.MutableInterpolableValue().Clone(),
                       underlying_value.GetNonInterpolableValue()));
@@ -187,7 +188,7 @@
       };
 
   ListInterpolationFunctions::Composite(
-      underlying_value_owner, underlying_fraction, *this, value,
+      underlying_value_owner, underlying_fraction, this, value,
       ListInterpolationFunctions::LengthMatchingStrategy::kEqual,
       ListInterpolationFunctions::InterpolableValuesKnownCompatible,
       NonInterpolableValuesAreCompatible, composite_callback);
@@ -197,7 +198,7 @@
     InterpolationValue&& start,
     InterpolationValue&& end) const {
   const CSSInterpolationType* interpolation_type =
-      inner_interpolation_type_.get();
+      inner_interpolation_type_.Get();
   return ListInterpolationFunctions::MaybeMergeSingles(
       std::move(start), std::move(end),
       ListInterpolationFunctions::LengthMatchingStrategy::kEqual,
diff --git a/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.h b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.h
index d24b5a2..0fe0ba8 100644
--- a/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.h
+++ b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.h
@@ -18,11 +18,11 @@
   CSSCustomListInterpolationType(
       PropertyHandle property,
       const PropertyRegistration* registration,
-      std::unique_ptr<CSSInterpolationType> inner_interpolation_type,
+      const CSSInterpolationType* inner_interpolation_type,
       CSSSyntaxType syntax_type,
       CSSSyntaxRepeat syntax_repeat)
       : CSSInterpolationType(property, registration),
-        inner_interpolation_type_(std::move(inner_interpolation_type)),
+        inner_interpolation_type_(inner_interpolation_type),
         syntax_repeat_(syntax_repeat) {
     DCHECK(property.IsCSSCustomProperty());
   }
@@ -50,6 +50,11 @@
   InterpolationValue MaybeConvertCustomPropertyUnderlyingValue(
       const CSSValue&) const final;
 
+  void Trace(Visitor* v) const override {
+    CSSInterpolationType::Trace(v);
+    v->Trace(inner_interpolation_type_);
+  }
+
  private:
   // These methods only apply to CSSInterpolationTypes used by standard CSS
   // properties. CSSCustomListInterpolationType is only accessible via
@@ -84,7 +89,7 @@
   // Currently, InterpolationTypes are not designed to "nest" in this way due to
   // their mandatory association with specific properties, so please do not call
   // InterpolationType::Apply on inner_interpolation_type_.
-  std::unique_ptr<CSSInterpolationType> inner_interpolation_type_;
+  Member<const CSSInterpolationType> inner_interpolation_type_;
 
   // TODO(crbug.com/981537, 981538, 981542): Add support for <image>,
   // <transform-function> and <transform-list> and make use of |syntax_type_|.
diff --git a/third_party/blink/renderer/core/animation/css_default_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_default_interpolation_type.cc
index faab562a..84ae512 100644
--- a/third_party/blink/renderer/core/animation/css_default_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_default_interpolation_type.cc
@@ -6,6 +6,7 @@
 
 #include "third_party/blink/renderer/core/animation/css_interpolation_environment.h"
 #include "third_party/blink/renderer/core/animation/string_keyframe.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder.h"
 
 namespace blink {
@@ -41,6 +42,14 @@
       MakeGarbageCollected<CSSDefaultNonInterpolableValue>(css_value));
 }
 
+void CSSDefaultInterpolationType::Composite(
+    UnderlyingValueOwner& underlying_value_owner,
+    double underlying_fraction,
+    const InterpolationValue& value,
+    double interpolation_fraction) const {
+  underlying_value_owner.Set(this, value);
+}
+
 void CSSDefaultInterpolationType::Apply(
     const InterpolableValue&,
     const NonInterpolableValue* non_interpolable_value,
diff --git a/third_party/blink/renderer/core/animation/css_default_interpolation_type.h b/third_party/blink/renderer/core/animation/css_default_interpolation_type.h
index 0b71a183..9223868 100644
--- a/third_party/blink/renderer/core/animation/css_default_interpolation_type.h
+++ b/third_party/blink/renderer/core/animation/css_default_interpolation_type.h
@@ -74,12 +74,10 @@
     return nullptr;
   }
 
-  void Composite(UnderlyingValueOwner& underlying_value_owner,
+  void Composite(UnderlyingValueOwner&,
                  double underlying_fraction,
-                 const InterpolationValue& value,
-                 double interpolation_fraction) const final {
-    underlying_value_owner.Set(*this, value);
-  }
+                 const InterpolationValue&,
+                 double interpolation_fraction) const final;
 
   void Apply(const InterpolableValue&,
              const NonInterpolableValue*,
diff --git a/third_party/blink/renderer/core/animation/css_display_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_display_interpolation_type.cc
index b0cb314c9..58ac15f 100644
--- a/third_party/blink/renderer/core/animation/css_display_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_display_interpolation_type.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value_mappings.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
@@ -192,7 +193,7 @@
     double underlying_fraction,
     const InterpolationValue& value,
     double interpolation_fraction) const {
-  underlying_value_owner.Set(*this, value);
+  underlying_value_owner.Set(this, value);
 }
 
 void CSSDisplayInterpolationType::ApplyStandardPropertyValue(
diff --git a/third_party/blink/renderer/core/animation/css_dynamic_range_limit_interpolation_type_test.cc b/third_party/blink/renderer/core/animation/css_dynamic_range_limit_interpolation_type_test.cc
index a4f22ad..6828138d 100644
--- a/third_party/blink/renderer/core/animation/css_dynamic_range_limit_interpolation_type_test.cc
+++ b/third_party/blink/renderer/core/animation/css_dynamic_range_limit_interpolation_type_test.cc
@@ -25,13 +25,14 @@
 
 class CSSDynamicRangeLimitInterpolationTypeTest : public PageTestBase {
  protected:
-  std::unique_ptr<CSSDynamicRangeLimitInterpolationType>
+  CSSDynamicRangeLimitInterpolationType*
   CreateDynamicRangeLimitInterpolationType() {
     ScopedCSSDynamicRangeLimitForTest scoped_feature(true);
     const CSSProperty& css_property =
         CSSProperty::Get(CSSPropertyID::kDynamicRangeLimit);
     PropertyHandle property = PropertyHandle(css_property);
-    return std::make_unique<CSSDynamicRangeLimitInterpolationType>(property);
+    return MakeGarbageCollected<CSSDynamicRangeLimitInterpolationType>(
+        property);
   }
 };
 
@@ -51,7 +52,7 @@
   StyleResolverState state(document, *element, nullptr,
                            StyleRequest(element->GetComputedStyle()));
 
-  std::unique_ptr<CSSDynamicRangeLimitInterpolationType>
+  CSSDynamicRangeLimitInterpolationType*
       dynamic_range_limit_interpolation_type =
           CreateDynamicRangeLimitInterpolationType();
 
@@ -68,7 +69,7 @@
 }
 
 TEST_F(CSSDynamicRangeLimitInterpolationTypeTest, MaybeConvertValue) {
-  std::unique_ptr<CSSDynamicRangeLimitInterpolationType>
+  CSSDynamicRangeLimitInterpolationType*
       dynamic_range_limit_interpolation_type =
           CreateDynamicRangeLimitInterpolationType();
   CSSDynamicRangeLimitInterpolationType::ConversionCheckers conversion_checkers;
diff --git a/third_party/blink/renderer/core/animation/css_filter_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_filter_list_interpolation_type.cc
index 2012bfb..4f41521 100644
--- a/third_party/blink/renderer/core/animation/css_filter_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_filter_list_interpolation_type.cc
@@ -10,6 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/animation/interpolable_filter.h"
 #include "third_party/blink/renderer/core/animation/list_interpolation_functions.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
@@ -273,7 +274,7 @@
     double interpolation_fraction) const {
   // We do our compositing behavior in |PreInterpolationCompositeIfNeeded|; see
   // the documentation on that method.
-  underlying_value_owner.Set(*this, value);
+  underlying_value_owner.Set(this, value);
 }
 
 void CSSFilterListInterpolationType::ApplyStandardPropertyValue(
diff --git a/third_party/blink/renderer/core/animation/css_font_palette_interpolation_type_test.cc b/third_party/blink/renderer/core/animation/css_font_palette_interpolation_type_test.cc
index 22a4ce6..36616d2f 100644
--- a/third_party/blink/renderer/core/animation/css_font_palette_interpolation_type_test.cc
+++ b/third_party/blink/renderer/core/animation/css_font_palette_interpolation_type_test.cc
@@ -25,12 +25,11 @@
 
 class CSSFontPaletteInterpolationTypeTest : public PageTestBase {
  protected:
-  std::unique_ptr<CSSFontPaletteInterpolationType>
-  CreateFontPaletteInterpolationType() {
+  CSSFontPaletteInterpolationType* CreateFontPaletteInterpolationType() {
     const CSSProperty& css_property =
         CSSProperty::Get(CSSPropertyID::kFontPalette);
     PropertyHandle property = PropertyHandle(css_property);
-    return std::make_unique<CSSFontPaletteInterpolationType>(property);
+    return MakeGarbageCollected<CSSFontPaletteInterpolationType>(property);
   }
 };
 
@@ -52,8 +51,8 @@
   StyleResolverState state(document, *element, nullptr,
                            StyleRequest(element->GetComputedStyle()));
 
-  std::unique_ptr<CSSFontPaletteInterpolationType>
-      font_palette_interpolation_type = CreateFontPaletteInterpolationType();
+  CSSFontPaletteInterpolationType* font_palette_interpolation_type =
+      CreateFontPaletteInterpolationType();
 
   InterpolationValue result = font_palette_interpolation_type
                                   ->MaybeConvertStandardPropertyUnderlyingValue(
@@ -68,8 +67,8 @@
 }
 
 TEST_F(CSSFontPaletteInterpolationTypeTest, MaybeConvertValue) {
-  std::unique_ptr<CSSFontPaletteInterpolationType>
-      font_palette_interpolation_type = CreateFontPaletteInterpolationType();
+  CSSFontPaletteInterpolationType* font_palette_interpolation_type =
+      CreateFontPaletteInterpolationType();
   CSSFontPaletteInterpolationType::ConversionCheckers conversion_checkers;
   CSSValue* value =
       MakeGarbageCollected<CSSCustomIdentValue>(AtomicString("--palette"));
diff --git a/third_party/blink/renderer/core/animation/css_font_size_adjust_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_font_size_adjust_interpolation_type.cc
index 77c035a..82944e9 100644
--- a/third_party/blink/renderer/core/animation/css_font_size_adjust_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_font_size_adjust_interpolation_type.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/animation/css_font_size_adjust_interpolation_type.h"
 
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value_mappings.h"
 #include "third_party/blink/renderer/core/css/css_math_function_value.h"
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
@@ -197,7 +198,7 @@
     underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd(
         underlying_fraction, *value.interpolable_value);
   } else {
-    underlying_value_owner.Set(*this, value);
+    underlying_value_owner.Set(this, value);
   }
 }
 
diff --git a/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc
index 84d8e145..66eb6af4 100644
--- a/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_font_variation_settings_interpolation_type.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_font_variation_value.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h"
@@ -182,7 +183,7 @@
     underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd(
         underlying_fraction, *value.interpolable_value);
   } else {
-    underlying_value_owner.Set(*this, value);
+    underlying_value_owner.Set(this, value);
   }
 }
 
diff --git a/third_party/blink/renderer/core/animation/css_grid_template_property_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_grid_template_property_interpolation_type.cc
index b93446e..c96cb01 100644
--- a/third_party/blink/renderer/core/animation/css_grid_template_property_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_grid_template_property_interpolation_type.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/animation/interpolable_grid_track_list.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
@@ -304,7 +305,7 @@
            *underlying_value_owner.Value().interpolable_value)
            .IsCompatibleWith(
                To<InterpolableGridTrackList>(*value.interpolable_value))) {
-    underlying_value_owner.Set(*this, value);
+    underlying_value_owner.Set(this, value);
     return;
   }
   underlying_value_owner.SetNonInterpolableValue(value.non_interpolable_value);
diff --git a/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc
index 9048ab7..1ff14c3 100644
--- a/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_image_interpolation_type.cc
@@ -8,6 +8,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/memory/values_equivalent.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_crossfade_value.h"
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
@@ -280,7 +281,7 @@
     double underlying_fraction,
     const InterpolationValue& value,
     double interpolation_fraction) const {
-  underlying_value_owner.Set(*this, value);
+  underlying_value_owner.Set(this, value);
 }
 
 void CSSImageInterpolationType::ApplyStandardPropertyValue(
diff --git a/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc
index 16589b2d..3621d61c 100644
--- a/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_image_list_interpolation_type.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/animation/css_image_interpolation_type.h"
 #include "third_party/blink/renderer/core/animation/image_list_property_functions.h"
 #include "third_party/blink/renderer/core/animation/list_interpolation_functions.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
@@ -169,7 +170,7 @@
     double underlying_fraction,
     const InterpolationValue& value,
     double interpolation_fraction) const {
-  underlying_value_owner.Set(*this, value);
+  underlying_value_owner.Set(this, value);
 }
 
 void CSSImageListInterpolationType::ApplyStandardPropertyValue(
diff --git a/third_party/blink/renderer/core/animation/css_image_slice_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_image_slice_interpolation_type.cc
index 78ebbd1..0f98eb45 100644
--- a/third_party/blink/renderer/core/animation/css_image_slice_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_image_slice_interpolation_type.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/animation/css_length_interpolation_type.h"
 #include "third_party/blink/renderer/core/animation/image_slice_property_functions.h"
 #include "third_party/blink/renderer/core/animation/side_index.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_border_image_slice_value.h"
 #include "third_party/blink/renderer/core/css/css_math_function_value.h"
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
@@ -284,7 +285,7 @@
     underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd(
         underlying_fraction, *value.interpolable_value);
   else
-    underlying_value_owner.Set(*this, value);
+    underlying_value_owner.Set(this, value);
 }
 
 void CSSImageSliceInterpolationType::ApplyStandardPropertyValue(
diff --git a/third_party/blink/renderer/core/animation/css_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_interpolation_type.cc
index 3b1f37a..43844c0 100644
--- a/third_party/blink/renderer/core/animation/css_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_interpolation_type.cc
@@ -378,6 +378,11 @@
   return MaybeConvertValue(*value, state, conversion_checkers);
 }
 
+void CSSInterpolationType::Trace(Visitor* v) const {
+  InterpolationType::Trace(v);
+  v->Trace(registration_);
+}
+
 InterpolationValue CSSInterpolationType::MaybeConvertUnderlyingValue(
     const CSSInterpolationEnvironment& environment) const {
   const ComputedStyle& style = environment.BaseStyle();
diff --git a/third_party/blink/renderer/core/animation/css_interpolation_type.h b/third_party/blink/renderer/core/animation/css_interpolation_type.h
index daa31d7..f12563c 100644
--- a/third_party/blink/renderer/core/animation/css_interpolation_type.h
+++ b/third_party/blink/renderer/core/animation/css_interpolation_type.h
@@ -70,6 +70,8 @@
     NOTREACHED();
   }
 
+  void Trace(Visitor* v) const override;
+
  protected:
   explicit CSSInterpolationType(PropertyHandle,
                                 const PropertyRegistration* = nullptr);
@@ -117,7 +119,7 @@
                                 const NonInterpolableValue*,
                                 StyleResolverState&) const;
 
-  WeakPersistent<const PropertyRegistration> registration_;
+  WeakMember<const PropertyRegistration> registration_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css_intrinsic_length_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_intrinsic_length_interpolation_type.cc
index 7abc671..8eb178e 100644
--- a/third_party/blink/renderer/core/animation/css_intrinsic_length_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_intrinsic_length_interpolation_type.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/animation/interpolable_length.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
diff --git a/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc
index 61c0849..9b04df3 100644
--- a/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_length_interpolation_type.cc
@@ -10,6 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/animation/interpolable_length.h"
 #include "third_party/blink/renderer/core/animation/length_property_functions.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
@@ -200,7 +201,7 @@
     double interpolation_fraction) const {
   // We do our compositing behavior in |PreInterpolationCompositeIfNeeded|; see
   // the documentation on that method.
-  underlying_value_owner.Set(*this, value);
+  underlying_value_owner.Set(this, value);
 }
 
 PairwiseInterpolationValue CSSLengthInterpolationType::MaybeMergeSingles(
diff --git a/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc
index a6ff34c..e98b590 100644
--- a/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/animation/length_list_property_functions.h"
 #include "third_party/blink/renderer/core/animation/list_interpolation_functions.h"
 #include "third_party/blink/renderer/core/animation/underlying_length_checker.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
@@ -150,7 +151,7 @@
     const InterpolationValue& value,
     double interpolation_fraction) const {
   ListInterpolationFunctions::Composite(
-      underlying_value_owner, underlying_fraction, *this, value,
+      underlying_value_owner, underlying_fraction, this, value,
       ListInterpolationFunctions::LengthMatchingStrategy::kLowestCommonMultiple,
       ListInterpolationFunctions::InterpolableValuesKnownCompatible,
       ListInterpolationFunctions::VerifyNoNonInterpolableValues,
diff --git a/third_party/blink/renderer/core/animation/css_offset_rotate_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_offset_rotate_interpolation_type.cc
index f6f13a4..718924f 100644
--- a/third_party/blink/renderer/core/animation/css_offset_rotate_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_offset_rotate_interpolation_type.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_math_expression_node.h"
 #include "third_party/blink/renderer/core/css/css_math_function_value.h"
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
@@ -212,7 +213,7 @@
     underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd(
         underlying_fraction, *value.interpolable_value);
   } else {
-    underlying_value_owner.Set(*this, value);
+    underlying_value_owner.Set(this, value);
   }
 }
 
diff --git a/third_party/blink/renderer/core/animation/css_overlay_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_overlay_interpolation_type.cc
index c360d73..b6fc0bc 100644
--- a/third_party/blink/renderer/core/animation/css_overlay_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_overlay_interpolation_type.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value_mappings.h"
 #include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
@@ -182,7 +183,7 @@
     double underlying_fraction,
     const InterpolationValue& value,
     double interpolation_fraction) const {
-  underlying_value_owner.Set(*this, value);
+  underlying_value_owner.Set(this, value);
 }
 
 void CSSOverlayInterpolationType::ApplyStandardPropertyValue(
diff --git a/third_party/blink/renderer/core/animation/css_path_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_path_interpolation_type.cc
index 17aa6c9..e60e5f2 100644
--- a/third_party/blink/renderer/core/animation/css_path_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_path_interpolation_type.cc
@@ -86,7 +86,7 @@
     const InterpolationValue& value,
     double interpolation_fraction) const {
   PathInterpolationFunctions::Composite(underlying_value_owner,
-                                        underlying_fraction, *this, value);
+                                        underlying_fraction, this, value);
 }
 
 InterpolationValue CSSPathInterpolationType::MaybeConvertNeutral(
diff --git a/third_party/blink/renderer/core/animation/css_ray_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_ray_interpolation_type.cc
index fe49138..f753b99 100644
--- a/third_party/blink/renderer/core/animation/css_ray_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_ray_interpolation_type.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "third_party/blink/renderer/core/animation/interpolable_length.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/basic_shape_functions.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value_mappings.h"
@@ -261,7 +262,7 @@
     underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd(
         underlying_fraction, *value.interpolable_value);
   } else {
-    underlying_value_owner.Set(*this, value);
+    underlying_value_owner.Set(this, value);
   }
 }
 
diff --git a/third_party/blink/renderer/core/animation/css_rotate_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_rotate_interpolation_type.cc
index 68f1da7..5ea9bf9 100644
--- a/third_party/blink/renderer/core/animation/css_rotate_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_rotate_interpolation_type.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
diff --git a/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc
index d24dfa7..07af7cf 100644
--- a/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_scale_interpolation_type.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/animation/interpolable_value.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_math_function_value.h"
 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
diff --git a/third_party/blink/renderer/core/animation/css_scrollbar_color_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_scrollbar_color_interpolation_type.cc
index 0f0f295..24514323 100644
--- a/third_party/blink/renderer/core/animation/css_scrollbar_color_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_scrollbar_color_interpolation_type.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "third_party/blink/renderer/core/animation/interpolable_scrollbar_color.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_color.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h"
@@ -211,7 +212,7 @@
            *underlying_value_owner.Value().non_interpolable_value)
            .IsCompatibleWith(To<CSSScrollbarColorNonInterpolableValue>(
                *value.non_interpolable_value))) {
-    underlying_value_owner.Set(*this, value);
+    underlying_value_owner.Set(this, value);
   }
 
   auto& underlying = To<InterpolableScrollbarColor>(
diff --git a/third_party/blink/renderer/core/animation/css_shadow_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_shadow_list_interpolation_type.cc
index a3413a2..486952b3 100644
--- a/third_party/blink/renderer/core/animation/css_shadow_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_shadow_list_interpolation_type.cc
@@ -10,6 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/animation/interpolable_shadow.h"
 #include "third_party/blink/renderer/core/animation/list_interpolation_functions.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
@@ -171,7 +172,7 @@
     double interpolation_fraction) const {
   // We do our compositing behavior in |PreInterpolationCompositeIfNeeded|; see
   // the documentation on that method.
-  underlying_value_owner.Set(*this, value);
+  underlying_value_owner.Set(this, value);
 }
 
 static ShadowList* CreateShadowList(
diff --git a/third_party/blink/renderer/core/animation/css_shape_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_shape_interpolation_type.cc
index 14864515..eedc750 100644
--- a/third_party/blink/renderer/core/animation/css_shape_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_shape_interpolation_type.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/core/animation/css_position_axis_list_interpolation_type.h"
 #include "third_party/blink/renderer/core/animation/interpolable_length.h"
 #include "third_party/blink/renderer/core/animation/interpolable_value.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_path_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
diff --git a/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc
index d8f2c03..a6c463a 100644
--- a/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc
@@ -173,7 +173,7 @@
     const InterpolationValue& value,
     double interpolation_fraction) const {
   ListInterpolationFunctions::Composite(
-      underlying_value_owner, underlying_fraction, *this, value,
+      underlying_value_owner, underlying_fraction, this, value,
       ListInterpolationFunctions::LengthMatchingStrategy::kLowestCommonMultiple,
       ListInterpolationFunctions::InterpolableValuesKnownCompatible,
       SizeInterpolationFunctions::NonInterpolableValuesAreCompatible,
diff --git a/third_party/blink/renderer/core/animation/css_text_indent_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_text_indent_interpolation_type.cc
index 6f46cef5..6a14d5ed 100644
--- a/third_party/blink/renderer/core/animation/css_text_indent_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_text_indent_interpolation_type.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/animation/interpolable_length.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
diff --git a/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
index 9c4bff4a..83cd0cc 100644
--- a/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
@@ -9,6 +9,7 @@
 
 #include "third_party/blink/renderer/core/animation/interpolable_transform_list.h"
 #include "third_party/blink/renderer/core/animation/length_units_checker.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_function_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
@@ -182,7 +183,7 @@
     double interpolation_fraction) const {
   // We do our compositing behavior in |PreInterpolationCompositeIfNeeded|; see
   // the documentation on that method.
-  underlying_value_owner.Set(*this, value);
+  underlying_value_owner.Set(this, value);
 }
 
 void CSSTransformInterpolationType::ApplyStandardPropertyValue(
diff --git a/third_party/blink/renderer/core/animation/css_translate_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_translate_interpolation_type.cc
index c7b947d..d724672 100644
--- a/third_party/blink/renderer/core/animation/css_translate_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_translate_interpolation_type.cc
@@ -9,6 +9,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/animation/interpolable_length.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
diff --git a/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.cc
index 01f9890..8134a77 100644
--- a/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.cc
@@ -10,6 +10,7 @@
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/animation/css_interpolation_environment.h"
 #include "third_party/blink/renderer/core/animation/string_keyframe.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_unparsed_declaration_value.h"
 #include "third_party/blink/renderer/core/css/css_unset_value.h"
 #include "third_party/blink/renderer/core/css/property_registration.h"
@@ -127,6 +128,14 @@
   return nullptr;
 }
 
+void CSSVarCycleInterpolationType::Composite(
+    UnderlyingValueOwner& underlying_value_owner,
+    double underlying_fraction,
+    const InterpolationValue& value,
+    double interpolation_fraction) const {
+  underlying_value_owner.Set(this, value);
+}
+
 void CSSVarCycleInterpolationType::Apply(
     const InterpolableValue&,
     const NonInterpolableValue*,
diff --git a/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.h b/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.h
index 2ddadef..e3ae136c 100644
--- a/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.h
+++ b/third_party/blink/renderer/core/animation/css_var_cycle_interpolation_type.h
@@ -29,6 +29,11 @@
   CSSVarCycleInterpolationType(const PropertyHandle&,
                                const PropertyRegistration&);
 
+  void Trace(Visitor* v) const override {
+    InterpolationType::Trace(v);
+    v->Trace(registration_);
+  }
+
  private:
   InterpolationValue MaybeConvertSingle(const PropertySpecificKeyframe&,
                                         const CSSInterpolationEnvironment&,
@@ -45,18 +50,16 @@
   InterpolationValue MaybeConvertUnderlyingValue(
       const CSSInterpolationEnvironment&) const final;
 
-  void Composite(UnderlyingValueOwner& underlying_value_owner,
+  void Composite(UnderlyingValueOwner&,
                  double underlying_fraction,
-                 const InterpolationValue& value,
-                 double interpolation_fraction) const final {
-    underlying_value_owner.Set(*this, value);
-  }
+                 const InterpolationValue&,
+                 double interpolation_fraction) const final;
 
   void Apply(const InterpolableValue&,
              const NonInterpolableValue*,
              CSSInterpolationEnvironment&) const final;
 
-  WeakPersistent<const PropertyRegistration> registration_;
+  WeakMember<const PropertyRegistration> registration_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc
index 9e9ca417f..034701a 100644
--- a/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_visibility_interpolation_type.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value_mappings.h"
 #include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
@@ -187,7 +188,7 @@
     double underlying_fraction,
     const InterpolationValue& value,
     double interpolation_fraction) const {
-  underlying_value_owner.Set(*this, value);
+  underlying_value_owner.Set(this, value);
 }
 
 void CSSVisibilityInterpolationType::ApplyStandardPropertyValue(
diff --git a/third_party/blink/renderer/core/animation/interpolable_value_test.cc b/third_party/blink/renderer/core/animation/interpolable_value_test.cc
index 18ea596..22b02815 100644
--- a/third_party/blink/renderer/core/animation/interpolable_value_test.cc
+++ b/third_party/blink/renderer/core/animation/interpolable_value_test.cc
@@ -30,7 +30,8 @@
     // suffices for this, and also means we can ignore the AnimatableValues for
     // the compositor (as z-index isn't compositor-compatible).
     PropertyHandle property_handle(GetCSSPropertyZIndex());
-    CSSNumberInterpolationType interpolation_type(property_handle);
+    CSSNumberInterpolationType* interpolation_type(
+        MakeGarbageCollected<CSSNumberInterpolationType>(property_handle));
     InterpolationValue start(MakeGarbageCollected<InterpolableNumber>(a));
     InterpolationValue end(MakeGarbageCollected<InterpolableNumber>(b));
     TransitionInterpolation* i = MakeGarbageCollected<TransitionInterpolation>(
diff --git a/third_party/blink/renderer/core/animation/interpolation_effect_test.cc b/third_party/blink/renderer/core/animation/interpolation_effect_test.cc
index 28ecd696..88231a9 100644
--- a/third_party/blink/renderer/core/animation/interpolation_effect_test.cc
+++ b/third_party/blink/renderer/core/animation/interpolation_effect_test.cc
@@ -31,7 +31,8 @@
   // suffices for this, and also means we can ignore the AnimatableValues for
   // the compositor (as z-index isn't compositor-compatible).
   PropertyHandle property_handle(GetCSSPropertyZIndex());
-  CSSNumberInterpolationType interpolation_type(property_handle);
+  CSSNumberInterpolationType* interpolation_type(
+      MakeGarbageCollected<CSSNumberInterpolationType>(property_handle));
   InterpolationValue start(MakeGarbageCollected<InterpolableNumber>(from));
   InterpolationValue end(MakeGarbageCollected<InterpolableNumber>(to));
   return MakeGarbageCollected<TransitionInterpolation>(
diff --git a/third_party/blink/renderer/core/animation/interpolation_type.cc b/third_party/blink/renderer/core/animation/interpolation_type.cc
new file mode 100644
index 0000000..d0609d2
--- /dev/null
+++ b/third_party/blink/renderer/core/animation/interpolation_type.cc
@@ -0,0 +1,21 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/animation/interpolation_type.h"
+
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
+
+namespace blink {
+
+void InterpolationType::Composite(UnderlyingValueOwner& underlying_value_owner,
+                                  double underlying_fraction,
+                                  const InterpolationValue& value,
+                                  double interpolation_fraction) const {
+  DCHECK(!underlying_value_owner.Value().non_interpolable_value);
+  DCHECK(!value.non_interpolable_value);
+  underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd(
+      underlying_fraction, *value.interpolable_value);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/interpolation_type.h b/third_party/blink/renderer/core/animation/interpolation_type.h
index 11e0474..02d214d 100644
--- a/third_party/blink/renderer/core/animation/interpolation_type.h
+++ b/third_party/blink/renderer/core/animation/interpolation_type.h
@@ -10,15 +10,15 @@
 #include "third_party/blink/renderer/core/animation/interpolation_value.h"
 #include "third_party/blink/renderer/core/animation/keyframe.h"
 #include "third_party/blink/renderer/core/animation/pairwise_interpolation_value.h"
-#include "third_party/blink/renderer/core/animation/primitive_interpolation.h"
 #include "third_party/blink/renderer/core/animation/property_handle.h"
-#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
 
 class CSSInterpolationEnvironment;
+class UnderlyingValueOwner;
 
 // Subclasses of InterpolationType implement the logic for a specific value type
 // of a specific PropertyHandle to:
@@ -27,9 +27,8 @@
 // - Convert the target Element's property value to an InterpolationValue:
 // maybeConvertUnderlyingValue()
 // - Apply an InterpolationValue to a target Element's property: apply().
-class CORE_EXPORT InterpolationType {
-  USING_FAST_MALLOC(InterpolationType);
-
+class CORE_EXPORT InterpolationType
+    : public GarbageCollected<InterpolationType> {
  public:
   InterpolationType(const InterpolationType&) = delete;
   InterpolationType& operator=(const InterpolationType&) = delete;
@@ -46,16 +45,16 @@
     ConversionChecker(const ConversionChecker&) = delete;
     ConversionChecker& operator=(const ConversionChecker&) = delete;
     virtual ~ConversionChecker() = default;
-    virtual void Trace(Visitor*) const {}
+    virtual void Trace(Visitor* v) const { v->Trace(type_); }
 
-    void SetType(const InterpolationType& type) { type_ = &type; }
-    const InterpolationType& GetType() const { return *type_; }
+    void SetType(const InterpolationType* type) { type_ = type; }
+    const InterpolationType* GetType() const { return type_; }
     virtual bool IsValid(const CSSInterpolationEnvironment&,
                          const InterpolationValue& underlying) const = 0;
 
    protected:
     ConversionChecker() : type_(nullptr) {}
-    const InterpolationType* type_;
+    Member<const InterpolationType> type_;
   };
   using ConversionCheckers = HeapVector<Member<ConversionChecker>>;
 
@@ -95,15 +94,10 @@
   virtual InterpolationValue MaybeConvertUnderlyingValue(
       const CSSInterpolationEnvironment&) const = 0;
 
-  virtual void Composite(UnderlyingValueOwner& underlying_value_owner,
+  virtual void Composite(UnderlyingValueOwner&,
                          double underlying_fraction,
-                         const InterpolationValue& value,
-                         double interpolation_fraction) const {
-    DCHECK(!underlying_value_owner.Value().non_interpolable_value);
-    DCHECK(!value.non_interpolable_value);
-    underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd(
-        underlying_fraction, *value.interpolable_value);
-  }
+                         const InterpolationValue&,
+                         double interpolation_fraction) const;
 
   virtual void Apply(const InterpolableValue&,
                      const NonInterpolableValue*,
@@ -118,13 +112,15 @@
 
   // Implement reference equality checking via pointer equality checking as
   // these are singletons.
-  bool operator==(const InterpolationType& other) const {
-    return this == &other;
+  bool operator==(const InterpolationType* other) const {
+    return this == other;
   }
-  bool operator!=(const InterpolationType& other) const {
-    return this != &other;
+  bool operator!=(const InterpolationType* other) const {
+    return this != other;
   }
 
+  virtual void Trace(Visitor* v) const {}
+
  protected:
   explicit InterpolationType(PropertyHandle property) : property_(property) {}
 
diff --git a/third_party/blink/renderer/core/animation/interpolation_types_map.cc b/third_party/blink/renderer/core/animation/interpolation_types_map.cc
index 58fbbba..11b8084 100644
--- a/third_party/blink/renderer/core/animation/interpolation_types_map.cc
+++ b/third_party/blink/renderer/core/animation/interpolation_types_map.cc
@@ -68,6 +68,8 @@
 #include "third_party/blink/renderer/core/css/property_registry.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
+#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
@@ -86,14 +88,16 @@
   return registry->Registration(property.CustomPropertyName());
 }
 
-const InterpolationTypes& InterpolationTypesMap::Get(
+const InterpolationTypes* InterpolationTypesMap::Get(
     const PropertyHandle& property) const {
   using ApplicableTypesMap =
-      HashMap<PropertyHandle, std::unique_ptr<const InterpolationTypes>>;
-  DEFINE_STATIC_LOCAL(ApplicableTypesMap, all_applicable_types_map, ());
+      GCedHeapHashMap<PropertyHandle, Member<InterpolationTypes>>;
+  DEFINE_STATIC_LOCAL(Persistent<ApplicableTypesMap>, all_applicable_types_map,
+                      (MakeGarbageCollected<ApplicableTypesMap>()));
 
-  DEFINE_STATIC_LOCAL(ApplicableTypesMap, reduce_motion_applicable_types_map,
-                      ());
+  DEFINE_STATIC_LOCAL(Persistent<ApplicableTypesMap>,
+                      reduce_motion_applicable_types_map,
+                      (MakeGarbageCollected<ApplicableTypesMap>()));
 
   // Custom property interpolation types may change over time so don't trust the
   // applicable_types_map without checking the registry. Also since the static
@@ -107,16 +111,16 @@
   bool reduce_motion = document_.ShouldForceReduceMotion();
 
   ApplicableTypesMap& applicable_types_map =
-      reduce_motion ? reduce_motion_applicable_types_map
-                    : all_applicable_types_map;
+      reduce_motion ? *reduce_motion_applicable_types_map
+                    : *all_applicable_types_map;
 
   auto entry = applicable_types_map.find(property);
   if (entry != applicable_types_map.end()) {
-    return *entry->value;
+    return entry->value;
   }
 
-  std::unique_ptr<InterpolationTypes> applicable_types =
-      std::make_unique<InterpolationTypes>();
+  InterpolationTypes* applicable_types(
+      MakeGarbageCollected<InterpolationTypes>());
 
   const CSSProperty& css_property = property.GetCSSProperty();
 
@@ -178,27 +182,28 @@
       case CSSPropertyID::kX:
       case CSSPropertyID::kY:
         applicable_types->push_back(
-            std::make_unique<CSSLengthInterpolationType>(property));
+            MakeGarbageCollected<CSSLengthInterpolationType>(property));
         break;
       case CSSPropertyID::kAspectRatio:
         applicable_types->push_back(
-            std::make_unique<CSSAspectRatioInterpolationType>(property));
+            MakeGarbageCollected<CSSAspectRatioInterpolationType>(property));
         break;
       case CSSPropertyID::kGridTemplateColumns:
       case CSSPropertyID::kGridTemplateRows:
         applicable_types->push_back(
-            std::make_unique<CSSGridTemplatePropertyInterpolationType>(
+            MakeGarbageCollected<CSSGridTemplatePropertyInterpolationType>(
                 property));
         break;
       case CSSPropertyID::kContainIntrinsicWidth:
       case CSSPropertyID::kContainIntrinsicHeight:
         applicable_types->push_back(
-            std::make_unique<CSSIntrinsicLengthInterpolationType>(property));
+            MakeGarbageCollected<CSSIntrinsicLengthInterpolationType>(
+                property));
         break;
       case CSSPropertyID::kDynamicRangeLimit:
         if (RuntimeEnabledFeatures::CSSDynamicRangeLimitEnabled()) {
           applicable_types->push_back(
-              std::make_unique<CSSDynamicRangeLimitInterpolationType>(
+              MakeGarbageCollected<CSSDynamicRangeLimitInterpolationType>(
                   property));
         }
         break;
@@ -218,30 +223,30 @@
       case CSSPropertyID::kWidows:
       case CSSPropertyID::kZIndex:
         applicable_types->push_back(
-            std::make_unique<CSSNumberInterpolationType>(property));
+            MakeGarbageCollected<CSSNumberInterpolationType>(property));
         break;
       case CSSPropertyID::kCornerTopLeftShape:
       case CSSPropertyID::kCornerTopRightShape:
       case CSSPropertyID::kCornerBottomLeftShape:
       case CSSPropertyID::kCornerBottomRightShape:
         applicable_types->push_back(
-            std::make_unique<CSSSuperellipseInterpolationType>(property));
+            MakeGarbageCollected<CSSSuperellipseInterpolationType>(property));
         break;
       case CSSPropertyID::kTextSizeAdjust:
         applicable_types->push_back(
-            std::make_unique<CSSPercentageInterpolationType>(property));
+            MakeGarbageCollected<CSSPercentageInterpolationType>(property));
         break;
       case CSSPropertyID::kLineHeight:
       case CSSPropertyID::kTabSize:
         applicable_types->push_back(
-            std::make_unique<CSSLengthInterpolationType>(property));
+            MakeGarbageCollected<CSSLengthInterpolationType>(property));
         applicable_types->push_back(
-            std::make_unique<CSSNumberInterpolationType>(property));
+            MakeGarbageCollected<CSSNumberInterpolationType>(property));
         break;
       case CSSPropertyID::kInterestTargetShowDelay:
       case CSSPropertyID::kInterestTargetHideDelay:
         applicable_types->push_back(
-            std::make_unique<CSSTimeInterpolationType>(property));
+            MakeGarbageCollected<CSSTimeInterpolationType>(property));
         break;
       case CSSPropertyID::kAccentColor:
       case CSSPropertyID::kBackgroundColor:
@@ -261,189 +266,192 @@
       case CSSPropertyID::kRowRuleColor:
       case CSSPropertyID::kWebkitTextStrokeColor:
         applicable_types->push_back(
-            std::make_unique<CSSColorInterpolationType>(property));
+            MakeGarbageCollected<CSSColorInterpolationType>(property));
         break;
       case CSSPropertyID::kFill:
       case CSSPropertyID::kStroke:
         applicable_types->push_back(
-            std::make_unique<CSSPaintInterpolationType>(property));
+            MakeGarbageCollected<CSSPaintInterpolationType>(property));
         break;
       case CSSPropertyID::kOffsetPath:
         applicable_types->push_back(
-            std::make_unique<CSSBasicShapeInterpolationType>(property));
+            MakeGarbageCollected<CSSBasicShapeInterpolationType>(property));
         applicable_types->push_back(
-            std::make_unique<CSSRayInterpolationType>(property));
+            MakeGarbageCollected<CSSRayInterpolationType>(property));
         applicable_types->push_back(
-            std::make_unique<CSSPathInterpolationType>(property));
+            MakeGarbageCollected<CSSPathInterpolationType>(property));
         applicable_types->push_back(
-            std::make_unique<CSSShapeInterpolationType>(property));
+            MakeGarbageCollected<CSSShapeInterpolationType>(property));
         break;
       case CSSPropertyID::kD:
         applicable_types->push_back(
-            std::make_unique<CSSPathInterpolationType>(property));
+            MakeGarbageCollected<CSSPathInterpolationType>(property));
         break;
       case CSSPropertyID::kBoxShadow:
       case CSSPropertyID::kTextShadow:
         applicable_types->push_back(
-            std::make_unique<CSSShadowListInterpolationType>(property));
+            MakeGarbageCollected<CSSShadowListInterpolationType>(property));
         break;
       case CSSPropertyID::kBorderImageSource:
       case CSSPropertyID::kListStyleImage:
       case CSSPropertyID::kWebkitMaskBoxImageSource:
         applicable_types->push_back(
-            std::make_unique<CSSImageInterpolationType>(property));
+            MakeGarbageCollected<CSSImageInterpolationType>(property));
         break;
       case CSSPropertyID::kBackgroundImage:
         applicable_types->push_back(
-            std::make_unique<CSSImageListInterpolationType>(property));
+            MakeGarbageCollected<CSSImageListInterpolationType>(property));
         break;
       case CSSPropertyID::kStrokeDasharray:
         applicable_types->push_back(
-            std::make_unique<CSSLengthListInterpolationType>(property));
+            MakeGarbageCollected<CSSLengthListInterpolationType>(property));
         break;
       case CSSPropertyID::kFontWeight:
         applicable_types->push_back(
-            std::make_unique<CSSFontWeightInterpolationType>(property));
+            MakeGarbageCollected<CSSFontWeightInterpolationType>(property));
         break;
       case CSSPropertyID::kFontStretch:
         applicable_types->push_back(
-            std::make_unique<CSSFontStretchInterpolationType>(property));
+            MakeGarbageCollected<CSSFontStretchInterpolationType>(property));
         break;
       case CSSPropertyID::kFontStyle:
         applicable_types->push_back(
-            std::make_unique<CSSFontStyleInterpolationType>(property));
+            MakeGarbageCollected<CSSFontStyleInterpolationType>(property));
         break;
       case CSSPropertyID::kFontVariationSettings:
         applicable_types->push_back(
-            std::make_unique<CSSFontVariationSettingsInterpolationType>(
+            MakeGarbageCollected<CSSFontVariationSettingsInterpolationType>(
                 property));
         break;
       case blink::CSSPropertyID::kFontPalette:
         applicable_types->push_back(
-            std::make_unique<CSSFontPaletteInterpolationType>(property));
+            MakeGarbageCollected<CSSFontPaletteInterpolationType>(property));
         break;
       case CSSPropertyID::kVisibility:
         applicable_types->push_back(
-            std::make_unique<CSSVisibilityInterpolationType>(property));
+            MakeGarbageCollected<CSSVisibilityInterpolationType>(property));
         break;
       case CSSPropertyID::kClip:
         applicable_types->push_back(
-            std::make_unique<CSSClipInterpolationType>(property));
+            MakeGarbageCollected<CSSClipInterpolationType>(property));
         break;
       case CSSPropertyID::kOffsetRotate:
         applicable_types->push_back(
-            std::make_unique<CSSOffsetRotateInterpolationType>(property));
+            MakeGarbageCollected<CSSOffsetRotateInterpolationType>(property));
         break;
       case CSSPropertyID::kBackgroundPositionX:
       case CSSPropertyID::kBackgroundPositionY:
       case CSSPropertyID::kWebkitMaskPositionX:
       case CSSPropertyID::kWebkitMaskPositionY:
         applicable_types->push_back(
-            std::make_unique<CSSPositionAxisListInterpolationType>(property));
+            MakeGarbageCollected<CSSPositionAxisListInterpolationType>(
+                property));
         break;
       case CSSPropertyID::kObjectPosition:
       case CSSPropertyID::kOffsetAnchor:
       case CSSPropertyID::kOffsetPosition:
       case CSSPropertyID::kPerspectiveOrigin:
         applicable_types->push_back(
-            std::make_unique<CSSPositionInterpolationType>(property));
+            MakeGarbageCollected<CSSPositionInterpolationType>(property));
         break;
       case CSSPropertyID::kBorderBottomLeftRadius:
       case CSSPropertyID::kBorderBottomRightRadius:
       case CSSPropertyID::kBorderTopLeftRadius:
       case CSSPropertyID::kBorderTopRightRadius:
         applicable_types->push_back(
-            std::make_unique<CSSLengthPairInterpolationType>(property));
+            MakeGarbageCollected<CSSLengthPairInterpolationType>(property));
         break;
       case CSSPropertyID::kTranslate:
         applicable_types->push_back(
-            std::make_unique<CSSTranslateInterpolationType>(property));
+            MakeGarbageCollected<CSSTranslateInterpolationType>(property));
         break;
       case CSSPropertyID::kTransformOrigin:
         applicable_types->push_back(
-            std::make_unique<CSSTransformOriginInterpolationType>(property));
+            MakeGarbageCollected<CSSTransformOriginInterpolationType>(
+                property));
         break;
       case CSSPropertyID::kBackgroundSize:
       case CSSPropertyID::kMaskSize:
         applicable_types->push_back(
-            std::make_unique<CSSSizeListInterpolationType>(property));
+            MakeGarbageCollected<CSSSizeListInterpolationType>(property));
         break;
       case CSSPropertyID::kBorderImageOutset:
       case CSSPropertyID::kBorderImageWidth:
       case CSSPropertyID::kWebkitMaskBoxImageOutset:
       case CSSPropertyID::kWebkitMaskBoxImageWidth:
         applicable_types->push_back(
-            std::make_unique<CSSBorderImageLengthBoxInterpolationType>(
+            MakeGarbageCollected<CSSBorderImageLengthBoxInterpolationType>(
                 property));
         break;
       case CSSPropertyID::kScale:
         applicable_types->push_back(
-            std::make_unique<CSSScaleInterpolationType>(property));
+            MakeGarbageCollected<CSSScaleInterpolationType>(property));
         break;
       case CSSPropertyID::kFontSize:
         applicable_types->push_back(
-            std::make_unique<CSSFontSizeInterpolationType>(property));
+            MakeGarbageCollected<CSSFontSizeInterpolationType>(property));
         break;
       case CSSPropertyID::kFontSizeAdjust:
         applicable_types->push_back(
-            std::make_unique<CSSFontSizeAdjustInterpolationType>(property));
+            MakeGarbageCollected<CSSFontSizeAdjustInterpolationType>(property));
         break;
       case CSSPropertyID::kTextIndent:
         applicable_types->push_back(
-            std::make_unique<CSSTextIndentInterpolationType>(property));
+            MakeGarbageCollected<CSSTextIndentInterpolationType>(property));
         break;
       case CSSPropertyID::kBorderImageSlice:
       case CSSPropertyID::kWebkitMaskBoxImageSlice:
         applicable_types->push_back(
-            std::make_unique<CSSImageSliceInterpolationType>(property));
+            MakeGarbageCollected<CSSImageSliceInterpolationType>(property));
         break;
       case CSSPropertyID::kClipPath:
         applicable_types->push_back(
-            std::make_unique<CSSBasicShapeInterpolationType>(property));
+            MakeGarbageCollected<CSSBasicShapeInterpolationType>(property));
         applicable_types->push_back(
-            std::make_unique<CSSPathInterpolationType>(property));
+            MakeGarbageCollected<CSSPathInterpolationType>(property));
         applicable_types->push_back(
-            std::make_unique<CSSShapeInterpolationType>(property));
+            MakeGarbageCollected<CSSShapeInterpolationType>(property));
         break;
       case CSSPropertyID::kShapeOutside:
         applicable_types->push_back(
-            std::make_unique<CSSBasicShapeInterpolationType>(property));
+            MakeGarbageCollected<CSSBasicShapeInterpolationType>(property));
         break;
       case CSSPropertyID::kRotate:
         applicable_types->push_back(
-            std::make_unique<CSSRotateInterpolationType>(property));
+            MakeGarbageCollected<CSSRotateInterpolationType>(property));
         break;
       case CSSPropertyID::kBackdropFilter:
       case CSSPropertyID::kFilter:
         applicable_types->push_back(
-            std::make_unique<CSSFilterListInterpolationType>(property));
+            MakeGarbageCollected<CSSFilterListInterpolationType>(property));
         break;
       case CSSPropertyID::kTransform:
         applicable_types->push_back(
-            std::make_unique<CSSTransformInterpolationType>(property));
+            MakeGarbageCollected<CSSTransformInterpolationType>(property));
         break;
       case CSSPropertyID::kVariable:
         DCHECK_EQ(GetRegistration(registry_, property), nullptr);
         break;
       case CSSPropertyID::kObjectViewBox:
         applicable_types->push_back(
-            std::make_unique<CSSBasicShapeInterpolationType>(property));
+            MakeGarbageCollected<CSSBasicShapeInterpolationType>(property));
         break;
       case CSSPropertyID::kDisplay:
         applicable_types->push_back(
-            std::make_unique<CSSDisplayInterpolationType>(property));
+            MakeGarbageCollected<CSSDisplayInterpolationType>(property));
         break;
       case CSSPropertyID::kContentVisibility:
         applicable_types->push_back(
-            std::make_unique<CSSContentVisibilityInterpolationType>(property));
+            MakeGarbageCollected<CSSContentVisibilityInterpolationType>(
+                property));
         break;
       case CSSPropertyID::kOverlay:
         applicable_types->push_back(
-            std::make_unique<CSSOverlayInterpolationType>(property));
+            MakeGarbageCollected<CSSOverlayInterpolationType>(property));
         break;
       case CSSPropertyID::kScrollbarColor:
         applicable_types->push_back(
-            std::make_unique<CSSScrollbarColorInterpolationType>(property));
+            MakeGarbageCollected<CSSScrollbarColorInterpolationType>(property));
         break;
       default:
         DCHECK(!css_property.IsInterpolable());
@@ -452,64 +460,64 @@
   }
 
   applicable_types->push_back(
-      std::make_unique<CSSDefaultInterpolationType>(property));
+      MakeGarbageCollected<CSSDefaultInterpolationType>(property));
 
-  auto add_result =
-      applicable_types_map.insert(property, std::move(applicable_types));
-  return *add_result.stored_value->value;
+  auto add_result = applicable_types_map.insert(property, applicable_types);
+  return add_result.stored_value->value;
 }
 
 size_t InterpolationTypesMap::Version() const {
   return registry_ ? registry_->Version() : 0;
 }
 
-static std::unique_ptr<CSSInterpolationType>
-CreateInterpolationTypeForCSSSyntax(const CSSSyntaxComponent syntax,
-                                    PropertyHandle property,
-                                    const PropertyRegistration& registration) {
+static const CSSInterpolationType* CreateInterpolationTypeForCSSSyntax(
+    const CSSSyntaxComponent syntax,
+    PropertyHandle property,
+    const PropertyRegistration& registration) {
   switch (syntax.GetType()) {
     case CSSSyntaxType::kAngle:
-      return std::make_unique<CSSAngleInterpolationType>(property,
-                                                         &registration);
+      return MakeGarbageCollected<CSSAngleInterpolationType>(property,
+                                                             &registration);
     case CSSSyntaxType::kColor:
-      return std::make_unique<CSSColorInterpolationType>(property,
-                                                         &registration);
+      return MakeGarbageCollected<CSSColorInterpolationType>(property,
+                                                             &registration);
     case CSSSyntaxType::kLength:
-      return std::make_unique<CSSCustomLengthInterpolationType>(property,
-                                                                &registration);
+      return MakeGarbageCollected<CSSCustomLengthInterpolationType>(
+          property, &registration);
     case CSSSyntaxType::kLengthPercentage:
-      return std::make_unique<CSSLengthInterpolationType>(property,
-                                                          &registration);
+      return MakeGarbageCollected<CSSLengthInterpolationType>(property,
+                                                              &registration);
     case CSSSyntaxType::kPercentage:
-      return std::make_unique<CSSPercentageInterpolationType>(property,
-                                                              &registration);
+      return MakeGarbageCollected<CSSPercentageInterpolationType>(
+          property, &registration);
     case CSSSyntaxType::kNumber:
-      return std::make_unique<CSSNumberInterpolationType>(property,
-                                                          &registration);
-    case CSSSyntaxType::kResolution:
-      return std::make_unique<CSSResolutionInterpolationType>(property,
+      return MakeGarbageCollected<CSSNumberInterpolationType>(property,
                                                               &registration);
+    case CSSSyntaxType::kResolution:
+      return MakeGarbageCollected<CSSResolutionInterpolationType>(
+          property, &registration);
     case CSSSyntaxType::kTime:
-      return std::make_unique<CSSTimeInterpolationType>(property,
-                                                        &registration);
+      return MakeGarbageCollected<CSSTimeInterpolationType>(property,
+                                                            &registration);
     case CSSSyntaxType::kImage:
       // TODO(andruud): Implement smooth interpolation for gradients.
       return nullptr;
     case CSSSyntaxType::kInteger:
-      return std::make_unique<CSSNumberInterpolationType>(property,
-                                                          &registration, true);
+      return MakeGarbageCollected<CSSNumberInterpolationType>(
+          property, &registration, true);
     case CSSSyntaxType::kTransformFunction:
       if (!syntax.IsRepeatable() ||
           syntax.GetRepeat() == CSSSyntaxRepeat::kCommaSeparated) {
         // <transform-function> needs an interpolation type different from
         // <transform-function>+ and <transform-list> as it can only use a
         // single function representation for interpolation and composition.
-        return std::make_unique<CSSCustomTransformFunctionInterpolationType>(
-            property, &registration);
+        return MakeGarbageCollected<
+            CSSCustomTransformFunctionInterpolationType>(property,
+                                                         &registration);
       }
       [[fallthrough]];
     case CSSSyntaxType::kTransformList:
-      return std::make_unique<CSSCustomTransformInterpolationType>(
+      return MakeGarbageCollected<CSSCustomTransformInterpolationType>(
           property, &registration);
     case CSSSyntaxType::kCustomIdent:
     case CSSSyntaxType::kIdent:
@@ -525,19 +533,19 @@
   }
 }
 
-InterpolationTypes InterpolationTypesMap::CreateInterpolationTypesForCSSSyntax(
+InterpolationTypes* InterpolationTypesMap::CreateInterpolationTypesForCSSSyntax(
     const AtomicString& property_name,
     const CSSSyntaxDefinition& definition,
     const PropertyRegistration& registration) {
   PropertyHandle property(property_name);
-  InterpolationTypes result;
+  InterpolationTypes* result = MakeGarbageCollected<InterpolationTypes>();
 
   // All custom properties may encounter var() dependency cycles.
-  result.push_back(
-      std::make_unique<CSSVarCycleInterpolationType>(property, registration));
+  result->push_back(MakeGarbageCollected<CSSVarCycleInterpolationType>(
+      property, registration));
 
   for (const CSSSyntaxComponent& component : definition.Components()) {
-    std::unique_ptr<CSSInterpolationType> interpolation_type =
+    const CSSInterpolationType* interpolation_type =
         CreateInterpolationTypeForCSSSyntax(component, property, registration);
 
     if (!interpolation_type) {
@@ -547,14 +555,15 @@
     if (component.IsRepeatable() &&
         (component.GetType() != CSSSyntaxType::kTransformFunction ||
          component.GetRepeat() != CSSSyntaxRepeat::kSpaceSeparated)) {
-      interpolation_type = std::make_unique<CSSCustomListInterpolationType>(
+      interpolation_type = MakeGarbageCollected<CSSCustomListInterpolationType>(
           property, &registration, std::move(interpolation_type),
           component.GetType(), component.GetRepeat());
     }
 
-    result.push_back(std::move(interpolation_type));
+    result->push_back(std::move(interpolation_type));
   }
-  result.push_back(std::make_unique<CSSDefaultInterpolationType>(property));
+  result->push_back(
+      MakeGarbageCollected<CSSDefaultInterpolationType>(property));
   return result;
 }
 
diff --git a/third_party/blink/renderer/core/animation/interpolation_types_map.h b/third_party/blink/renderer/core/animation/interpolation_types_map.h
index 482ad3c3..d19153e 100644
--- a/third_party/blink/renderer/core/animation/interpolation_types_map.h
+++ b/third_party/blink/renderer/core/animation/interpolation_types_map.h
@@ -4,9 +4,6 @@
 
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_INTERPOLATION_TYPES_MAP_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_INTERPOLATION_TYPES_MAP_H_
-
-#include <memory>
-
 #include "third_party/blink/renderer/core/animation/interpolation_type.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -19,7 +16,7 @@
 class PropertyRegistry;
 class PropertyRegistration;
 
-using InterpolationTypes = Vector<std::unique_ptr<const InterpolationType>>;
+using InterpolationTypes = HeapVector<Member<const InterpolationType>>;
 
 class CORE_EXPORT InterpolationTypesMap {
   STACK_ALLOCATED();
@@ -28,10 +25,10 @@
   InterpolationTypesMap(const PropertyRegistry* registry,
                         const Document& document);
 
-  const InterpolationTypes& Get(const PropertyHandle&) const;
+  const InterpolationTypes* Get(const PropertyHandle&) const;
   size_t Version() const;
 
-  static InterpolationTypes CreateInterpolationTypesForCSSSyntax(
+  static InterpolationTypes* CreateInterpolationTypesForCSSSyntax(
       const AtomicString& property_name,
       const CSSSyntaxDefinition&,
       const PropertyRegistration&);
diff --git a/third_party/blink/renderer/core/animation/interpolation_types_map_test.cc b/third_party/blink/renderer/core/animation/interpolation_types_map_test.cc
index a944bf08..1d2000b5 100644
--- a/third_party/blink/renderer/core/animation/interpolation_types_map_test.cc
+++ b/third_party/blink/renderer/core/animation/interpolation_types_map_test.cc
@@ -38,13 +38,13 @@
   InterpolationTypesMap map2(registry, *document2);
 
   PropertyHandle handle(property_name);
-  auto& types1 = map1.Get(handle);
-  auto& types2 = map2.Get(handle);
-  EXPECT_NE(&types1, &types2);
-  EXPECT_EQ(types1.size(), 1u);
+  const auto* types1 = map1.Get(handle);
+  const auto* types2 = map2.Get(handle);
+  EXPECT_NE(types1, types2);
+  EXPECT_EQ(types1->size(), 1u);
 
-  auto& types1_1 = map1.Get(handle);
-  EXPECT_EQ(&types1, &types1_1);
+  const auto* types1_1 = map1.Get(handle);
+  EXPECT_EQ(types1, types1_1);
 
   execution_context->NotifyContextDestroyed();
 }
diff --git a/third_party/blink/renderer/core/animation/invalidatable_interpolation.cc b/third_party/blink/renderer/core/animation/invalidatable_interpolation.cc
index 0eddb6c8..108a808 100644
--- a/third_party/blink/renderer/core/animation/invalidatable_interpolation.cc
+++ b/third_party/blink/renderer/core/animation/invalidatable_interpolation.cc
@@ -5,8 +5,10 @@
 #include "third_party/blink/renderer/core/animation/invalidatable_interpolation.h"
 
 #include <memory>
+
 #include "third_party/blink/renderer/core/animation/css_interpolation_environment.h"
 #include "third_party/blink/renderer/core/animation/string_keyframe.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 
@@ -29,10 +31,10 @@
 InvalidatableInterpolation::MaybeConvertPairwise(
     const CSSInterpolationEnvironment& environment,
     const UnderlyingValueOwner& underlying_value_owner) const {
-  for (const auto& interpolation_type : *interpolation_types_) {
+  for (const InterpolationType* interpolation_type : *interpolation_types_) {
     if ((start_keyframe_->IsNeutral() || end_keyframe_->IsNeutral()) &&
         (!underlying_value_owner ||
-         underlying_value_owner.GetType() != *interpolation_type)) {
+         underlying_value_owner.GetType() != interpolation_type)) {
       continue;
     }
     ConversionCheckers conversion_checkers;
@@ -40,10 +42,10 @@
         interpolation_type->MaybeConvertPairwise(
             *start_keyframe_, *end_keyframe_, environment,
             underlying_value_owner.Value(), conversion_checkers);
-    AddConversionCheckers(*interpolation_type, conversion_checkers);
+    AddConversionCheckers(interpolation_type, conversion_checkers);
     if (result) {
       return MakeGarbageCollected<PairwisePrimitiveInterpolation>(
-          *interpolation_type, std::move(result.start_interpolable_value),
+          interpolation_type, std::move(result.start_interpolable_value),
           std::move(result.end_interpolable_value),
           std::move(result.non_interpolable_value));
     }
@@ -58,19 +60,19 @@
   if (keyframe.IsNeutral() && !underlying_value_owner) {
     return nullptr;
   }
-  for (const auto& interpolation_type : *interpolation_types_) {
+  for (const InterpolationType* interpolation_type : *interpolation_types_) {
     if (keyframe.IsNeutral() &&
-        underlying_value_owner.GetType() != *interpolation_type) {
+        underlying_value_owner.GetType() != interpolation_type) {
       continue;
     }
     ConversionCheckers conversion_checkers;
     InterpolationValue result = interpolation_type->MaybeConvertSingle(
         keyframe, environment, underlying_value_owner.Value(),
         conversion_checkers);
-    AddConversionCheckers(*interpolation_type, conversion_checkers);
+    AddConversionCheckers(interpolation_type, conversion_checkers);
     if (result) {
       return MakeGarbageCollected<TypedInterpolationValue>(
-          *interpolation_type, std::move(result.interpolable_value),
+          interpolation_type, std::move(result.interpolable_value),
           std::move(result.non_interpolable_value));
     }
   }
@@ -79,7 +81,7 @@
 }
 
 void InvalidatableInterpolation::AddConversionCheckers(
-    const InterpolationType& type,
+    const InterpolationType* type,
     ConversionCheckers& conversion_checkers) const {
   for (wtf_size_t i = 0; i < conversion_checkers.size(); i++) {
     conversion_checkers[i]->SetType(type);
@@ -90,12 +92,12 @@
 TypedInterpolationValue*
 InvalidatableInterpolation::MaybeConvertUnderlyingValue(
     const CSSInterpolationEnvironment& environment) const {
-  for (const auto& interpolation_type : *interpolation_types_) {
+  for (const InterpolationType* interpolation_type : *interpolation_types_) {
     InterpolationValue result =
         interpolation_type->MaybeConvertUnderlyingValue(environment);
     if (result) {
       return MakeGarbageCollected<TypedInterpolationValue>(
-          *interpolation_type, std::move(result.interpolable_value),
+          interpolation_type, std::move(result.interpolable_value),
           std::move(result.non_interpolable_value));
     }
   }
@@ -182,7 +184,7 @@
   if (interpolation_types_ && interpolation_types_version_ == latest_version) {
     return;
   }
-  const InterpolationTypes* latest_interpolation_types = &map.Get(property_);
+  const InterpolationTypes* latest_interpolation_types = map.Get(property_);
   DCHECK(latest_interpolation_types);
   if (interpolation_types_ != latest_interpolation_types) {
     ClearConversionCache(environment);
@@ -245,9 +247,9 @@
     if (interpolations.size() == 1) {
       if (first_value) {
         first_interpolation.SetFlagIfInheritUsed(environment);
-        first_value->GetType().Apply(first_value->GetInterpolableValue(),
-                                     first_value->GetNonInterpolableValue(),
-                                     environment);
+        first_value->GetType()->Apply(first_value->GetInterpolableValue(),
+                                      first_value->GetNonInterpolableValue(),
+                                      environment);
       }
       return;
     }
@@ -276,14 +278,14 @@
         underlying_value_owner.GetType() != current_value->GetType()) {
       underlying_value_owner.Set(current_value);
     } else {
-      current_value->GetType().Composite(
+      current_value->GetType()->Composite(
           underlying_value_owner, current_interpolation.UnderlyingFraction(),
           current_value->Value(), current_interpolation.current_fraction_);
     }
   }
 
   if (should_apply && underlying_value_owner) {
-    underlying_value_owner.GetType().Apply(
+    underlying_value_owner.GetType()->Apply(
         *underlying_value_owner.Value().interpolable_value,
         underlying_value_owner.Value().non_interpolable_value.Get(),
         environment);
diff --git a/third_party/blink/renderer/core/animation/invalidatable_interpolation.h b/third_party/blink/renderer/core/animation/invalidatable_interpolation.h
index 3b638b1..dee31ba 100644
--- a/third_party/blink/renderer/core/animation/invalidatable_interpolation.h
+++ b/third_party/blink/renderer/core/animation/invalidatable_interpolation.h
@@ -37,7 +37,6 @@
                              PropertySpecificKeyframe* end_keyframe)
       : Interpolation(),
         property_(property),
-        interpolation_types_(nullptr),
         interpolation_types_version_(0),
         start_keyframe_(start_keyframe),
         end_keyframe_(end_keyframe),
@@ -57,6 +56,7 @@
   }
 
   void Trace(Visitor* visitor) const override {
+    visitor->Trace(interpolation_types_);
     visitor->Trace(start_keyframe_);
     visitor->Trace(end_keyframe_);
     visitor->Trace(cached_pair_conversion_);
@@ -85,13 +85,13 @@
       const PropertySpecificKeyframe&,
       const CSSInterpolationEnvironment&,
       const UnderlyingValueOwner&) const;
-  void AddConversionCheckers(const InterpolationType&,
+  void AddConversionCheckers(const InterpolationType*,
                              ConversionCheckers&) const;
   void SetFlagIfInheritUsed(CSSInterpolationEnvironment&) const;
   double UnderlyingFraction() const;
 
   const PropertyHandle property_;
-  mutable const InterpolationTypes* interpolation_types_;
+  mutable Member<const InterpolationTypes> interpolation_types_;
   mutable size_t interpolation_types_version_;
   Member<PropertySpecificKeyframe> start_keyframe_;
   Member<PropertySpecificKeyframe> end_keyframe_;
diff --git a/third_party/blink/renderer/core/animation/list_interpolation_functions.cc b/third_party/blink/renderer/core/animation/list_interpolation_functions.cc
index 485f963..ac2f6ec 100644
--- a/third_party/blink/renderer/core/animation/list_interpolation_functions.cc
+++ b/third_party/blink/renderer/core/animation/list_interpolation_functions.cc
@@ -340,7 +340,7 @@
 void ListInterpolationFunctions::Composite(
     UnderlyingValueOwner& underlying_value_owner,
     double underlying_fraction,
-    const InterpolationType& type,
+    const InterpolationType* type,
     const InterpolationValue& value,
     LengthMatchingStrategy length_matching_strategy,
     InterpolableValuesAreCompatibleCallback interpolable_values_are_compatible,
diff --git a/third_party/blink/renderer/core/animation/list_interpolation_functions.h b/third_party/blink/renderer/core/animation/list_interpolation_functions.h
index c755337..a5af615 100644
--- a/third_party/blink/renderer/core/animation/list_interpolation_functions.h
+++ b/third_party/blink/renderer/core/animation/list_interpolation_functions.h
@@ -64,7 +64,7 @@
                              const NonInterpolableValue*)>;
   static void Composite(UnderlyingValueOwner&,
                         double underlying_fraction,
-                        const InterpolationType&,
+                        const InterpolationType*,
                         const InterpolationValue&,
                         LengthMatchingStrategy,
                         InterpolableValuesAreCompatibleCallback,
diff --git a/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc b/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc
index 2bd232a..c29cade 100644
--- a/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc
+++ b/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/animation/css_number_interpolation_type.h"
 #include "third_party/blink/renderer/core/animation/interpolation_value.h"
 #include "third_party/blink/renderer/core/animation/underlying_value.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/properties/longhands.h"
 #include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -208,7 +209,8 @@
   auto list2 = CreateInterpolableList({{1.0, 1}, {2.0, 2}, {3.0, 3}});
 
   PropertyHandle property_handle(GetCSSPropertyZIndex());
-  CSSNumberInterpolationType interpolation_type(property_handle);
+  CSSNumberInterpolationType* interpolation_type(
+      MakeGarbageCollected<CSSNumberInterpolationType>(property_handle));
   UnderlyingValueOwner owner;
   owner.Set(interpolation_type, std::move(list1));
 
@@ -235,7 +237,8 @@
   auto list2 = CreateInterpolableList({4.0, 5.0});
 
   PropertyHandle property_handle(GetCSSPropertyZIndex());
-  CSSNumberInterpolationType interpolation_type(property_handle);
+  CSSNumberInterpolationType* interpolation_type(
+      MakeGarbageCollected<CSSNumberInterpolationType>(property_handle));
   UnderlyingValueOwner owner;
   owner.Set(interpolation_type, std::move(list1));
 
@@ -264,7 +267,8 @@
       {true, false, true});
 
   PropertyHandle property_handle(GetCSSPropertyZIndex());
-  CSSNumberInterpolationType interpolation_type(property_handle);
+  CSSNumberInterpolationType* interpolation_type(
+      MakeGarbageCollected<CSSNumberInterpolationType>(property_handle));
   UnderlyingValueOwner owner;
   owner.Set(interpolation_type, std::move(list1));
 
@@ -294,7 +298,8 @@
   auto list2 = CreateInterpolableList({{4.0, 1}, {5.0, 4}, {6.0, 3}});
 
   PropertyHandle property_handle(GetCSSPropertyZIndex());
-  CSSNumberInterpolationType interpolation_type(property_handle);
+  CSSNumberInterpolationType* interpolation_type(
+      MakeGarbageCollected<CSSNumberInterpolationType>(property_handle));
   UnderlyingValueOwner owner;
   owner.Set(interpolation_type, std::move(list1));
 
diff --git a/third_party/blink/renderer/core/animation/path_interpolation_functions.cc b/third_party/blink/renderer/core/animation/path_interpolation_functions.cc
index cab3c0bd..6974d06 100644
--- a/third_party/blink/renderer/core/animation/path_interpolation_functions.cc
+++ b/third_party/blink/renderer/core/animation/path_interpolation_functions.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/animation/css_interpolation_environment.h"
 #include "third_party/blink/renderer/core/animation/interpolated_svg_path_source.h"
 #include "third_party/blink/renderer/core/animation/svg_path_seg_interpolation_functions.h"
+#include "third_party/blink/renderer/core/animation/underlying_value_owner.h"
 #include "third_party/blink/renderer/core/css/css_path_value.h"
 #include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
 #include "third_party/blink/renderer/core/svg/svg_path.h"
@@ -195,7 +196,7 @@
 void PathInterpolationFunctions::Composite(
     UnderlyingValueOwner& underlying_value_owner,
     double underlying_fraction,
-    const InterpolationType& type,
+    const InterpolationType* type,
     const InterpolationValue& value) {
   const auto& list = To<InterpolableList>(*value.interpolable_value);
   // TODO(crbug.com/325821290): Avoid InterpolableNumber here.
diff --git a/third_party/blink/renderer/core/animation/path_interpolation_functions.h b/third_party/blink/renderer/core/animation/path_interpolation_functions.h
index ccd65e77..a5b05c3 100644
--- a/third_party/blink/renderer/core/animation/path_interpolation_functions.h
+++ b/third_party/blink/renderer/core/animation/path_interpolation_functions.h
@@ -25,7 +25,7 @@
 
   static void Composite(UnderlyingValueOwner&,
                         double underlying_fraction,
-                        const InterpolationType&,
+                        const InterpolationType*,
                         const InterpolationValue&);
 
   static InterpolationValue ConvertValue(const StylePath*,
diff --git a/third_party/blink/renderer/core/animation/primitive_interpolation.h b/third_party/blink/renderer/core/animation/primitive_interpolation.h
index 3344d83..adf447d 100644
--- a/third_party/blink/renderer/core/animation/primitive_interpolation.h
+++ b/third_party/blink/renderer/core/animation/primitive_interpolation.h
@@ -46,7 +46,7 @@
 class PairwisePrimitiveInterpolation : public PrimitiveInterpolation {
  public:
   PairwisePrimitiveInterpolation(
-      const InterpolationType& type,
+      const InterpolationType* type,
       InterpolableValue* start,
       InterpolableValue* end,
       const NonInterpolableValue* non_interpolable_value)
@@ -60,7 +60,7 @@
 
   ~PairwisePrimitiveInterpolation() override = default;
 
-  const InterpolationType& GetType() const { return type_; }
+  const InterpolationType* GetType() const { return type_; }
 
   TypedInterpolationValue* InitialValue() const {
     return MakeGarbageCollected<TypedInterpolationValue>(
@@ -69,6 +69,7 @@
 
   void Trace(Visitor* v) const override {
     PrimitiveInterpolation::Trace(v);
+    v->Trace(type_);
     v->Trace(start_);
     v->Trace(end_);
     v->Trace(non_interpolable_value_);
@@ -78,7 +79,7 @@
   void InterpolateValue(double fraction,
                         Member<TypedInterpolationValue>& result) const final {
     DCHECK(result);
-    DCHECK_EQ(&result->GetType(), &type_);
+    DCHECK_EQ(result->GetType(), type_);
     DCHECK_EQ(result->GetNonInterpolableValue(), non_interpolable_value_.Get());
     start_->AssertCanInterpolateWith(*end_);
     start_->Interpolate(*end_, fraction,
@@ -91,7 +92,7 @@
     return Blend(start, end, fraction);
   }
 
-  const InterpolationType& type_;
+  Member<const InterpolationType> type_;
   Member<InterpolableValue> start_;
   Member<InterpolableValue> end_;
   Member<const NonInterpolableValue> non_interpolable_value_;
diff --git a/third_party/blink/renderer/core/animation/transition_interpolation.cc b/third_party/blink/renderer/core/animation/transition_interpolation.cc
index 6a594cb..bb12117 100644
--- a/third_party/blink/renderer/core/animation/transition_interpolation.cc
+++ b/third_party/blink/renderer/core/animation/transition_interpolation.cc
@@ -3,9 +3,11 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/core/animation/transition_interpolation.h"
+
 #include <memory>
 
 #include "third_party/blink/renderer/core/animation/css/compositor_keyframe_value.h"
+#include "third_party/blink/renderer/core/animation/typed_interpolation_value.h"
 
 namespace blink {
 
@@ -42,8 +44,8 @@
 
 void TransitionInterpolation::Apply(
     CSSInterpolationEnvironment& environment) const {
-  type_.Apply(CurrentInterpolableValue(), CurrentNonInterpolableValue(),
-              environment);
+  type_->Apply(CurrentInterpolableValue(), CurrentNonInterpolableValue(),
+               environment);
 }
 
 TypedInterpolationValue* TransitionInterpolation::GetInterpolatedValue() const {
diff --git a/third_party/blink/renderer/core/animation/transition_interpolation.h b/third_party/blink/renderer/core/animation/transition_interpolation.h
index ee726967..f8e68f08 100644
--- a/third_party/blink/renderer/core/animation/transition_interpolation.h
+++ b/third_party/blink/renderer/core/animation/transition_interpolation.h
@@ -16,6 +16,7 @@
 namespace blink {
 
 class CSSInterpolationEnvironment;
+class TypedInterpolationValue;
 
 // See the documentation of Interpolation for general information about this
 // class hierarchy.
@@ -42,7 +43,7 @@
 class CORE_EXPORT TransitionInterpolation : public Interpolation {
  public:
   TransitionInterpolation(const PropertyHandle& property,
-                          const InterpolationType& type,
+                          const InterpolationType* type,
                           InterpolationValue&& start,
                           InterpolationValue&& end,
                           CompositorKeyframeValue* compositor_start,
@@ -51,7 +52,7 @@
         type_(type),
         start_(std::move(start)),
         end_(std::move(end)),
-        merge_(type.MaybeMergeSingles(start_.Clone(), end_.Clone())),
+        merge_(type->MaybeMergeSingles(start_.Clone(), end_.Clone())),
         compositor_start_(compositor_start),
         compositor_end_(compositor_end) {
     // Incredibly speculative CHECKs, to try and get any insight on
@@ -83,6 +84,7 @@
   void Interpolate(int iteration, double fraction) final;
 
   void Trace(Visitor* visitor) const override {
+    visitor->Trace(type_);
     visitor->Trace(start_);
     visitor->Trace(end_);
     visitor->Trace(merge_);
@@ -97,7 +99,7 @@
   const NonInterpolableValue* CurrentNonInterpolableValue() const;
 
   const PropertyHandle property_;
-  const InterpolationType& type_;
+  Member<const InterpolationType> type_;
   const InterpolationValue start_;
   const InterpolationValue end_;
   const PairwiseInterpolationValue merge_;
diff --git a/third_party/blink/renderer/core/animation/transition_keyframe.cc b/third_party/blink/renderer/core/animation/transition_keyframe.cc
index a93fa38..cb017a6 100644
--- a/third_party/blink/renderer/core/animation/transition_keyframe.cc
+++ b/third_party/blink/renderer/core/animation/transition_keyframe.cc
@@ -67,8 +67,8 @@
   state.EnsureParentStyle();
   InterpolationTypesMap map(document.GetPropertyRegistry(), document);
   CSSInterpolationEnvironment environment(map, state);
-  value_->GetType().Apply(value_->GetInterpolableValue(),
-                          value_->GetNonInterpolableValue(), environment);
+  value_->GetType()->Apply(value_->GetInterpolableValue(),
+                           value_->GetNonInterpolableValue(), environment);
 
   const ComputedStyle* style = state.TakeStyle();
   String property_value =
diff --git a/third_party/blink/renderer/core/animation/typed_interpolation_value.h b/third_party/blink/renderer/core/animation/typed_interpolation_value.h
index 2ebaadf..9224f29 100644
--- a/third_party/blink/renderer/core/animation/typed_interpolation_value.h
+++ b/third_party/blink/renderer/core/animation/typed_interpolation_value.h
@@ -6,20 +6,19 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_TYPED_INTERPOLATION_VALUE_H_
 
 #include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/core/animation/interpolation_type.h"
 #include "third_party/blink/renderer/core/animation/interpolation_value.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
 
-class InterpolationType;
-
 // Represents an interpolated value between an adjacent pair of
 // PropertySpecificKeyframes.
 class TypedInterpolationValue
     : public GarbageCollected<TypedInterpolationValue> {
  public:
   TypedInterpolationValue(
-      const InterpolationType& type,
+      const InterpolationType* type,
       InterpolableValue* interpolable_value,
       const NonInterpolableValue* non_interpolable_value = nullptr)
       : type_(type), value_(interpolable_value, non_interpolable_value) {
@@ -32,7 +31,7 @@
         type_, copy.interpolable_value, copy.non_interpolable_value);
   }
 
-  const InterpolationType& GetType() const { return type_; }
+  const InterpolationType* GetType() const { return type_; }
   const InterpolableValue& GetInterpolableValue() const {
     return *value_.interpolable_value;
   }
@@ -43,10 +42,13 @@
 
   InterpolationValue& MutableValue() { return value_; }
 
-  void Trace(Visitor* v) const { v->Trace(value_); }
+  void Trace(Visitor* v) const {
+    v->Trace(type_);
+    v->Trace(value_);
+  }
 
  private:
-  const InterpolationType& type_;
+  Member<const InterpolationType> type_;
   InterpolationValue value_;
 };
 
diff --git a/third_party/blink/renderer/core/animation/underlying_value_owner.cc b/third_party/blink/renderer/core/animation/underlying_value_owner.cc
index 2f801b9..ac080dc 100644
--- a/third_party/blink/renderer/core/animation/underlying_value_owner.cc
+++ b/third_party/blink/renderer/core/animation/underlying_value_owner.cc
@@ -48,10 +48,10 @@
   value_ = nullptr;
 }
 
-void UnderlyingValueOwner::Set(const InterpolationType& type,
+void UnderlyingValueOwner::Set(const InterpolationType* type,
                                const InterpolationValue& value) {
   DCHECK(value);
-  type_ = &type;
+  type_ = type;
   // By clearing |value_owner_| we will perform a copy before attempting to
   // mutate |value_|, thus upholding the const contract for this instance of
   // interpolationValue.
@@ -59,10 +59,10 @@
   value_ = &value;
 }
 
-void UnderlyingValueOwner::Set(const InterpolationType& type,
+void UnderlyingValueOwner::Set(const InterpolationType* type,
                                InterpolationValue&& value) {
   DCHECK(value);
-  type_ = &type;
+  type_ = type;
   value_owner_ = std::move(value);
   value_ = &value_owner_;
 }
diff --git a/third_party/blink/renderer/core/animation/underlying_value_owner.h b/third_party/blink/renderer/core/animation/underlying_value_owner.h
index 0e49867..7fe8b28b 100644
--- a/third_party/blink/renderer/core/animation/underlying_value_owner.h
+++ b/third_party/blink/renderer/core/animation/underlying_value_owner.h
@@ -39,16 +39,16 @@
   const NonInterpolableValue* GetNonInterpolableValue() const final;
   void SetNonInterpolableValue(const NonInterpolableValue*) final;
 
-  const InterpolationType& GetType() const {
+  const InterpolationType* GetType() const {
     DCHECK(type_);
-    return *type_;
+    return type_;
   }
 
   const InterpolationValue& Value() const;
 
   void Set(std::nullptr_t);
-  void Set(const InterpolationType&, const InterpolationValue&);
-  void Set(const InterpolationType&, InterpolationValue&&);
+  void Set(const InterpolationType*, const InterpolationValue&);
+  void Set(const InterpolationType*, InterpolationValue&&);
   void Set(TypedInterpolationValue*);
   void Set(const TypedInterpolationValue*);
 
diff --git a/third_party/blink/renderer/core/css/properties/css_color_function_parser.cc b/third_party/blink/renderer/core/css/properties/css_color_function_parser.cc
index ed41a802..3e27db91 100644
--- a/third_party/blink/renderer/core/css/properties/css_color_function_parser.cc
+++ b/third_party/blink/renderer/core/css/properties/css_color_function_parser.cc
@@ -447,30 +447,64 @@
   }
 }
 
+namespace {
+
+const CSSNumericLiteralValue* GetNumericLiteralValue(const CSSValue* value) {
+  auto* literal_value = DynamicTo<CSSNumericLiteralValue>(value);
+  // We can reach here with calc(NumericLiteral) as per ChannelIsResolvable.
+  if (auto* math_value = DynamicTo<CSSMathFunctionValue>(value)) {
+    DCHECK(math_value->ExpressionNode()->IsNumericLiteral());
+    literal_value = MakeGarbageCollected<CSSNumericLiteralValue>(
+        math_value->ExpressionNode()->DoubleValue(),
+        math_value->ExpressionNode()->ResolvedUnitType());
+  }
+  return literal_value;
+}
+
+double ResolveColorChannelForNumericLiteral(
+    const CSSNumericLiteralValue* value,
+    ColorFunctionParser::ChannelType channel_type,
+    double percentage_base) {
+  using ChannelType = ColorFunctionParser::ChannelType;
+  switch (channel_type) {
+    case ChannelType::kNumber:
+      if (value->IsAngle()) {
+        return value->ComputeDegrees();
+      } else {
+        return value->GetDoubleValueWithoutClamping();
+      }
+    case ChannelType::kPercentage:
+      return (value->GetDoubleValue() / 100.0) * percentage_base;
+    default:
+      NOTREACHED();
+  }
+}
+
+double ResolveAlphaForNumericLiteral(
+    const CSSNumericLiteralValue* value,
+    ColorFunctionParser::ChannelType channel_type) {
+  using ChannelType = ColorFunctionParser::ChannelType;
+  switch (channel_type) {
+    case ChannelType::kNumber:
+      return ClampTo<double>(value->GetDoubleValue(), 0.0, 1.0);
+    case ChannelType::kPercentage:
+      return ClampTo<double>(value->GetDoubleValue() / 100.0, 0.0, 1.0);
+    default:
+      NOTREACHED();
+  }
+}
+
+}  // namespace
+
 double ColorFunctionParser::ResolveColorChannel(
     const CSSValue* value,
     ChannelType channel_type,
     double percentage_base,
     const CSSColorChannelMap& color_channel_map) {
-  if (const CSSPrimitiveValue* primitive_value =
-          DynamicTo<CSSPrimitiveValue>(value)) {
-    switch (channel_type) {
-      case ChannelType::kNumber:
-        if (primitive_value->IsAngle()) {
-          return primitive_value->ComputeDegrees();
-        } else {
-          return primitive_value->GetDoubleValueWithoutClamping();
-        }
-      case ChannelType::kPercentage:
-        return (primitive_value->GetDoubleValue() / 100.0) * percentage_base;
-      case ChannelType::kRelative:
-        // Proceed to relative channel value resolution below.
-        break;
-      default:
-        NOTREACHED();
-    }
+  if (value->IsPrimitiveValue() && channel_type != ChannelType::kRelative) {
+    return ResolveColorChannelForNumericLiteral(GetNumericLiteralValue(value),
+                                                channel_type, percentage_base);
   }
-
   return ResolveRelativeChannelValue(value, channel_type, percentage_base,
                                      color_channel_map);
 }
@@ -479,22 +513,10 @@
     const CSSValue* value,
     ChannelType channel_type,
     const CSSColorChannelMap& color_channel_map) {
-  if (const CSSPrimitiveValue* primitive_value =
-          DynamicTo<CSSPrimitiveValue>(value)) {
-    switch (channel_type) {
-      case ChannelType::kNumber:
-        return ClampTo<double>(primitive_value->GetDoubleValue(), 0.0, 1.0);
-      case ChannelType::kPercentage:
-        return ClampTo<double>(primitive_value->GetDoubleValue() / 100.0, 0.0,
-                               1.0);
-      case ChannelType::kRelative:
-        // Proceed to relative channel value resolution below.
-        break;
-      default:
-        NOTREACHED();
-    }
+  if (value->IsPrimitiveValue() && channel_type != ChannelType::kRelative) {
+    return ResolveAlphaForNumericLiteral(GetNumericLiteralValue(value),
+                                         channel_type);
   }
-
   return ResolveRelativeChannelValue(
       value, channel_type, /*percentage_base=*/1.0, color_channel_map);
 }
@@ -514,20 +536,18 @@
     }
   }
 
-  if (const CSSMathFunctionValue* calc_value =
-          DynamicTo<CSSMathFunctionValue>(value)) {
-    switch (calc_value->Category()) {
-      case kCalcNumber:
-        return calc_value->DoubleValue();
-      case kCalcPercent:
-        return (CSSValueClampingUtils::ClampDouble(calc_value->DoubleValue()) /
-                100) *
-               percentage_base;
-      case kCalcAngle:
-        return calc_value->ComputeDegrees();
-      default:
-        NOTREACHED();
-    }
+  auto* literal_value = GetNumericLiteralValue(value);
+  switch (To<CSSMathFunctionValue>(value)->Category()) {
+    case kCalcNumber:
+      return literal_value->DoubleValue();
+    case kCalcPercent:
+      return (CSSValueClampingUtils::ClampDouble(literal_value->DoubleValue()) /
+              100) *
+             percentage_base;
+    case kCalcAngle:
+      return literal_value->ComputeDegrees();
+    default:
+      NOTREACHED();
   }
 
   NOTREACHED();
diff --git a/third_party/blink/renderer/core/css/property_registration.cc b/third_party/blink/renderer/core/css/property_registration.cc
index 28a6adbd..d3ee104f 100644
--- a/third_party/blink/renderer/core/css/property_registration.cc
+++ b/third_party/blink/renderer/core/css/property_registration.cc
@@ -75,6 +75,7 @@
 void PropertyRegistration::Trace(Visitor* visitor) const {
   visitor->Trace(initial_);
   visitor->Trace(property_rule_);
+  visitor->Trace(interpolation_types_);
 }
 
 static bool ComputationallyIndependent(const CSSValue& value) {
diff --git a/third_party/blink/renderer/core/css/property_registration.h b/third_party/blink/renderer/core/css/property_registration.h
index f9a52577..665068ea 100644
--- a/third_party/blink/renderer/core/css/property_registration.h
+++ b/third_party/blink/renderer/core/css/property_registration.h
@@ -59,7 +59,7 @@
   bool Inherits() const { return inherits_; }
   const CSSValue* Initial() const { return initial_.Get(); }
   StyleRuleProperty* PropertyRule() const { return property_rule_.Get(); }
-  const InterpolationTypes& GetInterpolationTypes() const {
+  const InterpolationTypes* GetInterpolationTypes() const {
     return interpolation_types_;
   }
   // See `ViewportUnitFlag`.
@@ -74,7 +74,7 @@
   const bool inherits_;
   const Member<const CSSValue> initial_;
   Member<StyleRuleProperty> property_rule_;
-  const InterpolationTypes interpolation_types_;
+  Member<const InterpolationTypes> interpolation_types_;
   mutable bool referenced_;
 };
 
diff --git a/third_party/blink/renderer/core/css/resolver/transform_builder.cc b/third_party/blink/renderer/core/css/resolver/transform_builder.cc
index bccca4cd..b1b6529 100644
--- a/third_party/blink/renderer/core/css/resolver/transform_builder.cc
+++ b/third_party/blink/renderer/core/css/resolver/transform_builder.cc
@@ -102,26 +102,28 @@
   }
 }
 
-bool TransformBuilder::HasRelativeLengths(const CSSValueList& value_list) {
+bool TransformBuilder::IsResolvableAtParseTime(const CSSValueList& value_list) {
   for (auto& value : value_list) {
     const auto* transform_value = To<CSSFunctionValue>(value.Get());
 
     for (const CSSValue* item : *transform_value) {
       const auto& primitive_value = To<CSSPrimitiveValue>(*item);
-      if (primitive_value.IsCalculated()) {
-        if (To<CSSMathFunctionValue>(primitive_value).MayHaveRelativeUnit()) {
-          return true;
+      if (const auto* math_value =
+              DynamicTo<CSSMathFunctionValue>(primitive_value)) {
+        if (math_value->MayHaveRelativeUnit() ||
+            math_value->IsElementDependent()) {
+          return false;
         }
       } else {
         CSSPrimitiveValue::UnitType unit_type =
             To<CSSNumericLiteralValue>(primitive_value).GetType();
         if (CSSPrimitiveValue::IsRelativeUnit(unit_type)) {
-          return true;
+          return false;
         }
       }
     }
   }
-  return false;
+  return true;
 }
 
 namespace {
diff --git a/third_party/blink/renderer/core/css/resolver/transform_builder.h b/third_party/blink/renderer/core/css/resolver/transform_builder.h
index e7a4d3e..96fd43ad 100644
--- a/third_party/blink/renderer/core/css/resolver/transform_builder.h
+++ b/third_party/blink/renderer/core/css/resolver/transform_builder.h
@@ -44,7 +44,9 @@
   STATIC_ONLY(TransformBuilder);
 
  public:
-  static bool HasRelativeLengths(const CSSValueList&);
+  // Returns true if all the values are known numbers and do not rely on
+  // resolving e.g. relative lengths or sibling-index().
+  static bool IsResolvableAtParseTime(const CSSValueList&);
   static TransformOperations CreateTransformOperations(
       const CSSValue& in_value,
       const CSSToLengthConversionData&);
diff --git a/third_party/blink/renderer/core/dom/container_node.cc b/third_party/blink/renderer/core/dom/container_node.cc
index eefd417..e0e8252 100644
--- a/third_party/blink/renderer/core/dom/container_node.cc
+++ b/third_party/blink/renderer/core/dom/container_node.cc
@@ -920,9 +920,10 @@
 
   // Request to merge combined texts in anonymous block.
   // See http://crbug.com/1233432
-  if (!previous_sibling->IsAnonymousBlock() ||
-      !next_sibling->IsAnonymousBlock())
+  if (!previous_sibling->IsAnonymousBlockFlow() ||
+      !next_sibling->IsAnonymousBlockFlow()) {
     return false;
+  }
 
   if (IsA<LayoutTextCombine>(previous_sibling->SlowLastChild()) &&
       IsA<LayoutTextCombine>(next_sibling->SlowFirstChild())) [[unlikely]] {
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 30cc993..f1e2375 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -9598,7 +9598,7 @@
   UseCounter::Count(context, WebFeature::kHTMLUnsafeMethods);
   CHECK(RuntimeEnabledFeatures::SanitizerAPIEnabled());
   Document* doc = parseHTMLInternal(context, html, exception_state);
-  SanitizerAPI::SanitizeUnsafeInternal(doc->body(), options, exception_state);
+  SanitizerAPI::SanitizeUnsafeInternal(doc, options, exception_state);
   return doc;
 }
 
@@ -9609,7 +9609,7 @@
                               ExceptionState& exception_state) {
   CHECK(RuntimeEnabledFeatures::SanitizerAPIEnabled());
   Document* doc = parseHTMLInternal(context, html, exception_state);
-  SanitizerAPI::SanitizeSafeInternal(doc->body(), options, exception_state);
+  SanitizerAPI::SanitizeSafeInternal(doc, options, exception_state);
   return doc;
 }
 
diff --git a/third_party/blink/renderer/core/frame/integrity_policy.cc b/third_party/blink/renderer/core/frame/integrity_policy.cc
index 06adeeb7..ad8a852 100644
--- a/third_party/blink/renderer/core/frame/integrity_policy.cc
+++ b/third_party/blink/renderer/core/frame/integrity_policy.cc
@@ -69,12 +69,16 @@
     network::mojom::RequestMode request_mode,
     const IntegrityMetadataSet& integrity_metadata,
     const KURL& url) {
-  if ((!integrity_metadata.empty() &&
+  if (!context ||
+      (!integrity_metadata.empty() &&
        request_mode != network::mojom::RequestMode::kNoCors) ||
       url.ProtocolIsData() || url.ProtocolIs("blob")) {
     return true;
   }
   PolicyContainer* policy_container = context->GetPolicyContainer();
+  if (!policy_container) {
+    return true;
+  }
   const network::IntegrityPolicy& integrity_policy =
       policy_container->GetPolicies().integrity_policy;
   const network::IntegrityPolicy& integrity_policy_report_only =
diff --git a/third_party/blink/renderer/core/geometry/dom_matrix_read_only.cc b/third_party/blink/renderer/core/geometry/dom_matrix_read_only.cc
index cf2ddbe..b421e6a 100644
--- a/third_party/blink/renderer/core/geometry/dom_matrix_read_only.cc
+++ b/third_party/blink/renderer/core/geometry/dom_matrix_read_only.cc
@@ -480,9 +480,10 @@
     return;
   }
 
-  if (TransformBuilder::HasRelativeLengths(To<CSSValueList>(*value))) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kSyntaxError,
-                                      "Lengths must be absolute, not relative");
+  if (!TransformBuilder::IsResolvableAtParseTime(To<CSSValueList>(*value))) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kSyntaxError,
+        "Values must be resolvable at parse time");
     return;
   }
 
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
index c367c4d..4d4a115 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
@@ -293,8 +293,8 @@
   // WebGL-specific interface
   virtual bool UsingSwapChain() const { return false; }
   virtual void MarkLayerComposited() { NOTREACHED(); }
-  virtual sk_sp<SkData> PaintRenderingResultsToRGBADataArray(
-      SourceDrawingBuffer) {
+  virtual scoped_refptr<StaticBitmapImage>
+  GetRGBAUnacceleratedStaticBitmapImage(SourceDrawingBuffer source_buffer) {
     NOTREACHED();
   }
   virtual gfx::Size DrawingBufferSize() const { NOTREACHED(); }
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 3bac009..8a1b7e9 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -1223,26 +1223,8 @@
       if (ResourceProvider())
         image_bitmap = ResourceProvider()->Snapshot(reason);
     } else {
-      sk_sp<SkData> pixel_data =
-          context_->PaintRenderingResultsToRGBADataArray(source_buffer);
-      if (pixel_data) {
-        // If the accelerated canvas is too big, there is a logic in WebGL code
-        // path that scales down the drawing buffer to the maximum supported
-        // size. Hence, we need to query the adjusted size of DrawingBuffer.
-        gfx::Size adjusted_size = context_->DrawingBufferSize();
-        if (!adjusted_size.IsEmpty()) {
-          image_bitmap = StaticBitmapImage::Create(
-              std::move(pixel_data),
-              SkImageInfo::Make(
-                  SkISize::Make(adjusted_size.width(), adjusted_size.height()),
-                  (GetRenderingContextFormat() ==
-                   viz::SinglePlaneFormat::kRGBA_F16)
-                      ? kRGBA_F16_SkColorType
-                      : kRGBA_8888_SkColorType,
-                  kUnpremul_SkAlphaType,
-                  GetRenderingContextColorSpace().ToSkColorSpace()));
-        }
-      }
+      image_bitmap =
+          context_->GetRGBAUnacceleratedStaticBitmapImage(source_buffer);
     }
   } else if (context_) {
     DCHECK(IsRenderingContext2D() || IsImageBitmapRenderingContext() ||
diff --git a/third_party/blink/renderer/core/layout/block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/block_layout_algorithm.cc
index d927115e..1b4d640 100644
--- a/third_party/blink/renderer/core/layout/block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/block_layout_algorithm.cc
@@ -435,7 +435,7 @@
     }
 
     MinMaxSizesFloatInput child_float_input;
-    if (child.IsInline() || child.IsAnonymousBlock()) {
+    if (child.IsInline() || child.IsAnonymousBlockFlow()) {
       child_float_input.float_left_inline_size = float_left_inline_size;
       child_float_input.float_right_inline_size = float_right_inline_size;
     }
@@ -446,7 +446,7 @@
     builder.SetPercentageResolutionBlockSize(
         PercentageSizeForChild(child).block_size);
     // Pass the replaced %-size down to inline layout.
-    if ((child.IsAnonymousBlock() || child.IsInline()) &&
+    if ((child.IsAnonymousBlockFlow() || child.IsInline()) &&
         replaced_child_percentage_size_.block_size !=
             child_percentage_size_.block_size) {
       builder.SetReplacedChildPercentageResolutionBlockSize(
@@ -3147,7 +3147,7 @@
                   &Style())
               .GetPosition();
 
-      if (child.IsAnonymousBlock()) {
+      if (child.IsAnonymousBlockFlow()) {
         builder.SetInlineAutoBehavior(AutoSizeBehavior::kStretchImplicit);
       } else if (justify_self == ItemPosition::kStretch && !has_auto_margins) {
         builder.SetInlineAutoBehavior(AutoSizeBehavior::kStretchExplicit);
@@ -3231,7 +3231,7 @@
                 {ItemPosition::kNormal, OverflowAlignment::kDefault}, &Style())
             .GetPosition();
 
-    if (child.IsAnonymousBlock()) {
+    if (child.IsAnonymousBlockFlow()) {
       builder.SetInlineAutoBehavior(AutoSizeBehavior::kStretchImplicit);
     } else if (justify_self == ItemPosition::kStretch && !has_auto_margins) {
       builder.SetInlineAutoBehavior(AutoSizeBehavior::kStretchExplicit);
@@ -3252,7 +3252,7 @@
   builder.SetPercentageResolutionSize(PercentageSizeForChild(child));
 
   // Pass the replaced %-size down to inline layout.
-  if ((child.IsAnonymousBlock() || child.IsInline()) &&
+  if ((child.IsAnonymousBlockFlow() || child.IsInline()) &&
       replaced_child_percentage_size_ != child_percentage_size_) {
     builder.SetReplacedChildPercentageResolutionSize(
         replaced_child_percentage_size_);
diff --git a/third_party/blink/renderer/core/layout/block_node.cc b/third_party/blink/renderer/core/layout/block_node.cc
index 75633e4..a3974f93f 100644
--- a/third_party/blink/renderer/core/layout/block_node.cc
+++ b/third_party/blink/renderer/core/layout/block_node.cc
@@ -38,7 +38,6 @@
 #include "third_party/blink/renderer/core/layout/inline/inline_cursor.h"
 #include "third_party/blink/renderer/core/layout/inline/inline_node.h"
 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
-#include "third_party/blink/renderer/core/layout/layout_box_utils.h"
 #include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/layout/layout_input_node.h"
 #include "third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h"
diff --git a/third_party/blink/renderer/core/layout/flex/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/flex/layout_flexible_box.cc
index 79c8695..35f3fd8 100644
--- a/third_party/blink/renderer/core/layout/flex/layout_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/flex/layout_flexible_box.cc
@@ -71,11 +71,13 @@
   // are text nodes wrapped in anonymous flex items, the adjacent text nodes
   // need to be merged into the same flex item.
   LayoutObject* prev = remove_child->PreviousSibling();
-  if (!prev || !prev->IsAnonymousBlock())
+  if (!prev || !prev->IsAnonymousBlockFlow()) {
     return;
+  }
   LayoutObject* next = remove_child->NextSibling();
-  if (!next || !next->IsAnonymousBlock())
+  if (!next || !next->IsAnonymousBlockFlow()) {
     return;
+  }
   To<LayoutBoxModelObject>(next)->MoveAllChildrenTo(
       To<LayoutBoxModelObject>(prev));
   next->Destroy();
@@ -129,9 +131,7 @@
 }
 
 void LayoutFlexibleBox::RemoveChild(LayoutObject* child) {
-  if (!DocumentBeingDestroyed() &&
-      (RuntimeEnabledFeatures::LayoutWebkitBoxTreeFixEnabled() ||
-       !StyleRef().IsDeprecatedFlexbox())) {
+  if (!DocumentBeingDestroyed()) {
     MergeAnonymousFlexItems(child);
   }
 
diff --git a/third_party/blink/renderer/core/layout/inline/inline_containing_block_utils.cc b/third_party/blink/renderer/core/layout/inline/inline_containing_block_utils.cc
index d8fead6..1138dd1 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_containing_block_utils.cc
+++ b/third_party/blink/renderer/core/layout/inline/inline_containing_block_utils.cc
@@ -147,8 +147,9 @@
   //    text </span> text.
   // </div>
   for (const auto& child : container_builder->Children()) {
-    if (!child.fragment->IsAnonymousBlock())
+    if (!child.fragment->IsAnonymousBlockFlow()) {
       continue;
+    }
 
     const auto& child_fragment = To<PhysicalBoxFragment>(*child.fragment);
     const auto* items = child_fragment.Items();
@@ -205,8 +206,9 @@
       //    text </span> text.
       // </div>
       for (const auto& child : physical_fragment.Children()) {
-        if (!child.fragment->IsAnonymousBlock())
+        if (!child.fragment->IsAnonymousBlockFlow()) {
           continue;
+        }
 
         const auto& child_fragment = To<PhysicalBoxFragment>(*child.fragment);
         if (!child_fragment.HasItems())
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 df8c296..937bd5e 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_node.cc
+++ b/third_party/blink/renderer/core/layout/inline/inline_node.cc
@@ -1556,8 +1556,7 @@
     text_item_ranges.ReserveInitialCapacity(num_text_items);
     ClearCollectionScope clear_scope(&text_item_ranges);
 
-    const bool has_ligatures =
-        shape_result->NumGlyphs() < shape_result->NumCharacters();
+    const bool has_ligatures = shape_result->HasLigatures();
     if (has_ligatures) {
       shape_result->EnsurePositionData();
     }
diff --git a/third_party/blink/renderer/core/layout/inline/line_breaker.cc b/third_party/blink/renderer/core/layout/inline/line_breaker.cc
index 303519a..04a76b9 100644
--- a/third_party/blink/renderer/core/layout/inline/line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/inline/line_breaker.cc
@@ -1464,7 +1464,8 @@
   DCHECK_EQ(current_.text_offset, item.StartOffset());
 
   const ShapeResult& shape = *item.TextShapeResult();
-  if (shape.NumGlyphs() == 0 || !needs_svg_segmentation_) {
+  const unsigned num_glyphs = shape.NumGlyphs();
+  if (num_glyphs == 0 || !needs_svg_segmentation_) {
     InlineItemResult* result = AddItem(item, line_info);
     result->should_create_line_box = true;
     result->shape_result = ShapeResultView::Create(&shape);
@@ -1477,7 +1478,7 @@
   }
 
   Vector<unsigned> index_list;
-  index_list.reserve(shape.NumGlyphs());
+  index_list.reserve(num_glyphs);
   shape.ForEachGlyph(0, CollectCharIndex, &index_list);
   if (shape.IsRtl())
     index_list.Reverse();
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index 33f6a97..5f8744d 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -216,17 +216,12 @@
   // If the requested insertion point is not one of our children, then this is
   // because there is an anonymous container within this object that contains
   // the beforeDescendant.
-  if (before_descendant_container->IsAnonymousBlock()) {
+  if (before_descendant_container->IsAnonymousBlockFlow()) {
     // Insert the child into the anonymous block box instead of here. Note that
     // a LayoutOutsideListMarker is out-of-flow for tree building purposes, and
     // that is not inline level, although IsInline() is true.
-    const bool is_block_flow_like =
-        RuntimeEnabledFeatures::LayoutWebkitBoxTreeFixEnabled()
-            ? IsLayoutBlockFlow()
-            : (StyleRef().IsDeprecatedFlexbox() ||
-               (!IsFlexibleBox() && !IsLayoutGrid()));
     if ((new_child->IsInline() && !new_child->IsLayoutOutsideListMarker()) ||
-        (new_child->IsFloatingOrOutOfFlowPositioned() && is_block_flow_like) ||
+        (new_child->IsFloatingOrOutOfFlowPositioned() && IsLayoutBlockFlow()) ||
         before_descendant->Parent()->SlowFirstChild() != before_descendant) {
       before_descendant_container->AddChild(new_child, before_descendant);
     } else {
@@ -267,12 +262,7 @@
   // here.
   DCHECK(!ChildrenInline());
 
-  const bool is_block_flow_like =
-      !RuntimeEnabledFeatures::LayoutWebkitBoxTreeFixEnabled() &&
-      (StyleRef().IsDeprecatedFlexbox() ||
-       (!IsFlexibleBox() && !IsLayoutGrid()));
-  if (new_child->IsInline() ||
-      (new_child->IsFloatingOrOutOfFlowPositioned() && is_block_flow_like)) {
+  if (new_child->IsInline()) {
     // If we're inserting an inline child but all of our children are blocks,
     // then we have to make sure it is put into an anomyous block box. We try to
     // use an existing anonymous box if possible, otherwise a new one is created
@@ -280,7 +270,7 @@
     LayoutObject* after_child =
         before_child ? before_child->PreviousSibling() : LastChild();
 
-    if (after_child && after_child->IsAnonymousBlock()) {
+    if (after_child && after_child->IsAnonymousBlockFlow()) {
       after_child->AddChild(new_child);
       return;
     }
@@ -299,7 +289,7 @@
 
 void LayoutBlock::RemoveLeftoverAnonymousBlock(LayoutBlock* child) {
   NOT_DESTROYED();
-  DCHECK(child->IsAnonymousBlock());
+  DCHECK(child->IsAnonymousBlockFlow());
   DCHECK(!child->ChildrenInline());
   DCHECK_EQ(child->Parent(), this);
 
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.cc b/third_party/blink/renderer/core/layout/layout_block_flow.cc
index c4a54389..4677515c 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.cc
@@ -121,7 +121,7 @@
   // line of an element. For example, the first line of an anonymous block
   // box is only affected if it is the first child of its parent element.
   // https://drafts.csswg.org/css-text-3/#text-indent-property
-  return !IsAnonymousBlock() || !PreviousSibling() || IsFlexItem() ||
+  return !IsAnonymousBlockFlow() || !PreviousSibling() || IsFlexItem() ||
          IsGridItem();
 }
 
@@ -175,7 +175,7 @@
 
       if (before_child && before_child->Parent() != this) {
         before_child = before_child->Parent();
-        DCHECK(before_child->IsAnonymousBlock());
+        DCHECK(before_child->IsAnonymousBlockFlow());
         DCHECK_EQ(before_child->Parent(), this);
       }
     }
@@ -189,7 +189,7 @@
     LayoutObject* after_child =
         before_child ? before_child->PreviousSibling() : LastChild();
 
-    if (after_child && after_child->IsAnonymousBlock()) {
+    if (after_child && after_child->IsAnonymousBlockFlow()) {
       after_child->AddChild(new_child);
       return;
     }
@@ -213,14 +213,14 @@
   // at this point.
   LayoutBox::AddChild(new_child, before_child);
   auto* parent_layout_block = DynamicTo<LayoutBlock>(Parent());
-  if (made_boxes_non_inline && IsAnonymousBlock() && parent_layout_block) {
+  if (made_boxes_non_inline && IsAnonymousBlockFlow() && parent_layout_block) {
     parent_layout_block->RemoveLeftoverAnonymousBlock(this);
     // |this| may be dead now.
   }
 }
 
 static bool IsMergeableAnonymousBlock(const LayoutBlockFlow* block) {
-  return block->IsAnonymousBlock() && !block->BeingDestroyed() &&
+  return block->IsAnonymousBlockFlow() && !block->BeingDestroyed() &&
          !block->IsViewTransitionRoot();
 }
 
@@ -287,7 +287,7 @@
 
   if (FirstChild() && !BeingDestroyed() &&
       !old_child->IsFloatingOrOutOfFlowPositioned() &&
-      !old_child->IsAnonymousBlock()) {
+      !old_child->IsAnonymousBlockFlow()) {
     // If the child we're removing means that we can now treat all children as
     // inline without the need for anonymous blocks, then do that.
     MakeChildrenInlineIfPossible();
@@ -308,7 +308,7 @@
 
 void LayoutBlockFlow::ChildBecameFloatingOrOutOfFlow(LayoutBox* child) {
   NOT_DESTROYED();
-  if (IsAnonymousBlock()) {
+  if (IsAnonymousBlockFlow()) {
     if (auto* parent_inline = DynamicTo<LayoutInline>(Parent())) {
       // The child used to be an in-flow block-in-inline, which requires an
       // anonymous wrapper (|this|). It is no longer needed for this child, so
@@ -323,7 +323,7 @@
 
   // Reparent the child to an adjacent anonymous block if one is available.
   auto* prev = DynamicTo<LayoutBlockFlow>(child->PreviousSibling());
-  if (prev && prev->IsAnonymousBlock()) {
+  if (prev && prev->IsAnonymousBlockFlow()) {
     MoveChildTo(prev, child, nullptr, false);
     // The anonymous block we've moved to may now be adjacent to former siblings
     // of ours that it can contain also.
@@ -331,7 +331,7 @@
     return;
   }
   auto* next = DynamicTo<LayoutBlockFlow>(child->NextSibling());
-  if (next && next->IsAnonymousBlock()) {
+  if (next && next->IsAnonymousBlockFlow()) {
     MoveChildTo(next, child, next->FirstChild(), false);
   }
 }
@@ -439,7 +439,7 @@
   }
   // Collapsing away anonymous wrappers isn't relevant for the children of
   // anonymous blocks.
-  if (IsAnonymousBlock()) {
+  if (IsAnonymousBlockFlow()) {
     return;
   }
 
@@ -454,8 +454,9 @@
     // There are still block children in the container, so any anonymous
     // wrappers are still needed.
     auto* child_block_flow = DynamicTo<LayoutBlockFlow>(child);
-    if (!child->IsAnonymousBlock() || !child_block_flow)
+    if (!child->IsAnonymousBlockFlow() || !child_block_flow) {
       return;
+    }
     // If one of the children is being destroyed then it is unsafe to clean up
     // anonymous wrappers as the
     // entire branch may be being destroyed.
@@ -571,15 +572,16 @@
   NOT_DESTROYED();
   MakeChildrenNonInline();
   auto* parent_layout_block = DynamicTo<LayoutBlock>(Parent());
-  if (IsAnonymousBlock() && parent_layout_block)
+  if (IsAnonymousBlockFlow() && parent_layout_block) {
     parent_layout_block->RemoveLeftoverAnonymousBlock(this);
+  }
   // |this| may be dead here
 }
 
 bool LayoutBlockFlow::ShouldTruncateOverflowingText() const {
   NOT_DESTROYED();
   const LayoutObject* object_to_check = this;
-  if (IsAnonymousBlock()) {
+  if (IsAnonymousBlockFlow()) {
     const LayoutObject* parent = Parent();
     if (!parent || !parent->BehavesLikeBlockContainer()) {
       return false;
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object.cc b/third_party/blink/renderer/core/layout/layout_box_model_object.cc
index 0082878..db71514 100644
--- a/third_party/blink/renderer/core/layout/layout_box_model_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_box_model_object.cc
@@ -66,8 +66,9 @@
 void CollapseLoneAnonymousBlockChild(LayoutBox* parent, LayoutObject* child) {
   auto* child_block_flow = DynamicTo<LayoutBlockFlow>(child);
   auto* parent_block_flow = DynamicTo<LayoutBlockFlow>(parent);
-  if (!child->IsAnonymousBlock() || !child_block_flow)
+  if (!child->IsAnonymousBlockFlow() || !child_block_flow) {
     return;
+  }
   if (!parent_block_flow)
     return;
   parent_block_flow->CollapseAnonymousBlockChild(child_block_flow);
diff --git a/third_party/blink/renderer/core/layout/layout_input_node.h b/third_party/blink/renderer/core/layout/layout_input_node.h
index c3716532..d189c29 100644
--- a/third_party/blink/renderer/core/layout/layout_input_node.h
+++ b/third_party/blink/renderer/core/layout/layout_input_node.h
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/core/layout/geometry/axis.h"
 #include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
+#include "third_party/blink/renderer/core/layout/layout_box_utils.h"
 #include "third_party/blink/renderer/core/layout/list/layout_outside_list_marker.h"
 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
 #include "third_party/blink/renderer/platform/text/writing_mode.h"
@@ -154,7 +155,7 @@
   bool IsMathML() const { return box_->IsMathML(); }
 
   bool IsAnonymous() const { return box_->IsAnonymous(); }
-  bool IsAnonymousBlock() const { return box_->IsAnonymousBlock(); }
+  bool IsAnonymousBlockFlow() const { return box_->IsAnonymousBlockFlow(); }
 
   // If the node is a quirky container for margin collapsing, see:
   // https://html.spec.whatwg.org/C/#margin-collapsing-quirks
diff --git a/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc b/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc
index bbc1945..0734df1c 100644
--- a/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc
+++ b/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc
@@ -29,7 +29,6 @@
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
 #include "third_party/blink/renderer/core/layout/fragmentation_utils.h"
 #include "third_party/blink/renderer/core/layout/geometry/writing_mode_converter.h"
-#include "third_party/blink/renderer/core/layout/layout_box_utils.h"
 #include "third_party/blink/renderer/core/layout/layout_multi_column_set.h"
 #include "third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
diff --git a/third_party/blink/renderer/core/layout/layout_multi_column_set.cc b/third_party/blink/renderer/core/layout/layout_multi_column_set.cc
index 5d502c94..529e24c 100644
--- a/third_party/blink/renderer/core/layout/layout_multi_column_set.cc
+++ b/third_party/blink/renderer/core/layout/layout_multi_column_set.cc
@@ -29,7 +29,6 @@
 #include "third_party/blink/renderer/core/editing/position_with_affinity.h"
 #include "third_party/blink/renderer/core/layout/fragmentation_utils.h"
 #include "third_party/blink/renderer/core/layout/geometry/box_strut.h"
-#include "third_party/blink/renderer/core/layout/layout_box_utils.h"
 #include "third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h"
 #include "third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h"
 #include "third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.h"
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 61647493..c2d1156 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -1326,8 +1326,13 @@
     NOT_DESTROYED();
     return bitfields_.IsAnonymous();
   }
-  bool IsAnonymousBlock() const {
+  bool IsAnonymousBlockFlow() const {
     NOT_DESTROYED();
+    if (RuntimeEnabledFeatures::LayoutIsAnonymousBlockFixEnabled()) {
+      return IsAnonymous() && IsLayoutBlockFlow() &&
+             StyleRef().Display() == EDisplay::kBlock &&
+             !IsLayoutFlowThread() && !IsLayoutMultiColumnSet();
+    }
     // This function is kept in sync with anonymous block creation conditions in
     // LayoutBlock::createAnonymousBlock(). This includes creating an anonymous
     // LayoutBlock having a BLOCK or BOX display. Other classes such as
diff --git a/third_party/blink/renderer/core/layout/length_utils.cc b/third_party/blink/renderer/core/layout/length_utils.cc
index d046226..58358ee 100644
--- a/third_party/blink/renderer/core/layout/length_utils.cc
+++ b/third_party/blink/renderer/core/layout/length_utils.cc
@@ -1781,7 +1781,7 @@
       ShrinkLogicalSize(border_box_size, border_scrollbar_padding);
 
   if (space.IsAnonymous() ||
-      (node.IsAnonymousBlock() &&
+      (node.IsAnonymousBlockFlow() &&
        child_available_size.block_size == kIndefiniteSize)) {
     child_available_size.block_size = space.AvailableSize().block_size;
   }
@@ -1814,7 +1814,7 @@
     const BlockNode node,
     const LogicalSize child_available_size) {
   // Anonymous block or spaces should use the parent percent block-size.
-  if (space.IsAnonymous() || node.IsAnonymousBlock()) {
+  if (space.IsAnonymous() || node.IsAnonymousBlockFlow()) {
     return {child_available_size.inline_size,
             space.PercentageResolutionBlockSize()};
   }
@@ -1836,7 +1836,7 @@
     const BoxStrut& border_scrollbar_padding,
     const BoxStrut& border_padding) {
   // Anonymous block or spaces should use the parent percent block-size.
-  if (space.IsAnonymous() || node.IsAnonymousBlock()) {
+  if (space.IsAnonymous() || node.IsAnonymousBlockFlow()) {
     return {child_available_size.inline_size,
             space.ReplacedChildPercentageResolutionBlockSize()};
   }
diff --git a/third_party/blink/renderer/core/layout/list/layout_list_item.cc b/third_party/blink/renderer/core/layout/list/layout_list_item.cc
index 6b43c40..953f1bb 100644
--- a/third_party/blink/renderer/core/layout/list/layout_list_item.cc
+++ b/third_party/blink/renderer/core/layout/list/layout_list_item.cc
@@ -117,8 +117,9 @@
     return FindSymbolMarkerLayoutText(inline_list_item->Marker());
   }
 
-  if (object->IsAnonymousBlock())
+  if (object->IsAnonymousBlockFlow()) {
     return FindSymbolMarkerLayoutText(GetLayoutObjectForParentNode(object));
+  }
 
   if (object->IsLayoutTextCombine()) {
     return FindSymbolMarkerLayoutText(object->Parent());
diff --git a/third_party/blink/renderer/core/layout/physical_box_fragment.cc b/third_party/blink/renderer/core/layout/physical_box_fragment.cc
index 413576d0..b6209fcd 100644
--- a/third_party/blink/renderer/core/layout/physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/physical_box_fragment.cc
@@ -1150,7 +1150,7 @@
   DCHECK(IsOutlineOwner());
 
   // For anonymous blocks, the children add outline rects.
-  if (!IsAnonymousBlock() || GetBoxType() == kPageBorderBox) {
+  if (!IsAnonymousBlockFlow() || GetBoxType() == kPageBorderBox) {
     if (IsSvgText()) {
       if (Items()) {
         collector.AddRect(PhysicalRect::EnclosingRect(
diff --git a/third_party/blink/renderer/core/layout/physical_fragment.h b/third_party/blink/renderer/core/layout/physical_fragment.h
index 116db816..534d3701 100644
--- a/third_party/blink/renderer/core/layout/physical_fragment.h
+++ b/third_party/blink/renderer/core/layout/physical_fragment.h
@@ -237,8 +237,8 @@
   bool IsCSSBox() const { return !IsLineBox() && !IsFragmentainerBox(); }
 
   bool IsBlockFlow() const;
-  bool IsAnonymousBlock() const {
-    return IsCSSBox() && layout_object_->IsAnonymousBlock();
+  bool IsAnonymousBlockFlow() const {
+    return IsCSSBox() && layout_object_->IsAnonymousBlockFlow();
   }
   bool IsFrameSet() const { return IsCSSBox() && layout_object_->IsFrameSet(); }
   bool IsListMarker() const {
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
index bd11b6f7..735787b 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -507,7 +507,7 @@
       (fragment && fragment->IsMonolithic())) {
     // Anonymous blocks are not allowed to be containing blocks, so we should
     // skip over any such elements.
-    if (!fragment || !fragment->IsAnonymousBlock()) {
+    if (!fragment || !fragment->IsAnonymousBlockFlow()) {
       context.current_container.fragment = fragment;
     }
   }
diff --git a/third_party/blink/renderer/core/style/style_fetched_image.cc b/third_party/blink/renderer/core/style/style_fetched_image.cc
index d35ebd8..d1b2f74b 100644
--- a/third_party/blink/renderer/core/style/style_fetched_image.cc
+++ b/third_party/blink/renderer/core/style/style_fetched_image.cc
@@ -286,8 +286,17 @@
   return true;
 }
 
-String StyleFetchedImage::FragmentIdentifier() const {
-  return KURL(url_data_->ResolvedUrl()).FragmentIdentifier().ToString();
+const String& StyleFetchedImage::FragmentIdentifier() const {
+  if (cached_fragment_identifier_.IsNull()) {
+    cached_fragment_identifier_ =
+        KURL(url_data_->ResolvedUrl()).FragmentIdentifier().ToString();
+    // If URL does not have a fragment identifier we'll get a null
+    // String. Normalize to the empty String to avoid repeated URL resolving.
+    if (!cached_fragment_identifier_) {
+      cached_fragment_identifier_ = g_empty_string;
+    }
+  }
+  return cached_fragment_identifier_;
 }
 
 bool StyleFetchedImage::CanBeSpeculativelyDecoded() const {
diff --git a/third_party/blink/renderer/core/style/style_fetched_image.h b/third_party/blink/renderer/core/style/style_fetched_image.h
index c718086..0b34e14 100644
--- a/third_party/blink/renderer/core/style/style_fetched_image.h
+++ b/third_party/blink/renderer/core/style/style_fetched_image.h
@@ -104,8 +104,9 @@
   bool GetImageAnimationPolicy(mojom::blink::ImageAnimationPolicy&) override;
   bool CanBeSpeculativelyDecoded() const override;
 
-  String FragmentIdentifier() const;
+  const String& FragmentIdentifier() const;
 
+  mutable String cached_fragment_identifier_;
   Member<ImageResourceContent> image_;
   Member<const CSSUrlData> url_data_;
   Member<const Document> document_;
diff --git a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
index 28fdbe7a..e921050 100644
--- a/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/video_track_recorder.cc
@@ -856,6 +856,7 @@
                              WrapPersistent(callback_interface)))),
       frame_buffer_pool_limit_(frame_buffer_pool_limit) {
   TRACE_EVENT("media", "VideoTrackRecorderImpl::VideoTrackRecorderImpl");
+  CHECK(main_thread_task_runner_);
   DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
   DCHECK(track_);
   DCHECK(track_->GetSourceType() == MediaStreamSource::kTypeVideo);
@@ -905,6 +906,7 @@
     base::TimeTicks capture_time) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
   TRACE_EVENT("media", "VideoTrackRecorderImpl::OnVideoFrame");
+  CHECK(video_frame);
 
   // Measure how common video transformation changes are, crbug.com/391786486.
   auto frame_transformation = GetFrameTransformation(video_frame);
@@ -936,6 +938,7 @@
     scoped_refptr<media::VideoFrame> video_frame,
     base::TimeTicks capture_time) {
   TRACE_EVENT("media", "VideoTrackRecorderImpl::ProcessOneVideoFrame");
+  CHECK(video_frame);
   if (!encoder_) {
     InitializeEncoder(bits_per_second_, allow_vea_encoder,
                       video_frame->storage_type(),
@@ -1045,6 +1048,7 @@
   const bool create_vea_encoder = allow_vea_encoder && can_use_vea;
   auto encoding_task_runner =
       base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
+  CHECK(encoding_task_runner);
   auto encoder = CreateMediaVideoEncoder(encoding_task_runner, codec_profile,
                                          is_screencast, create_vea_encoder);
 
@@ -1101,6 +1105,7 @@
                          callback_interface),
       track_(track),
       key_frame_processor_(key_frame_config) {
+  CHECK(main_thread_task_runner_);
   DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
   // HandleEncodedVideoFrame() will be called on Render Main thread.
   // Note: Adding an encoded sink internally generates a new key frame
diff --git a/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc b/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc
index b09e8e8..edddfc8b5 100644
--- a/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/processed_local_audio_source.cc
@@ -143,12 +143,7 @@
     : blink::MediaStreamAudioSource(std::move(task_runner),
                                     true /* is_local_source */,
                                     disable_local_echo),
-      // Remote APM is only enabled for mic input, other input sources have
-      // conflicting requirements on echo cancellation:
-      // https://crbug.com/1328012
-      use_remote_apm_(media::IsChromeWideEchoCancellationEnabled() &&
-                      device.type ==
-                          mojom::blink::MediaStreamType::DEVICE_AUDIO_CAPTURE),
+      use_remote_apm_(media::IsChromeWideEchoCancellationEnabled()),
       consumer_frame_(&frame),
       dependency_factory_(
           PeerConnectionDependencyFactory::From(*frame.DomWindow())),
@@ -157,6 +152,8 @@
       started_callback_(std::move(started_callback)),
       allow_invalid_render_frame_id_for_testing_(false) {
   DCHECK(frame.DomWindow());
+  CHECK_EQ(device.type, mojom::blink::MediaStreamType::DEVICE_AUDIO_CAPTURE);
+
   SetDevice(device);
   SendLogMessage(StringPrintf(
       "%s({audio_processing_properties=[%s]}, {APM=%s})[session_id=%s]",
diff --git a/third_party/blink/renderer/modules/storage_access/document_storage_access.cc b/third_party/blink/renderer/modules/storage_access/document_storage_access.cc
index 16e4abac..dcbc39f1 100644
--- a/third_party/blink/renderer/modules/storage_access/document_storage_access.cc
+++ b/third_party/blink/renderer/modules/storage_access/document_storage_access.cc
@@ -343,9 +343,7 @@
         "requestStorageAccess not allowed"));
     return promise;
   }
-  if (RuntimeEnabledFeatures::FedCmWithStorageAccessAPIEnabled(
-          GetSupplementable()->GetExecutionContext()) &&
-      GetSupplementable()->GetExecutionContext()->IsFeatureEnabled(
+  if (GetSupplementable()->GetExecutionContext()->IsFeatureEnabled(
           network::mojom::PermissionsPolicyFeature::kIdentityCredentialsGet)) {
     UseCounter::Count(GetSupplementable()->GetExecutionContext(),
                       WebFeature::kFedCmWithStorageAccessAPI);
diff --git a/third_party/blink/renderer/modules/webcodecs/background_readback.cc b/third_party/blink/renderer/modules/webcodecs/background_readback.cc
index 63e88be..b378f86 100644
--- a/third_party/blink/renderer/modules/webcodecs/background_readback.cc
+++ b/third_party/blink/renderer/modules/webcodecs/background_readback.cc
@@ -208,7 +208,9 @@
   gfx::Point src_point;
   auto shared_image = txt_frame->shared_image();
   auto origin = shared_image->surface_origin();
-  ri->WaitSyncTokenCHROMIUM(txt_frame->acquire_sync_token().GetConstData());
+  std::unique_ptr<gpu::RasterScopedAccess> ri_access =
+      shared_image->BeginRasterAccess(ri, txt_frame->acquire_sync_token(),
+                                      /*readonly=*/true);
 
   gfx::Size texture_size = txt_frame->coded_size();
   ri->ReadbackARGBPixelsAsync(
@@ -216,8 +218,10 @@
       texture_size, src_point, info, base::saturated_cast<GLuint>(rgba_stide),
       dst_pixels,
       WTF::BindOnce(&BackgroundReadback::OnARGBPixelsFrameReadCompleted,
-                    WrapWeakPersistent(this), std::move(result_cb),
-                    std::move(txt_frame), std::move(result)));
+                    WrapWeakPersistent(this), std::move(result_cb), txt_frame,
+                    std::move(result)));
+  media::WaitAndReplaceSyncTokenClient client(ri, std::move(ri_access));
+  txt_frame->UpdateReleaseSyncToken(&client);
 }
 
 void BackgroundReadback::OnARGBPixelsFrameReadCompleted(
@@ -232,17 +236,13 @@
     ReadbackOnThread(std::move(txt_frame), std::move(result_cb));
     return;
   }
-  if (auto* ri = GetSharedGpuRasterInterface()) {
-    media::WaitAndReplaceSyncTokenClient client(ri);
-    txt_frame->UpdateReleaseSyncToken(&client);
-  } else {
-    success = false;
-  }
+
+  auto* ri = GetSharedGpuRasterInterface();
 
   result_frame->set_color_space(txt_frame->ColorSpace());
   result_frame->metadata().MergeMetadataFrom(txt_frame->metadata());
   result_frame->metadata().ClearTextureFrameMetadata();
-  std::move(result_cb).Run(success ? std::move(result_frame) : nullptr);
+  std::move(result_cb).Run(ri ? std::move(result_frame) : nullptr);
 }
 
 void BackgroundReadback::ReadbackRGBTextureBackedFrameToBuffer(
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 612fecc6..5a7c92d 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -2031,7 +2031,8 @@
   return GetDrawingBuffer()->Size();
 }
 
-sk_sp<SkData> WebGLRenderingContextBase::PaintRenderingResultsToRGBADataArray(
+scoped_refptr<StaticBitmapImage>
+WebGLRenderingContextBase::GetRGBAUnacceleratedStaticBitmapImage(
     SourceDrawingBuffer source_buffer) {
   if (isContextLost())
     return nullptr;
@@ -2043,7 +2044,7 @@
   if (!GetDrawingBuffer()->ResolveAndBindForReadAndDraw())
     return nullptr;
   ScopedFramebufferRestorer restorer(this);
-  return GetDrawingBuffer()->PaintRenderingResultsToRGBADataArray(
+  return GetDrawingBuffer()->GetRGBAUnacceleratedStaticBitmapImage(
       source_buffer);
 }
 
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
index 95d52d6..be063b29 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
@@ -585,8 +585,8 @@
 
   void MarkLayerComposited() override;
 
-  sk_sp<SkData> PaintRenderingResultsToRGBADataArray(
-      SourceDrawingBuffer) override;
+  scoped_refptr<StaticBitmapImage> GetRGBAUnacceleratedStaticBitmapImage(
+      SourceDrawingBuffer source_buffer) override;
 
   unsigned MaxVertexAttribs() const { return max_vertex_attribs_; }
 
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
index f542ddf..4c105e9 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
@@ -90,8 +90,7 @@
   UntracedMember<void*> deprecated_ink_bounds_;
   float width;
   unsigned start_index_;
-  unsigned num_characters_;
-  unsigned bitfields : 32;
+  unsigned bitfields;
 };
 
 ASSERT_SIZE(ShapeResult, SameSizeAsShapeResult);
@@ -420,7 +419,6 @@
     : width_(other.width_),
       start_index_(other.start_index_),
       num_characters_(other.num_characters_),
-      num_glyphs_(other.num_glyphs_),
       direction_(other.direction_),
       has_vertical_offsets_(other.has_vertical_offsets_),
       is_applied_spacing_(other.is_applied_spacing_) {
@@ -726,6 +724,23 @@
   return PositionForOffset(offset, adjust_mid_cluster);
 }
 
+bool ShapeResult::HasLigatures() const {
+  for (const Member<ShapeResultRun>& run : runs_) {
+    if (run->HasLigatures()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+unsigned ShapeResult::NumGlyphs() const {
+  unsigned num_glyphs = 0u;
+  for (const Member<ShapeResultRun>& run : runs_) {
+    num_glyphs += run->NumGlyphs();
+  }
+  return num_glyphs;
+}
+
 bool ShapeResult::HasFallbackFonts(const SimpleFontData* primary_font) const {
   for (const Member<ShapeResultRun>& run : runs_) {
     if (run->font_data_ != primary_font) {
@@ -1565,8 +1580,6 @@
     ComputeGlyphPositions<false>(run, start_glyph, num_glyphs, harfbuzz_buffer);
   }
   width_ += run->width_;
-  num_glyphs_ += run->NumGlyphs();
-  DCHECK_GE(num_glyphs_, run->NumGlyphs());
 
   InsertRun(run);
 }
@@ -1615,7 +1628,6 @@
   // RTL runs have glyphs in the descending order of character_index.
   if (IsRtl())
     run->glyph_data_.Reverse();
-  num_glyphs_ += run->NumGlyphs();
   InsertRun(run);
   return run;
 }
@@ -1720,6 +1732,7 @@
 
   unsigned target_run_size_before = target->runs_.size();
   bool should_merge = !target->runs_.empty();
+  bool has_glyphs = false;
   for (; run_index < runs_.size(); run_index++) {
     const auto& run = runs_[run_index];
     unsigned run_start = run->start_index_;
@@ -1733,7 +1746,7 @@
       if (ShapeResultRun* sub_run = run->CreateSubRun(start, end)) {
         sub_run->start_index_ += index_diff;
         target->width_ += sub_run->width_;
-        target->num_glyphs_ += sub_run->glyph_data_.size();
+        has_glyphs |= sub_run->glyph_data_.size();
         if (auto* merged_run =
                 should_merge ? target->runs_.back()->MergeIfPossible(*sub_run)
                              : nullptr) {
@@ -1752,7 +1765,7 @@
     }
   }
 
-  if (!target->num_glyphs_) {
+  if (!has_glyphs) {
     return run_index;
   }
 
@@ -1805,7 +1818,6 @@
 void ShapeResult::CheckConsistency() const {
   if (runs_.empty()) {
     DCHECK_EQ(0u, num_characters_);
-    DCHECK_EQ(0u, num_glyphs_);
     return;
   }
 
@@ -1814,13 +1826,11 @@
 
   const unsigned start_index = StartIndex();
   unsigned index = start_index;
-  unsigned num_glyphs = 0;
   if (IsLtr()) {
     for (const auto& run : runs_) {
       // Characters maybe missing, but must be in increasing order.
       DCHECK_GE(run->start_index_, index);
       index = run->start_index_ + run->num_characters_;
-      num_glyphs += run->glyph_data_.size();
     }
   } else {
     // RTL on Mac may not have runs for the all characters. crbug.com/774034
@@ -1828,13 +1838,11 @@
     for (const auto& run : base::Reversed(runs_)) {
       DCHECK_GE(run->start_index_, index);
       index = run->start_index_ + run->num_characters_;
-      num_glyphs += run->glyph_data_.size();
     }
   }
   const unsigned end_index = EndIndex();
   DCHECK_LE(index, end_index);
   DCHECK_EQ(end_index - start_index, num_characters_);
-  DCHECK_EQ(num_glyphs, num_glyphs_);
 }
 #endif
 
@@ -1850,8 +1858,6 @@
   DCHECK(font_data);
   ShapeResult* result =
       MakeGarbageCollected<ShapeResult>(start_index, length, direction);
-  result->num_glyphs_ = length;
-  DCHECK_EQ(result->num_glyphs_, length);  // no overflow
   result->has_vertical_offsets_ =
       font_data->PlatformData().IsVerticalAnyUpright();
   // Tab characters are always LTR or RTL, not TTB, even when
@@ -1898,8 +1904,6 @@
   DCHECK(font_data);
   ShapeResult* result =
       MakeGarbageCollected<ShapeResult>(start_index, length, direction);
-  result->num_glyphs_ = length;
-  DCHECK_EQ(result->num_glyphs_, length);  // no overflow
   result->has_vertical_offsets_ =
       font_data->PlatformData().IsVerticalAnyUpright();
   hb_direction_t hb_direction =
@@ -1941,7 +1945,6 @@
   run->width_ = std::max(0.0f, stretch_size);
 
   result->width_ = run->width_;
-  result->num_glyphs_ = run->NumGlyphs();
   result->runs_.push_back(run);
 
   return result;
@@ -2006,7 +2009,6 @@
   run->width_ = std::max(0.0f, assembly_parameters.stretch_size);
 
   result->width_ = run->width_;
-  result->num_glyphs_ = run->NumGlyphs();
   result->runs_.push_back(run);
   return result;
 }
@@ -2014,8 +2016,6 @@
 void ShapeResult::ToString(StringBuilder* output) const {
   output->Append("#chars=");
   output->AppendNumber(num_characters_);
-  output->Append(", #glyphs=");
-  output->AppendNumber(num_glyphs_);
   output->Append(", dir=");
   output->AppendNumber(direction_);
   output->Append(", runs[");
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result.h
index c18c03f..d1472d36 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result.h
@@ -180,7 +180,9 @@
   float Width() const { return width_; }
   LayoutUnit SnappedWidth() const { return LayoutUnit::FromFloatCeil(width_); }
   unsigned NumCharacters() const { return num_characters_; }
-  unsigned NumGlyphs() const { return num_glyphs_; }
+
+  bool HasLigatures() const;
+  unsigned NumGlyphs() const;
   bool HasFallbackFonts(const SimpleFontData* primary_font) const;
 
   // TODO(eae): Remove start_x and return value once ShapeResultBuffer has been
@@ -514,8 +516,7 @@
   mutable float width_ = 0;
 
   unsigned start_index_ = 0;
-  unsigned num_characters_ = 0;
-  unsigned num_glyphs_ : 29 = 0;
+  unsigned num_characters_ : 29 = 0;
 
   // Overall direction for the TextRun, dictates which order each individual
   // sub run (represented by ShapeResultRun structs in the m_runs vector) can
@@ -529,7 +530,7 @@
   unsigned is_applied_spacing_ : 1 = false;
 
   // Note: When you add more bit flags, please consider to reduce size of
-  // |num_glyphs_| or |num_characters_|.
+  // |num_characters_|.
 
  private:
   friend class HarfBuzzShaper;
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_run.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result_run.h
index fd9d180b..cca612a 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_run.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_run.h
@@ -89,6 +89,7 @@
   }
 
   unsigned NumGlyphs() const { return glyph_data_.size(); }
+  bool HasLigatures() const { return NumGlyphs() < num_characters_; }
   bool IsLtr() const { return HB_DIRECTION_IS_FORWARD(direction_); }
   bool IsRtl() const { return HB_DIRECTION_IS_BACKWARD(direction_); }
   bool IsHorizontal() const { return HB_DIRECTION_IS_HORIZONTAL(direction_); }
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc
index 2260bfc..ea2d258 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc
@@ -184,7 +184,6 @@
   ShapeResult* new_result = MakeGarbageCollected<ShapeResult>(
       start_index_ + char_index_offset_, num_characters_, Direction());
   new_result->runs_.ReserveInitialCapacity(parts_.size());
-  unsigned num_glyphs = 0u;
   for (const auto& part : RunsOrParts()) {
     auto* new_run = MakeGarbageCollected<ShapeResultRun>(
         part.run_->font_data_.Get(), part.run_->direction_,
@@ -202,10 +201,8 @@
     new_run->num_characters_ = part.num_characters_;
     new_run->CheckConsistency();
     new_result->runs_.push_back(new_run);
-    num_glyphs += part.NumGlyphs();
   }
 
-  new_result->num_glyphs_ = num_glyphs;
   new_result->has_vertical_offsets_ = has_vertical_offsets_;
   new_result->width_ = width_;
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index 4488e31..d1db4f4 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -678,6 +678,7 @@
     is_cleared_ = true;
     RasterRecordOOP(std::move(last_recording), needs_clear,
                     resource()->GetClientSharedImage()->mailbox());
+    resource()->GetSyncToken();
   }
 
   bool ShouldReplaceTargetBuffer(
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index 9eccd730..107c037 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -1753,7 +1753,8 @@
   return requested_format_;
 }
 
-sk_sp<SkData> DrawingBuffer::PaintRenderingResultsToRGBADataArray(
+scoped_refptr<StaticBitmapImage>
+DrawingBuffer::GetRGBAUnacceleratedStaticBitmapImage(
     SourceDrawingBuffer source_buffer) {
   ScopedStateRestorer scoped_state_restorer(this);
 
@@ -1808,7 +1809,11 @@
     front_color_buffer_->EndAccess();
   }
 
-  return dst_buffer;
+  return StaticBitmapImage::Create(
+      std::move(dst_buffer),
+      SkImageInfo::Make(SkISize::Make(Size().width(), Size().height()),
+                        color_type, kUnpremul_SkAlphaType,
+                        color_space_.ToSkColorSpace()));
 }
 
 void DrawingBuffer::ReadBackFramebuffer(base::span<uint8_t> pixels,
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
index e50a6017..6e485c25 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
@@ -293,7 +293,8 @@
       const gfx::ColorSpace& dst_color_space,
       WebGraphicsContext3DVideoFramePool::FrameReadyCallback callback);
 
-  sk_sp<SkData> PaintRenderingResultsToRGBADataArray(SourceDrawingBuffer);
+  scoped_refptr<StaticBitmapImage> GetRGBAUnacceleratedStaticBitmapImage(
+      SourceDrawingBuffer source_buffer);
 
   int SampleCount() const { return sample_count_; }
   bool ExplicitResolveOfMultisampleData() const {
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
index 50f3a54..24a22829 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
@@ -189,10 +189,8 @@
       resource_provider_(std::move(resource_provider)),
       roughness_reporter_(std::make_unique<cc::VideoPlaybackRoughnessReporter>(
           std::move(roughness_reporting_callback))),
-      frame_trackers_(false, nullptr),
-      frame_sorter_(base::BindRepeating(
-          &cc::FrameSequenceTrackerCollection::AddSortedFrame,
-          base::Unretained(&frame_trackers_))) {
+      frame_trackers_(false, nullptr) {
+  frame_sorter_.AddObserver(&frame_trackers_);
   DETACH_FROM_THREAD(thread_checker_);
 }
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 4d024f8..e15c6bc 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2041,15 +2041,6 @@
       base_feature: "none",
     },
     {
-      name: "FedCmWithStorageAccessAPI",
-      depends_on: ["FedCm"],
-      base_feature_status: "enabled",
-      browser_process_read_access: true,
-      origin_trial_feature_name: "FedCmWithStorageAccessAPI",
-      copied_from_base_feature_if: "overridden",
-      status: "stable",
-    },
-    {
       name: "FencedFrames",
       base_feature: "none",
       // This helps enable and expose the <fencedframe> element, but note that
@@ -2770,6 +2761,10 @@
       name: "LayoutIgnoreMarginsForSticky",
     },
     {
+      name: "LayoutIsAnonymousBlockFix",
+      status: "stable",
+    },
+    {
       name: "LayoutMinSizeIndefinite",
       status: "stable",
     },
@@ -2786,10 +2781,6 @@
       status: "experimental",
     },
     {
-      name: "LayoutWebkitBoxTreeFix",
-      status: "stable",
-    },
-    {
       name: "LazyInitializeMediaControls",
       base_feature: "none",
       public: true,
@@ -4184,7 +4175,6 @@
       // select option colors. https://issues.chromium.org/issues/40788356
       // This is shipping in M136 and can be removed in M138
       name: "SelectColorsRemoveImportant",
-      status: "stable",
     },
     {
       // The selectedcontentelement attribute is for connecting select elements
@@ -4922,6 +4912,7 @@
       // This flag uses the original DOM text for input when the password
       // echo is enabled to create the offset_map.
       name: "UseOriginalDomOffsetsForOffsetMap",
+      status: "stable",
     },
     {
       name: "UsePositionForPointInFlexibleBoxWithSingleChildElement",
diff --git a/third_party/blink/tools/blinkpy/common/message_pool.py b/third_party/blink/tools/blinkpy/common/message_pool.py
index 871ba30..b20b6c9 100644
--- a/third_party/blink/tools/blinkpy/common/message_pool.py
+++ b/third_party/blink/tools/blinkpy/common/message_pool.py
@@ -45,8 +45,9 @@
 import queue
 import six
 import sys
+import threading
 import traceback
-from typing import Any, Callable, Optional, Protocol
+from typing import Any, Callable, Iterator, Optional, Protocol, Tuple
 
 from blinkpy.common.host import Host
 from blinkpy.common.system import stack_utils
@@ -111,6 +112,11 @@
         self._host = host
         self._name = 'manager'
         self._running_inline = (self._num_workers == 1)
+        # When both `_messages_to_*` queues are saturated, further `put()`s on
+        # either will block. The `_producer_thread` avoids the deadlock by
+        # asynchronously feeding the workers, which frees up `run()` to handle
+        # worker -> manager messages.
+        self._producer_thread = None
         if self._running_inline:
             self._messages_to_worker = queue.Queue()
             self._messages_to_manager = queue.Queue()
@@ -125,9 +131,25 @@
         self._close()
         return False
 
-    def run(self, shards):
+    def run(self, messages: Iterator[Tuple[Any, ...]]):
         """Posts a list of messages to the pool and waits for them to complete."""
-        for message in shards:
+        assert not self._producer_thread
+        if self._running_inline:
+            # `queue.Queue.put()` shouldn't block because it's allocated
+            # in-memory with no `maxsize`.
+            self._message_workers(messages)
+        else:
+            self._producer_thread = threading.Thread(
+                target=self._message_workers,
+                args=(messages, ),
+                name='message-pool-producer',
+                daemon=True)
+            self._producer_thread.start()
+
+        self.wait()
+
+    def _message_workers(self, messages: Iterator[Tuple[Any, ...]]):
+        for message in messages:
             self._messages_to_worker.put(
                 _Message(
                     self._name,
@@ -145,8 +167,6 @@
                     from_user=False,
                     logs=()))
 
-        self.wait()
-
     def _start_workers(self):
         assert not self._workers
         self._workers_stopped = set()
@@ -208,6 +228,13 @@
                 worker.join(1)
         self._workers.clear()
 
+        if producer_thread := self._producer_thread:
+            self._producer_thread = None
+            producer_thread.join(10)
+            if producer_thread.is_alive():
+                raise WorkerException(
+                    "message pool producer thread didn't join in time")
+
     def _log_messages(self, messages):
         for message in messages:
             logging.root.handle(message)
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index ab5841f..a3e9ba2 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -1568,6 +1568,7 @@
             'gpu::Mailbox',
             'gpu::MailboxHolder',
             'gpu::raster::RasterInterface',
+            'gpu::RasterScopedAccess',
             'gpu::SHARED_IMAGE_USAGE_.+',
             'gpu::SharedImageInterface',
             'gpu::SharedImageTexture',
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base.py b/third_party/blink/tools/blinkpy/web_tests/port/base.py
index ebf62f98..2ec04ea 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base.py
@@ -73,6 +73,7 @@
 from blinkpy.w3c.wpt_manifest import (
     FuzzyRange,
     FuzzyParameters,
+    Relation,
     WPTManifest,
     MANIFEST_NAME,
 )
@@ -1079,8 +1080,10 @@
                 return True
         return False
 
-    def reference_files(self, test_name):
+    def reference_files(self, test_name: str) -> list[tuple[Relation, str]]:
         """Returns a list of expectation (== or !=) and filename pairs"""
+        if match := self.WPT_REGEX.match(test_name):
+            return self._wpt_references_files(match.group(1), match.group(2))
 
         # Try to find -expected.* or -expected-mismatch.* in the same directory.
         reftest_list = []
@@ -1091,15 +1094,12 @@
                                               match=(expectation == '=='))
                 if self._filesystem.exists(path):
                     reftest_list.append((expectation, path))
-        if reftest_list:
-            return reftest_list
+        return reftest_list
 
+    def _wpt_references_files(self, wpt_path: str,
+                              path_in_wpt: str) -> list[tuple[Relation, str]]:
         # Try to extract information from MANIFEST.json.
-        match = self.WPT_REGEX.match(test_name)
-        if not match:
-            return []
-        wpt_path = match.group(1)
-        path_in_wpt = match.group(2)
+        reftest_list = []
         for expectation, ref_path_in_wpt in self.wpt_manifest(
                 wpt_path).extract_reference_list(path_in_wpt):
             if ref_path_in_wpt.startswith('about:'):
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index aab7fff..688f917 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -8867,6 +8867,9 @@
 crbug.com/827209 [ Linux ] fast/events/middleClickAutoscroll-latching.html [ Failure Pass Timeout ]
 crbug.com/827209 [ Mac ] fast/events/middleClickAutoscroll-latching.html [ Failure Pass Timeout ]
 
+# Temporarily disabled to unblock https://crrev.com/c/6519316
+crbug.com/391381439 http/tests/devtools/sources/debugger/async-callstack-network-initiator.js [ Failure Pass Timeout ]
+
 # Gardener 2024-10-11
 crbug.com/372293920 [ Mac11 ] wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https.html [ Crash Failure Pass Timeout ]
 crbug.com/372293920 [ Mac11-arm64 ] wpt_internal/webxr/ar/ar_hittestsource_lifetimes.https.html [ Crash Failure Pass Timeout ]
@@ -9220,3 +9223,11 @@
 
 # Gardener 2025-05-07
 crbug.com/416123241 [ Linux ] fast/forms/select/customizable-select/disallowed-select-descendants-console-message.html [ Failure Pass ]
+
+# Temporarily skipped to land https://crrev.com/c/6502712, after which expectations will be updated
+crbug.com/414525205 http/tests/devtools/console/console-functions.js [ Failure Pass ]
+crbug.com/414525205 http/tests/devtools/sources/debugger-ui/watch-expressions-preserve-expansion.js [ Failure Pass ]
+crbug.com/414525205 http/tests/devtools/sources/debugger/properties-special.js [ Failure Pass ]
+crbug.com/414525205 external/wpt/webidl/ecmascript-binding/builtin-function-properties.any.html [ Failure Pass ]
+crbug.com/414525205 external/wpt/webidl/ecmascript-binding/builtin-function-properties.any.worker.html [ Failure Pass ]
+crbug.com/414525205 external/wpt/webidl/ecmascript-binding/legacy-factory-function-builtin-properties.window.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/wpt/css/geometry/DOMMatrix-001-expected.txt b/third_party/blink/web_tests/external/wpt/css/geometry/DOMMatrix-001-expected.txt
new file mode 100644
index 0000000..eb6bb23
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/geometry/DOMMatrix-001-expected.txt
@@ -0,0 +1,16 @@
+This is a testharness.js-based test.
+Found 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
+[FAIL] new DOMMatrix("scale(sign(1em))")
+  assert_throws_dom: function "function() { new self[constr](string); }" did not throw
+[FAIL] new DOMMatrix("translateX(calc(10px * sign(1em - 10px)))")
+  assert_throws_dom: function "function() { new self[constr](string); }" did not throw
+[FAIL] new DOMMatrix("rotate(calc(5deg * sign(1em - 10px)))")
+  assert_throws_dom: function "function() { new self[constr](string); }" did not throw
+[FAIL] new DOMMatrixReadOnly("scale(sign(1em))")
+  assert_throws_dom: function "function() { new self[constr](string); }" did not throw
+[FAIL] new DOMMatrixReadOnly("translateX(calc(10px * sign(1em - 10px)))")
+  assert_throws_dom: function "function() { new self[constr](string); }" did not throw
+[FAIL] new DOMMatrixReadOnly("rotate(calc(5deg * sign(1em - 10px)))")
+  assert_throws_dom: function "function() { new self[constr](string); }" did not throw
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/geometry/DOMMatrix-001.html b/third_party/blink/web_tests/external/wpt/css/geometry/DOMMatrix-001.html
index f578da0..3436e17c 100644
--- a/third_party/blink/web_tests/external/wpt/css/geometry/DOMMatrix-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/geometry/DOMMatrix-001.html
@@ -113,6 +113,8 @@
             ['translateX    (5px)',
              'scale(2 2) translateX(5) translateY(5)',
              'scale(2, 2), translateX(5)  ,translateY(5)',
+             'scale(sign(1em))',
+             'scale(sibling-index())',
              'translateX(5em)',
              'translateX(5ex)',
              'translateX(5ch)',
@@ -130,10 +132,14 @@
              'translateX(5vmin)',
              'translateX(5vmax)',
              'translateX(5%)',
+             'translateX(calc(10px * sign(1em - 10px)))',
+             'translateX(calc(10px * sibling-index()))',
              'rotate(5)',
              'rotate(5, 5, 5)',
              'rotate(5, 5px, 5px)',
              'rotate(5deg, 5px, 5px)',
+             'rotate(calc(5deg * sign(1em - 10px)))',
+             'rotate(calc(5deg * sibling-index()))',
              ' ',
              '/**/',
              '\0',
diff --git a/third_party/blink/web_tests/external/wpt/fetch/local-network-access/fetch.tentative.https.html b/third_party/blink/web_tests/external/wpt/fetch/local-network-access/fetch.tentative.https.html
index a01bad6a..649dd23 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/local-network-access/fetch.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/fetch/local-network-access/fetch.tentative.https.html
@@ -16,21 +16,36 @@
         resolveUrl("resources/fetch-private.html", sourceResolveOptions(source));
 
     function checkResult(evt) {
-      const { error, ok, type, body } = evt.data;
-
-      assert_equals(ok, true, "response ok mismatch");
-      assert_equals(body, "success", "response body mismatch");
-
+      checkTestResult(evt.data, FetchTestResult.SUCCESS);
       t.done();
     }
 
     const promise = new Promise((resolve) => {
-                      window.addEventListener('message', resolve);
+                      window.addEventListener('message', resolve, {once: true});
                     }).then(checkResult);
     const popup = window.open(sourceUrl);
     t.add_cleanup(() => popup.close());
 
     return promise;
-}, 'LNA Public to private with permission');
+  }, 'LNA Public to private with permission');
+
+  promise_test(t => {
+    const source = { server: Server.HTTPS_PUBLIC };
+    const sourceUrl = resolveUrl("resources/fetch-private-permission-denied.html",
+        sourceResolveOptions(source));
+
+    function checkResult(evt) {
+      checkTestResult(evt.data, FetchTestResult.FAILURE);
+      t.done();
+    }
+
+    const promise = new Promise((resolve) => {
+                      window.addEventListener('message', resolve, {once: true});
+                    }).then(checkResult);
+    const popup = window.open(sourceUrl);
+    t.add_cleanup(() => popup.close());
+
+    return promise;
+  }, 'LNA Public to private with permission denied');
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/fetch/local-network-access/resources/fetch-private-permission-denied.html b/third_party/blink/web_tests/external/wpt/fetch/local-network-access/resources/fetch-private-permission-denied.html
new file mode 100644
index 0000000..7368cf5f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fetch/local-network-access/resources/fetch-private-permission-denied.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Fetch Private resource</title>
+
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="support.sub.js"></script>
+<script>
+"use strict";
+
+// Deny 'local-network-access' permission then attempt to fetch a resource in
+// the private address space.
+Promise.resolve().then(async () => {
+  test_driver.set_test_context(opener);
+  await test_driver.set_permission({ name: 'local-network-access' }, 'denied');
+
+  const target = {
+    server: Server.HTTPS_PRIVATE,
+    behavior: { response: ResponseBehavior.allowCrossOrigin() },
+  };
+  const targetUrl = resolveTargetUrl(target);
+
+  fetch(targetUrl)
+      .then(async function(response) {
+        const body = await response.text();
+        const message = {
+          ok: response.ok,
+          type: response.type,
+          body: body,
+        };
+        opener.postMessage(message, "*");
+      })
+      .catch(error => {
+        opener.postMessage({ error: error.toString() }, "*");
+      });
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/fetch/local-network-access/resources/support.sub.js b/third_party/blink/web_tests/external/wpt/fetch/local-network-access/resources/support.sub.js
index 299f004..ef343b9 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/local-network-access/resources/support.sub.js
+++ b/third_party/blink/web_tests/external/wpt/fetch/local-network-access/resources/support.sub.js
@@ -173,3 +173,15 @@
     error: "TypeError: Failed to fetch",
   },
 };
+
+
+// Helper function for checking results from fetch tests.
+function checkTestResult(actual, expected) {
+  assert_equals(actual.error, expected.error, "error mismatch");
+  assert_equals(actual.ok, expected.ok, "response ok mismatch");
+  assert_equals(actual.body, expected.body, "response body mismatch");
+
+  if (expected.type !== undefined) {
+    assert_equals(type, expected.type, "response type mismatch");
+  }
+}
diff --git a/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-basic-filtering.tentative.html b/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-basic-filtering.tentative.html
index 0b6924b..38c764ae 100644
--- a/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-basic-filtering.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-basic-filtering.tentative.html
@@ -273,6 +273,15 @@
       div.setHTMLUnsafe(testcase.data, config);
       assert_testcase(div, testcase);
     }, `setHTMLUnsafe testcase ${group.id}/${index}, "${testcase.data}"`);
+
+    // parseHTML and parseHTMLUnsafe need to allow "html" and "body" for these
+    // tests to be useful. Update the config, if necessary.
+    if (config && config["sanitizer"] && config["sanitizer"]["elements"]) {
+      config["sanitizer"] = new Sanitizer(config["sanitizer"]);
+      config["sanitizer"].allowElement("body");
+      config["sanitizer"].allowElement("html");
+    }
+
     test(_ => {
       assert_testcase(
         Document.parseHTML(testcase.data, config).body, testcase);
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/inspector-protocol/layout-fonts/languages-emoji-rare-glyphs-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/inspector-protocol/layout-fonts/languages-emoji-rare-glyphs-expected.txt
deleted file mode 100644
index dbc7b9a..0000000
--- a/third_party/blink/web_tests/platform/mac-mac15-arm64/inspector-protocol/layout-fonts/languages-emoji-rare-glyphs-expected.txt
+++ /dev/null
@@ -1,60 +0,0 @@
-百家姓 趙錢孫李 周吳鄭王 馮陳褚衛 蔣沈韓楊 朱秦尤許 何呂施張 孔曹嚴華 金魏陶薑 戚謝鄒喻 柏水竇章 雲蘇潘葛 奚範彭郎 魯韋昌馬 苗鳳花方 俞任袁柳 酆鮑史唐 費廉岑薛 雷賀倪湯 滕殷羅畢 郝鄔安常 樂於時傅 皮卞齊康 伍餘元蔔 顧孟平黃 和穆蕭尹 姚邵堪汪 祁毛禹狄 米貝明臧 計伏成戴 談宋茅龐 熊紀舒屈 項祝董梁 杜阮藍閔 席季麻強 賈路婁危 江童顏郭 梅盛林刁 鍾徐邱駱 高夏蔡田 樊胡淩霍 虞萬支柯 昝管盧莫 經房裘繆 幹解應宗 丁宣賁鄧 鬱單杭洪 包諸左石 崔吉鈕龔 程嵇邢滑 裴陸榮翁 荀羊於惠 甄曲家封 芮羿儲靳 汲邴糜松 井段富巫 烏焦巴弓 牧隗山穀 車侯宓蓬 全郗班仰 秋仲伊宮 寧仇欒暴 甘鈄厲戎 祖武符劉 景詹束龍 葉幸司韶 郜黎薊薄 印宿白懷 蒲台從鄂 索鹹籍賴 卓藺屠蒙 池喬陰鬱 胥能蒼雙 聞莘黨翟 譚貢勞逄 姬申扶堵 冉宰酈雍 卻璩桑桂 濮牛壽通 邊扈燕冀 郟浦尚農 溫別莊晏 柴瞿閻充 慕連茹習 宦艾魚容 向古易慎 戈廖庚終 暨居衡步 都耿滿弘 匡國文寇 廣祿闕東 毆殳沃利 蔚越夔隆 師鞏厙聶 晁勾敖融 冷訾辛闞 那簡饒空 曾毋沙乜 養鞠須豐 巢關蒯相 查後荊紅 遊竺權逯 蓋益桓公 萬俟司馬 上官歐陽 夏侯諸葛 聞人東方 赫連皇甫 尉遲公羊 澹台公冶 宗政濮陽 淳於單於 太叔申屠 公孫仲孫 軒轅令狐 鐘離宇文 長孫慕容 鮮於閭丘 司徒司空 亓官司寇 仉督子車 顓孫端木 巫馬公西 漆雕樂正 壤駟公良 拓拔夾穀 宰父穀粱 晉楚閆法 汝鄢塗欽 段幹百里 東郭南門 呼延歸海 羊舌微生 嶽帥緱亢 況後有琴 梁丘左丘 東門西門 商牟佘佴 伯賞南宮 墨哈譙笪 年愛陽佟
-#hundred_chinese_surnames:
-"Songti SC" : 563,
-"Times" : 140
-
-いろはにほへと ちりぬるを わかよたれそ つねならむ うゐのおくやま けふこえて あさき ゆめみし ゑひもせす(ん)色は匂へど 散りぬるを 我が世誰ぞ 常ならむ 有為の奥山 今日越えて 浅き夢見じ 酔ひもせず(ん)
-#japanese_iroha:
-"Songti SC" : 92,
-"Times" : 15
-
-키스의 고유조건은 입술끼리 만나야 하고 특별한 기술은 필요치 않다.
-#korean_pangram:
-"AppleMyungjo" : 28,
-"Times" : 9
-
-ऋषियों को सताने वाले दुष्ट राक्षसों के राजा रावण का सर्वनाश करने वाले विष्णुवतार भगवान श्रीराम, अयोध्या के महाराज दशरथ के बड़े सपुत्र थे।
-#hindi_pangram:
-"ITF Devanagari" : 97,
-"Times" : 24
-
-نصٌّ حكيمٌ لهُ سِرٌّ قاطِعٌ وَذُو شَأنٍ عَظيمٍ مكتوبٌ على ثوبٍ أخضرَ ومُغلفٌ بجلدٍ أزرق
-#arabic_pangram:
-"Geeza Pro" : 71,
-"Times" : 14
-
-🌱🌲🌳🌴🌵🌷🌸🌹🌺🌻🌼💐🌾🌿🍀🍁🍂🍃🍄🌰☺️😀👪♐☔☃️™️
-#emoji_emoji:
-"Apple Color Emoji" : 27
-
-☔︎♐︎☺☺︎☃☃︎☯︎☯™™︎00︎
-#text_emoji:
-"Times" : 4,
-"Apple Symbols" : 2,
-"AppleMyungjo" : 2,
-"Hiragino Mincho ProN" : 2,
-"Times New Roman" : 2
-
-𓀀𓀁𓀂𓀃𓀄𓀅𓀆𓀇𓀈𓀉𓀊𓀋𓀌𓀍𓀎𓀏
-#egyptian_hieroglyphs:
-"Noto Sans EgyptHiero" : 16
-
-ខ្ញុំអាចញុំកញ្ចក់បាន ដោយគ្មានបញ្ហារ
-#khmer:
-"Khmer MN" : 26,
-"Times" : 1
-
-𐌲𐌿𐍄𐌹𐍃𐌺
-#gothic:
-"Noto Sans Gothic" : 6
-
-ܐܬܘܪܝܐ
-#syriac:
-"Noto Sans Syriac" : 6
-
-⇦⇧⇨⇩←↑→↓⟀
-#text_presentation_arrows_maths:
-"Apple Symbols" : 5,
-"Hiragino Mincho ProN" : 4
-
-
diff --git a/third_party/blink/web_tests/platform/mac/inspector-protocol/layout-fonts/languages-emoji-rare-glyphs-expected.txt b/third_party/blink/web_tests/platform/mac/inspector-protocol/layout-fonts/languages-emoji-rare-glyphs-expected.txt
index e92cd8e..dbc7b9a 100644
--- a/third_party/blink/web_tests/platform/mac/inspector-protocol/layout-fonts/languages-emoji-rare-glyphs-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/inspector-protocol/layout-fonts/languages-emoji-rare-glyphs-expected.txt
@@ -20,7 +20,7 @@
 
 نصٌّ حكيمٌ لهُ سِرٌّ قاطِعٌ وَذُو شَأنٍ عَظيمٍ مكتوبٌ على ثوبٍ أخضرَ ومُغلفٌ بجلدٍ أزرق
 #arabic_pangram:
-"Geeza Pro" : 69,
+"Geeza Pro" : 71,
 "Times" : 14
 
 🌱🌲🌳🌴🌵🌷🌸🌹🌺🌻🌼💐🌾🌿🍀🍁🍂🍃🍄🌰☺️😀👪♐☔☃️™️
diff --git a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/emphasis-complex-expected.png b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/emphasis-complex-expected.png
index b1bf9e94..758989e 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/emphasis-complex-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/emphasis-complex-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/font-fallback-expected.png b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/font-fallback-expected.png
index 60867ca..435bf3a 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/font-fallback-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/font-fallback-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/justify-ideograph-vertical-expected.png b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/justify-ideograph-vertical-expected.png
index 0d6c8a7..46dfeb0 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/text-antialias/justify-ideograph-vertical-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/text-antialias/justify-ideograph-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/transforms/cssmatrix-2d-interface-expected.txt b/third_party/blink/web_tests/transforms/cssmatrix-2d-interface-expected.txt
index 231a294..e0824fa 100644
--- a/third_party/blink/web_tests/transforms/cssmatrix-2d-interface-expected.txt
+++ b/third_party/blink/web_tests/transforms/cssmatrix-2d-interface-expected.txt
@@ -67,7 +67,7 @@
 
 Test throwing exception from setMatrixValue
 PASS m.setMatrixValue("banana") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Failed to parse 'banana'..
-PASS m.setMatrixValue("translate(10em, 20%)") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Lengths must be absolute, not relative.
+PASS m.setMatrixValue("translate(10em, 20%)") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Values must be resolvable at parse time.
 PASS m.setMatrixValue("translate(10px, 20px) scale()") threw exception SyntaxError: Failed to execute 'setMatrixValue' on 'DOMMatrix': Failed to parse 'translate(10px, 20px) scale()'..
 
 
diff --git a/third_party/blink/web_tests/virtual/text-antialias/mac-fallback-font-for-character-expected.png b/third_party/blink/web_tests/virtual/text-antialias/mac-fallback-font-for-character-expected.png
index e9bcfea..4b516f6 100644
--- a/third_party/blink/web_tests/virtual/text-antialias/mac-fallback-font-for-character-expected.png
+++ b/third_party/blink/web_tests/virtual/text-antialias/mac-fallback-font-for-character-expected.png
Binary files differ
diff --git a/third_party/compiler-rt/src b/third_party/compiler-rt/src
index cff6ae8..16c0e42 160000
--- a/third_party/compiler-rt/src
+++ b/third_party/compiler-rt/src
@@ -1 +1 @@
-Subproject commit cff6ae8a80960ceca4d83cf6efa271222d2e7d85
+Subproject commit 16c0e42913a45357f0e557dcc0b7055129c78af6
diff --git a/third_party/crossbench b/third_party/crossbench
index 47e5e3d..35e8177a 160000
--- a/third_party/crossbench
+++ b/third_party/crossbench
@@ -1 +1 @@
-Subproject commit 47e5e3d994ddcc2319cec110ce670ccc1baa3223
+Subproject commit 35e8177a7d7594203c543fe3875fd587b949fca2
diff --git a/third_party/dawn b/third_party/dawn
index 968646ae..5b595fd 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit 968646ae6a99b1751d5a7a4298e3bf6976123326
+Subproject commit 5b595fdbcfc1d0d79354ab74f675c564273874c7
diff --git a/third_party/depot_tools b/third_party/depot_tools
index 18580cf..fab0a42 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit 18580cf9c407a3c23aa072b9d745832516de699e
+Subproject commit fab0a4296b8830c569a4dc82285bfb58a5c2fca8
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 28fbee1..cd65015 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 28fbee169c8a26c6c2ed2ec2550465fe3d697a34
+Subproject commit cd650159ff9210d83ed1e1aaf02b1c071d5123b3
diff --git a/third_party/libc++/src b/third_party/libc++/src
index a9cc573..a01c02c 160000
--- a/third_party/libc++/src
+++ b/third_party/libc++/src
@@ -1 +1 @@
-Subproject commit a9cc573e7c591795d11b72d8323ba0e573b55bd6
+Subproject commit a01c02c9d4acbdae3b7e8a2f3ee58579a9c29f96
diff --git a/third_party/skia b/third_party/skia
index c8f54c1..ac2d5cee 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit c8f54c1bc565b8a7eced0a77088b228efdc70c2c
+Subproject commit ac2d5ceecb588e3be2a798896b92d27c91ad6461
diff --git a/tools/crates/gnrt/Cargo.lock b/tools/crates/gnrt/Cargo.lock
index a7e7268f..e60625e 100644
--- a/tools/crates/gnrt/Cargo.lock
+++ b/tools/crates/gnrt/Cargo.lock
@@ -517,7 +517,6 @@
 version = "0.0.0"
 dependencies = [
  "anyhow",
- "bitflags 2.9.0",
  "cargo-platform",
  "clap",
  "env_logger",
diff --git a/tools/crates/gnrt/Cargo.toml b/tools/crates/gnrt/Cargo.toml
index 578127c..57e439ba 100644
--- a/tools/crates/gnrt/Cargo.toml
+++ b/tools/crates/gnrt/Cargo.toml
@@ -14,7 +14,6 @@
 
 [dependencies]
 anyhow = "1"
-bitflags = "2.9.0"
 cargo-platform = "0.1"
 clap = {version = "4", features = ["derive"]}
 guppy = "0.17.18"
diff --git a/tools/crates/gnrt/build/build.rs b/tools/crates/gnrt/build/build.rs
index e16be25..b97be06 100644
--- a/tools/crates/gnrt/build/build.rs
+++ b/tools/crates/gnrt/build/build.rs
@@ -11,7 +11,7 @@
 //! https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts
 
 use anyhow::{ensure, Result};
-use heck::{ToPascalCase, ToShoutySnakeCase};
+use heck::ToPascalCase;
 use itertools::Itertools;
 use proc_macro2::TokenStream;
 use quote::{format_ident, quote};
@@ -27,10 +27,6 @@
     format_ident!("{}", s.to_pascal_case())
 }
 
-fn format_const_ident(s: &str) -> syn::Ident {
-    format_ident!("{}", s.to_shouty_snake_case())
-}
-
 /// Represents one line of output from `rustc --print=cfg <triple_name>`.
 struct CfgLine {
     triple_name: &'static str,
@@ -71,7 +67,7 @@
         .iter()
         .filter_map(|cfg| {
             if cfg.property_name == property_name {
-                Some((format_const_ident(cfg.triple_name), format_ident(&cfg.property_value)))
+                Some((format_ident(cfg.triple_name), format_ident(&cfg.property_value)))
             } else {
                 None
             }
@@ -82,6 +78,7 @@
         .clone()
         .into_iter()
         .map(|(_, property_value)| property_value)
+        .sorted()
         .unique()
         .collect_vec();
     let (triple_idents, property_value_idents) =
@@ -90,17 +87,14 @@
         #doc_string
         #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
         pub enum #enum_ident {
-            #( #unique_property_value_idents ),*
+            #( #unique_property_value_idents, )*
         }
 
-        impl TryFrom<RustTargetTriple> for #enum_ident {
-            type Error = anyhow::Error;
-
-            fn try_from(value: RustTargetTriple) -> Result<Self> {
+        impl From<RustTargetTriple> for #enum_ident {
+            fn from(value: RustTargetTriple) -> Self {
                 match value {
                     #( RustTargetTriple :: #triple_idents
-                        => Ok(#enum_ident :: #property_value_idents), )*
-                    other => Err(anyhow!("Not a single triple?: {other:?}")),
+                        => #enum_ident :: #property_value_idents, )*
                 }
             }
         }
@@ -117,7 +111,7 @@
         .filter(|line| !line.trim().is_empty())
         .sorted()
         .collect_vec();
-    let triple_idents = triple_names.iter().map(|triple| format_const_ident(triple)).collect_vec();
+    let triple_idents = triple_names.iter().map(|triple| format_ident(triple)).collect_vec();
     let cfg_lines = triple_names
         .iter()
         .map(|&triple| get_cfg_lines(triple))
@@ -140,7 +134,7 @@
             /// * The 1st tuple element is the name of a property (e.g. `target_env`)
             /// * The 2nd tuple element is the value of the property (e.g. `msvc`)
             pub static RUST_TRIPLE_PROPERTIES: [(&str, &str, &str); #len] = [
-                #( (#tuple_elem0s, #tuple_elem1s, #tuple_elem2s) ),*
+                #( (#tuple_elem0s, #tuple_elem1s, #tuple_elem2s), )*
             ];
         }
     };
@@ -165,48 +159,42 @@
         &mut output_file,
         quote! {
             use anyhow::{anyhow, Result};
-            use bitflags::bitflags;
-            use std::convert::TryFrom;
+            use std::convert::From;
+            use std::collections::HashSet;
             use std::str::FromStr;
-        },
-    )?;
-    writeln!(&mut output_file)?;
+            use std::sync::LazyLock;
 
-    // `bitflags!` doesn't format well using `prettyplease`, so let's just
-    // manually `writeln!` it out.
-    writeln!(
-        &mut output_file,
-        r#"
-bitflags! {{
-    /// `RustTargetTriple` enum exhaustively names all target triples that are
-    /// supported by Chromium when compiling Rust libraries.
-    #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
-    pub struct RustTargetTriple: u64 {{"#
-    )?;
-    for (index, ident) in triple_idents.iter().enumerate() {
-        writeln!(&mut output_file, "        const {ident} = 1 << {index};")?;
-    }
-    writeln!(&mut output_file, "    }}")?;
-    writeln!(&mut output_file, "}}")?;
+            /// `RustTargetTriple` enum exhaustively names all target triples that are
+            /// supported by Chromium when compiling Rust libraries.
+            #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+            pub enum RustTargetTriple {
+                #( #triple_idents, )*
+            }
 
-    write_pretty_file_fragment(
-        &mut output_file,
-        quote! {
             impl RustTargetTriple {
-                pub fn as_triple_name(&self) -> Result<&'static str> {
+                pub fn as_triple_name(&self) -> &'static str {
                     match *self {
-                        #( RustTargetTriple :: #triple_idents
-                            => Ok(#triple_names), )*
-                        other => Err(anyhow!("Not a single triple?: {other:?}")),
+                        #( RustTargetTriple :: #triple_idents => #triple_names, )*
                     }
                 }
+
+                /// A set of all possible/known triples.
+                pub fn all() -> &'static HashSet<RustTargetTriple> {
+                    static ALL_TRIPLES: LazyLock<HashSet<RustTargetTriple>> =
+                        LazyLock::new(|| {
+                            [
+                                #( RustTargetTriple :: #triple_idents, )*
+                            ].into_iter().collect()
+                        });
+                    &ALL_TRIPLES
+                }
             }
 
             impl FromStr for RustTargetTriple {
                 type Err = anyhow::Error;
                 fn from_str(input: &str) -> Result<RustTargetTriple> {
                     for (s, t) in &[
-                        #( (#triple_names, RustTargetTriple::#triple_idents) ),*
+                        #( (#triple_names, RustTargetTriple::#triple_idents), )*
                     ] {
                         if input == *s {
                             return Ok(*t);
diff --git a/tools/crates/gnrt/lib/condition.rs b/tools/crates/gnrt/lib/condition.rs
index 2487e51..e0fae30 100644
--- a/tools/crates/gnrt/lib/condition.rs
+++ b/tools/crates/gnrt/lib/condition.rs
@@ -6,11 +6,14 @@
 use crate::target_triple::{RustTargetTriple, RUST_TRIPLE_PROPERTIES};
 
 use anyhow::{anyhow, Result};
-use std::{collections::HashMap, sync::LazyLock};
+use std::{
+    collections::{HashMap, HashSet},
+    sync::LazyLock,
+};
 
 /// Representation of a `Condition` associated with a conditional/optional
 /// dependency.
-#[derive(Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
+#[derive(Clone, Debug, Eq, PartialEq)]
 pub enum Condition {
     /// The condition is always false.  In other words, supported Chromium
     /// builds never meet this condition.
@@ -22,11 +25,10 @@
     /// Example: `#[cfg(not(target_arch = "powerpc"))]`.
     AlwaysTrue,
     /// The conditional dependency applies to a subset of target triples
-    /// (`RustTargetTriple` is a bit flag - it may have more than 1 bit set).
     ///
     /// For example `#[cfg(target_os = "windows")]` translates into
     /// `Condition::TripleSet(...)`.
-    TripleSet(RustTargetTriple),
+    TripleSet(HashSet<RustTargetTriple>),
     /// Some of the [conditional
     /// compilation](https://doc.rust-lang.org/reference/conditional-compilation.html) directives
     /// weren't recognized by `gnrt`.
@@ -45,10 +47,14 @@
         *self == Condition::AlwaysFalse
     }
 
-    fn from_triple_set(set: RustTargetTriple) -> Self {
+    fn from_triple(triple: RustTargetTriple) -> Self {
+        Condition::TripleSet([triple].into())
+    }
+
+    fn from_triple_set(set: HashSet<RustTargetTriple>) -> Self {
         if set.is_empty() {
             Condition::AlwaysFalse
-        } else if set.is_all() {
+        } else if set == *RustTargetTriple::all() {
             Condition::AlwaysTrue
         } else {
             Condition::TripleSet(set)
@@ -63,7 +69,7 @@
                 err.clone()
             }
             (Condition::TripleSet(lhs), Condition::TripleSet(rhs)) => {
-                Condition::from_triple_set(lhs | rhs)
+                Condition::from_triple_set(&lhs | &rhs)
             }
         }
     }
@@ -74,7 +80,7 @@
             (Condition::AlwaysTrue, other) | (other, Condition::AlwaysTrue) => other,
             (err @ Condition::Unsupported(_), _) | (_, err @ Condition::Unsupported(_)) => err,
             (Condition::TripleSet(lhs), Condition::TripleSet(rhs)) => {
-                Condition::from_triple_set(lhs & rhs)
+                Condition::from_triple_set(&lhs & &rhs)
             }
         }
     }
@@ -84,14 +90,14 @@
             Condition::AlwaysFalse => Condition::AlwaysTrue,
             Condition::AlwaysTrue => Condition::AlwaysFalse,
             err @ Condition::Unsupported(_) => err,
-            Condition::TripleSet(value) => Condition::TripleSet(!value),
+            Condition::TripleSet(value) => Condition::TripleSet(negate_triple_set(&value)),
         }
     }
 
     pub fn to_handlebars_value(&self) -> Result<Option<String>> {
         match self {
             Condition::AlwaysTrue => Ok(None),
-            Condition::TripleSet(set) => Ok(Some(format_as_gn_expr::format(*set))),
+            Condition::TripleSet(set) => Ok(Some(format_as_gn_expr::format(set))),
             Condition::AlwaysFalse => unreachable!(
                 "AlwaysFalse dependencies should be filtered out \
                               by `fn collect_dependencies` from `deps.rs`"
@@ -115,6 +121,10 @@
     }
 }
 
+fn negate_triple_set(set: &HashSet<RustTargetTriple>) -> HashSet<RustTargetTriple> {
+    RustTargetTriple::all() - set
+}
+
 /// Module for converting a set of target triples into a conditional expression
 /// that uses GN/Chromium variables and syntax.
 ///
@@ -128,17 +138,17 @@
 ///   `gn get_gn_target_triple_expr`).  This is used as a last resort, because
 ///   this results in long and not very readable expressions.
 mod format_as_gn_expr {
+    use super::negate_triple_set;
     use crate::target_triple::{RustTargetArch, RustTargetOs, RustTargetTriple};
     use itertools::Itertools;
     use std::collections::HashSet;
 
-    /// Translates `target_triples` (where 1 or more bits/triples can be set)
-    /// into a condition expressed in GN/Chromium syntax (e.g.  `is_win &&
-    /// current_cpu == "x64"`).  Tries to use heuristics to return a short,
-    /// readable expression.
-    pub fn format(target_triples: RustTargetTriple) -> String {
+    /// Translates `target_triples` into a condition expressed in GN/Chromium
+    /// syntax (e.g. `is_win && current_cpu == "x64"`).  Tries to use
+    /// heuristics to return a short, readable expression.
+    pub fn format(target_triples: &HashSet<RustTargetTriple>) -> String {
         assert!(!target_triples.is_empty());
-        assert!(!target_triples.is_all());
+        assert_ne!(target_triples, RustTargetTriple::all());
 
         if let Some(expr) = get_single_filter(target_triples, get_gn_os_expr) {
             return expr;
@@ -166,10 +176,10 @@
     /// filter. `get_expr` should typically be `get_gn_os_expr` or
     /// `get_gn_arch_expr`.
     fn get_single_filter(
-        target_triples: RustTargetTriple,
-        get_expr: fn(RustTargetTriple) -> &'static str,
+        target_triples: &HashSet<RustTargetTriple>,
+        get_expr: fn(&RustTargetTriple) -> &'static str,
     ) -> Option<String> {
-        let get_expr_set = |triples: RustTargetTriple| -> HashSet<&'static str> {
+        let get_expr_set = |triples: &HashSet<RustTargetTriple>| -> HashSet<&'static str> {
             triples.iter().map(get_expr).collect()
         };
         fn negate(expr: &str) -> String {
@@ -180,7 +190,7 @@
             }
         }
         let expr_in_target = get_expr_set(target_triples);
-        let expr_in_rest = get_expr_set(!target_triples);
+        let expr_in_rest = get_expr_set(&negate_triple_set(target_triples));
         if !expr_in_target.is_disjoint(&expr_in_rest)
             || expr_in_target.is_empty()
             || expr_in_rest.is_empty()
@@ -200,23 +210,20 @@
     /// and or `get_expr2` should typically be `get_gn_os_expr` or
     /// `get_gn_arch_expr`.
     fn get_double_filter(
-        target_triples: RustTargetTriple,
-        get_expr1: fn(RustTargetTriple) -> &'static str,
-        get_expr2: fn(RustTargetTriple) -> &'static str,
+        target_triples: &HashSet<RustTargetTriple>,
+        get_expr1: fn(&RustTargetTriple) -> &'static str,
+        get_expr2: fn(&RustTargetTriple) -> &'static str,
     ) -> Option<String> {
         let mut or_operands = vec![];
-        let chunked = target_triples
-            .iter()
-            .sorted_by_key(|&triple| get_expr1(triple))
-            .chunk_by(|&triple| get_expr1(triple));
+        let chunked = target_triples.iter().copied().sorted_by_key(get_expr1).chunk_by(get_expr1);
         for (expr1, chunk) in chunked.into_iter() {
-            let target_triples_matching_expr1 = chunk.reduce(|x, y| x | y).unwrap();
+            let target_triples_matching_expr1 = chunk.collect::<HashSet<_>>();
             let all_triples_matching_expr1 = RustTargetTriple::all()
                 .iter()
-                .filter(|&triple| get_expr1(triple) == expr1)
-                .reduce(|x, y| x | y)
-                .expect("Expecting that at least one triple matches");
-            assert!(all_triples_matching_expr1.contains(target_triples_matching_expr1));
+                .copied()
+                .filter(|triple| get_expr1(triple) == expr1)
+                .collect::<HashSet<_>>();
+            assert!(all_triples_matching_expr1.is_superset(&target_triples_matching_expr1));
             if target_triples_matching_expr1 == all_triples_matching_expr1 {
                 or_operands.push(expr1.to_string());
             } else {
@@ -228,9 +235,9 @@
                     .collect_vec();
                 let all_triples_matching_expr1_and_expr2 = all_triples_matching_expr1
                     .iter()
-                    .filter(|&triple| expr2_or_operands.contains(&get_expr2(triple)))
-                    .reduce(|x, y| x | y)
-                    .expect("Expecting that at least one triple matches");
+                    .copied()
+                    .filter(|triple| expr2_or_operands.contains(&get_expr2(triple)))
+                    .collect::<HashSet<_>>();
                 if all_triples_matching_expr1_and_expr2 != target_triples_matching_expr1 {
                     return None;
                 }
@@ -256,10 +263,10 @@
 
     /// Translates `RustTargetTriple` into a GN conditional expression that will
     /// match the GN notion of the OS of that triple.
-    fn get_gn_os_expr(rust_triple: RustTargetTriple) -> &'static str {
-        // `RustTargetOs` and `try_into` come from `target_triples.rs` which is
+    fn get_gn_os_expr(rust_triple: &RustTargetTriple) -> &'static str {
+        // `RustTargetOs` and `into` come from `target_triples.rs` which is
         // auto-generated by `gnrt`'s `build.rs`.
-        let rust_os = rust_triple.try_into().expect("`rust_triple` should specify a single triple");
+        let rust_os = (*rust_triple).into();
         match rust_os {
             RustTargetOs::Android => "is_android",
             RustTargetOs::Fuchsia => "is_fuchsia",
@@ -272,10 +279,10 @@
 
     /// Translates `RustTargetTriple` into a GN conditional expression that will
     /// match the GN notion of the CPU architecture of that triple.
-    fn get_gn_arch_expr(rust_triple: RustTargetTriple) -> &'static str {
-        // `RustTargetArch` and `try_into` come from `target_triples.rs` which is
+    fn get_gn_arch_expr(rust_triple: &RustTargetTriple) -> &'static str {
+        // `RustTargetArch` and `into` come from `target_triples.rs` which is
         // auto-generated by `gnrt`'s `build.rs`.
-        let rust_os = rust_triple.try_into().expect("`rust_triple` should specify a single triple");
+        let rust_os = (*rust_triple).into();
         match rust_os {
             RustTargetArch::Aarch64 => "current_cpu == \"arm64\"",
             RustTargetArch::Arm => "current_cpu == \"arm\"",
@@ -288,11 +295,10 @@
     /// Translates `RustTargetTriple` into a GN conditional expression that will
     /// match the value of `rust_abi_target` as set by
     /// `//build/config/rust.gni`.
-    fn get_gn_target_triple_expr(rust_triple: RustTargetTriple) -> String {
+    fn get_gn_target_triple_expr(rust_triple: &RustTargetTriple) -> String {
         // `as_triple_name` comes from `target_triples.rs` which is auto-generated by
         // `gnrt`'s `build.rs`.
-        let rust_triple_name =
-            rust_triple.as_triple_name().expect("`rust_triple` should specify a single triple");
+        let rust_triple_name = rust_triple.as_triple_name();
         format!("rust_abi_target == \"{rust_triple_name}\"")
     }
 }
@@ -301,28 +307,26 @@
     match cfg_expr {
         cargo_platform::CfgExpr::Not(expr) => Condition::not(cfg_expr_to_condition(expr)),
         cargo_platform::CfgExpr::All(exprs) => {
-            let mut conds = exprs.iter().map(cfg_expr_to_condition).collect::<Vec<_>>();
-            conds.sort();
-            conds.dedup();
-
             // https://doc.rust-lang.org/reference/conditional-compilation.html#r-cfg.predicate.all
             // says that "It is true if "all of the given predicates are true, or if the
             // list is empty."
-            conds.into_iter().fold(Condition::AlwaysTrue, |accumulated, condition| {
-                Condition::and(accumulated, condition)
-            })
+            exprs
+                .iter()
+                .map(cfg_expr_to_condition)
+                .fold(Condition::AlwaysTrue, |accumulated, condition| {
+                    Condition::and(accumulated, condition)
+                })
         }
         cargo_platform::CfgExpr::Any(exprs) => {
-            let mut conds = exprs.iter().map(cfg_expr_to_condition).collect::<Vec<_>>();
-            conds.sort();
-            conds.dedup();
-
             // https://doc.rust-lang.org/reference/conditional-compilation.html#r-cfg.predicate.any
             // says that "It is true if at least one of the given predicates is true. If
             // there are no predicates, it is false.".
-            conds.into_iter().fold(Condition::AlwaysFalse, |accumulated, condition| {
-                Condition::or(accumulated, condition)
-            })
+            exprs
+                .iter()
+                .map(cfg_expr_to_condition)
+                .fold(Condition::AlwaysFalse, |accumulated, condition| {
+                    Condition::or(accumulated, condition)
+                })
         }
         cargo_platform::CfgExpr::Value(cfg) => cfg_to_condition(cfg),
     }
@@ -341,15 +345,14 @@
 /// is true (e.g. `target_env = "msvc"` is true for all 3 of Chromium Windows
 /// target triples).
 static PROP_NAME_TO_PROP_VALUE_TO_TRIPLE_SET: LazyLock<
-    HashMap<&str, HashMap<&str, RustTargetTriple>>,
+    HashMap<&str, HashMap<&str, HashSet<RustTargetTriple>>>,
 > = LazyLock::new(|| {
     // `RUST_TRIPLE_PROPERTIES` comes from `target_triples.rs` which is
     // auto-generated by `gnrt`'s `build.rs`.
-    let mut result: HashMap<_, HashMap<_, _>> = HashMap::new();
+    let mut result: HashMap<_, HashMap<_, HashSet<_>>> = HashMap::new();
     for (triple, prop_name, prop_value) in RUST_TRIPLE_PROPERTIES {
         let triple = triple.parse().unwrap();
-        let sets = result.entry(prop_name).or_default().entry(prop_value).or_insert(triple);
-        *sets |= triple;
+        result.entry(prop_name).or_default().entry(prop_value).or_default().insert(triple);
     }
     result
 });
@@ -362,7 +365,7 @@
     if let Some(value_to_triple_set_map) = PROP_NAME_TO_PROP_VALUE_TO_TRIPLE_SET.get(key) {
         match value_to_triple_set_map.get(value) {
             None => return Condition::AlwaysFalse,
-            Some(set) => return Condition::TripleSet(*set),
+            Some(set) => return Condition::TripleSet(set.clone()),
         }
     }
 
@@ -397,7 +400,7 @@
         // major issues.
         //
         // TODO(https://crbug.com/402096443): Handle this by tracking not only a set of
-        // `RustTargetTriple` but also a parallel bitflag of `RustDebugConfig` (with
+        // `RustTargetTriple` but also a parallel set/bitflag of `RustDebugConfig` (with
         // just two bits - on and off).
         return Condition::AlwaysTrue;
     }
@@ -439,7 +442,7 @@
 
 fn triple_to_condition(triple: &str) -> Condition {
     match triple.parse() {
-        Ok(set) => Condition::TripleSet(set),
+        Ok(triple) => Condition::from_triple(triple),
         Err(_) => Condition::AlwaysFalse,
     }
 }
@@ -573,7 +576,7 @@
                      not(target_abi = \"llvm\"), \
                      not(windows_raw_dylib))"
             ),
-            Condition::TripleSet(RustTargetTriple::I686_UNKNOWN_LINUX_GNU),
+            Condition::from_triple(RustTargetTriple::I686UnknownLinuxGnu),
         );
 
         // Cfg expressions taken from `getrandom-0.3` => `libc` dependency.
diff --git a/tools/grit/grit_rule.gni b/tools/grit/grit_rule.gni
index 912fdae..597f4f4 100644
--- a/tools/grit/grit_rule.gni
+++ b/tools/grit/grit_rule.gni
@@ -120,8 +120,23 @@
     _output_dir = target_gen_dir
   }
 
-  _grit_outputs =
-      get_path_info(rebase_path(invoker.outputs, ".", _output_dir), "abspath")
+  if (defined(invoker.outputs)) {
+    _grit_outputs =
+        get_path_info(rebase_path(invoker.outputs, ".", _output_dir), "abspath")
+  } else {
+    _grit_outputs = []
+  }
+
+  _create_android_resources = defined(invoker.create_android_resources) &&
+                              invoker.create_android_resources
+  if (_create_android_resources) {
+    if (defined(invoker.resources_zip)) {
+      _resources_zip = invoker.resources_zip
+    } else {
+      _resources_zip = "$target_out_dir/$target_name.resources.zip"
+    }
+    _grit_outputs += [ _resources_zip ]
+  }
 
   # Add .info output for all pak files
   _pak_info_outputs = []
@@ -195,6 +210,13 @@
              "--depend-on-stamp",
            ] + _grit_flags
 
+    if (_create_android_resources) {
+      args += [
+        "--android-output-zip",
+        rebase_path(_resources_zip, root_build_dir),
+      ]
+    }
+
     # Add brotli executable if using brotli.
     if (defined(invoker.use_brotli) && invoker.use_brotli) {
       _brotli_target = "//third_party/brotli:brotli($host_toolchain)"
@@ -241,7 +263,7 @@
     _asserted_list_file =
         "$target_out_dir/${_grit_output_name}_expected_outputs.txt"
     write_file(_asserted_list_file,
-               rebase_path(invoker.outputs, root_build_dir, _output_dir))
+               rebase_path(_grit_outputs, root_build_dir, _output_dir))
     inputs += [ _asserted_list_file ]
     args += [
       "--assert-file-list",
@@ -307,14 +329,12 @@
 
     # Since we generate a file, we need to be run before the targets that
     # depend on us.
-    sources = []
-    foreach(_output, _grit_outputs) {
-      _extension = get_path_info(_output, "extension")
-      if (_extension != "json" && _extension != "gz" && _extension != "pak" &&
-          _extension != "xml") {
-        sources += [ _output ]
-      }
-    }
+    sources = filter_include(_grit_outputs,
+                             [
+                               "*.h",
+                               "*.cc",
+                               "*.rc",
+                             ])
 
     # Deps set on the template invocation will go on the action that runs
     # grit above rather than this library. This target needs to depend on the
@@ -356,93 +376,55 @@
   #    grd_file = "foo_strings.grd"
   #  }
   template("java_strings_grd") {
-    forward_variables_from(invoker, [ "testonly" ])
-
-    _resources_zip = "$target_out_dir/$target_name.resources.zip"
     _grit_target_name = "${target_name}__grit"
-    _grit_output_dir = "$target_gen_dir/${target_name}_grit_output"
-
+    _resources_zip = "$target_out_dir/$target_name.resources.zip"
     grit(_grit_target_name) {
       forward_variables_from(invoker,
-                             [
-                               "deps",
-                               "defines",
-                             ])
+                             TESTONLY_AND_VISIBILITY + [
+                                   "deps",
+                                   "defines",
+                                 ])
+
+      # TODO(agrieve): Remove outputs from callers.
+      not_needed(invoker, [ "outputs" ])
+      create_android_resources = true
+      resources_zip = _resources_zip
       grit_flags = [
         "-E",
         "ANDROID_JAVA_TAGGED_ONLY=false",
       ]
-      output_dir = _grit_output_dir
+
+      # Ignore pak file IDs since they are not relevant.
       resource_ids = ""
       source = invoker.grd_file
-      outputs = invoker.outputs
-    }
-
-    _zip_target_name = "${target_name}__zip"
-
-    zip(_zip_target_name) {
-      base_dir = _grit_output_dir
-
-      # No need to depend on the source_set().
-      _grit_dep = ":${_grit_target_name}_grit"
-      deps = [ _grit_dep ]
-      inputs = filter_exclude(get_target_outputs(_grit_dep), [ "*.stamp" ])
-      output = _resources_zip
     }
 
     android_generated_resources(target_name) {
       forward_variables_from(invoker,
-                             [
-                               "resource_overlay",
-                               "visibility",
-                             ])
-      generating_target = ":$_zip_target_name"
+                             TESTONLY_AND_VISIBILITY + [ "resource_overlay" ])
       generated_resources_zip = _resources_zip
+      generating_target = ":$_grit_target_name"
     }
   }
 
-  # Declare a target that packages strings.xml generated from a grd file.
+  # An android_generated_resources() for an existing grit() target.
   #
-  # If this target is included in the deps of an android resources/library/apk,
-  # the strings.xml will be included with that target.
-  #
-  # Variables
-  #  grit_output_dir: directory containing grit-generated files.
-  #  generated_files: list of android resource files to package.
+  # The grit target must have set create_android_resources = true
   #
   # Example
   #  java_strings_grd_prebuilt("foo_strings_grd") {
-  #    grit_output_dir = "$root_gen_dir/foo/grit"
-  #    generated_files = [
-  #      "values/strings.xml"
-  #    ]
+  #    generating_target = ":my_grit_rule"
   #  }
   template("java_strings_grd_prebuilt") {
-    forward_variables_from(invoker, [ "testonly" ])
-
-    _resources_zip = "$target_out_dir/$target_name.resources.zip"
-    _zip_target_name = "${target_name}__zip"
-
-    zip(_zip_target_name) {
-      base_dir = invoker.grit_output_dir
-      inputs = rebase_path(invoker.generated_files, ".", base_dir)
-      output = _resources_zip
-      if (defined(invoker.deps)) {
-        deps = []
-        foreach(_dep, invoker.deps) {
-          deps += [ "${_dep}_grit" ]
-        }
-      }
-    }
-
     android_generated_resources(target_name) {
       forward_variables_from(invoker,
-                             [
-                               "resource_overlay",
-                               "visibility",
-                             ])
-      generating_target = ":$_zip_target_name"
-      generated_resources_zip = _resources_zip
+                             TESTONLY_AND_VISIBILITY + [
+                                   "generating_target",
+                                   "resource_overlay",
+                                 ])
+      generated_resources_zip =
+          get_label_info(invoker.generating_target, "target_out_dir") + "/" +
+          get_label_info(invoker.generating_target, "name") + ".resources.zip"
     }
   }
 }
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index d0b916b..b238a0f 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -704,6 +704,10 @@
     "META": {"sizes": {"includes": [10]}},
     "includes": [5200],
   },
+  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/chromeos/floating_workspace/resources.grd": {
+    "META": {"sizes": {"includes": [10],}},
+    "includes": [5210],
+  },
   # END chrome/ WebUI resources section
 
   # START chrome/ miscellaneous section.
diff --git a/tools/json_to_struct/json_to_struct.py b/tools/json_to_struct/json_to_struct.py
index d68f8a8..b9215e679 100755
--- a/tools/json_to_struct/json_to_struct.py
+++ b/tools/json_to_struct/json_to_struct.py
@@ -127,6 +127,10 @@
     f.write(u'#define %s\n' % header_guard)
     f.write(u'\n')
 
+    aggregation = GetAggregationDetails(description)
+
+    if aggregation.kind == AggregationKind.ARRAY:
+      f.write("#include <array>\n")
     f.write(u'#include <cstddef>\n')
     f.write(u'\n')
 
@@ -135,7 +139,11 @@
         f.write(u'#include <%s>\n' % header)
       f.write(u'\n')
 
-    for header in schema.get(u'headers', []):
+    headers = schema.get(u'headers', [])
+    if aggregation.kind == AggregationKind.MAP:
+      headers.append("base/containers/fixed_flat_map.h")
+
+    for header in sorted(headers):
       f.write(u'#include "%s"\n' % header)
     f.write(u'\n')
 
@@ -150,8 +158,6 @@
     for var_name, value in description.get('int_variables', {}).items():
       f.write(u'extern const int %s;\n' % var_name)
 
-    aggregation = GetAggregationDetails(description)
-
     # Generate forward declarations of all elements.
     if aggregation.export_items:
       f.write(u'\n')
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 5335630..6cb8d3fc 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -4430,383 +4430,6 @@
   <int value="11" label="Server returned error"/>
 </enum>
 
-<enum name="ErrSecOSStatus">
-  <int value="-67898" label="errSecTimestampRevocationNotification"/>
-  <int value="-67897" label="errSecTimestampRevocationWarning"/>
-  <int value="-67896" label="errSecTimestampWaiting"/>
-  <int value="-67895" label="errSecTimestampRejection"/>
-  <int value="-67894" label="errSecSigningTimeMissing"/>
-  <int value="-67893" label="errSecTimestampSystemFailure"/>
-  <int value="-67892" label="errSecTimestampAddInfoNotAvailable"/>
-  <int value="-67891" label="errSecTimestampUnacceptedExtension"/>
-  <int value="-67890" label="errSecTimestampUnacceptedPolicy"/>
-  <int value="-67889" label="errSecTimestampTimeNotAvailable"/>
-  <int value="-67888" label="errSecTimestampBadDataFormat"/>
-  <int value="-67887" label="errSecTimestampBadRequest"/>
-  <int value="-67886" label="errSecTimestampBadAlg"/>
-  <int value="-67885" label="errSecTimestampServiceNotAvailable"/>
-  <int value="-67884" label="errSecTimestampNotTrusted"/>
-  <int value="-67883" label="errSecTimestampInvalid"/>
-  <int value="-67882" label="errSecTimestampMissing"/>
-  <int value="-67881" label="errSecExtendedKeyUsageNotCritical"/>
-  <int value="-67880" label="errSecMissingRequiredExtension"/>
-  <int value="-67879" label="errSecInvalidModifyMode"/>
-  <int value="-67878" label="errSecInvalidNewOwner"/>
-  <int value="-67877" label="errSecInvalidIndexInfo"/>
-  <int value="-67876" label="errSecInvalidAccessRequest"/>
-  <int value="-67875" label="errSecInvalidDBLocation"/>
-  <int value="-67874" label="errSecUnsupportedOperator"/>
-  <int value="-67873" label="errSecUnsupportedNumSelectionPreds"/>
-  <int value="-67872" label="errSecUnsupportedQueryLimits"/>
-  <int value="-67871" label="errSecMissingValue"/>
-  <int value="-67870" label="errSecDatastoreIsOpen"/>
-  <int value="-67869" label="errSecDatabaseLocked"/>
-  <int value="-67868" label="errSecInvalidParsingModule"/>
-  <int value="-67867" label="errSecIncompatibleFieldFormat"/>
-  <int value="-67866" label="errSecFieldSpecifiedMultiple"/>
-  <int value="-67865" label="errSecUnsupportedNumRecordTypes"/>
-  <int value="-67864" label="errSecUnsupportedNumIndexes"/>
-  <int value="-67863" label="errSecUnsupportedNumAttributes"/>
-  <int value="-67862" label="errSecUnsupportedLocality"/>
-  <int value="-67861" label="errSecUnsupportedIndexInfo"/>
-  <int value="-67860" label="errSecUnsupportedFieldFormat"/>
-  <int value="-67859" label="errSecNoFieldValues"/>
-  <int value="-67858" label="errSecInvalidCRLIndex"/>
-  <int value="-67857" label="errSecInvalidBundleInfo"/>
-  <int value="-67856" label="errSecRequestDescriptor"/>
-  <int value="-67855" label="errSecInvalidRequestor"/>
-  <int value="-67854" label="errSecInvalidValidityPeriod"/>
-  <int value="-67853" label="errSecInvalidEncoding"/>
-  <int value="-67852" label="errSecInvalidTupleCredendtials"/>
-  <int value="-67851" label="errSecInvalidBaseACLs"/>
-  <int value="-67850" label="errSecInvalidTupleGroup"/>
-  <int value="-67849" label="errSecUnsupportedService"/>
-  <int value="-67848" label="errSecUnsupportedAddressType"/>
-  <int value="-67847" label="errSecRequestRejected"/>
-  <int value="-67846" label="errSecRequestLost"/>
-  <int value="-67845" label="errSecRejectedForm"/>
-  <int value="-67844" label="errSecNoDefaultAuthority"/>
-  <int value="-67843" label="errSecNotTrusted"/>
-  <int value="-67842" label="errSecMultipleValuesUnsupported"/>
-  <int value="-67841" label="errSecInvalidTuple"/>
-  <int value="-67840" label="errSecInvalidStopOnPolicy"/>
-  <int value="-67839" label="errSecInvalidResponseVector"/>
-  <int value="-67838" label="errSecInvalidRequestInputs"/>
-  <int value="-67837" label="errSecInvalidReason"/>
-  <int value="-67836" label="errSecInvalidTimeString"/>
-  <int value="-67835" label="errSecInvalidPolicyIdentifiers"/>
-  <int value="-67834" label="errSecInvalidIndex"/>
-  <int value="-67833" label="errSecInvalidIdentifier"/>
-  <int value="-67832" label="errSecInvalidID"/>
-  <int value="-67831" label="errSecInvalidFormType"/>
-  <int value="-67830" label="errSecInvalidCRL"/>
-  <int value="-67829" label="errSecInvalidCRLType"/>
-  <int value="-67828" label="errSecInvalidCRLEncoding"/>
-  <int value="-67827" label="errSecInvaldCRLAuthority"/>
-  <int value="-67826" label="errSecInvalidCertAuthority"/>
-  <int value="-67825" label="errSecVerifyActionFailed"/>
-  <int value="-67824" label="errSecInvalidAuthority"/>
-  <int value="-67823" label="errSecInvalidAction"/>
-  <int value="-67822" label="errSecInsufficientCredentials"/>
-  <int value="-67821" label="errSecCertificateSuspended"/>
-  <int value="-67820" label="errSecCertificateRevoked"/>
-  <int value="-67819" label="errSecCertificateNotValidYet"/>
-  <int value="-67818" label="errSecCertificateExpired"/>
-  <int value="-67817" label="errSecCertificateCannotOperate"/>
-  <int value="-67816" label="errSecInvalidCRLGroup"/>
-  <int value="-67815" label="errSecInvalidDigestAlgorithm"/>
-  <int value="-67814" label="errSecAlreadyLoggedIn"/>
-  <int value="-67813" label="errSecInvalidLoginName"/>
-  <int value="-67812" label="errSecDeviceVerifyFailed"/>
-  <int value="-67811" label="errSecPublicKeyInconsistent"/>
-  <int value="-67810" label="errSecBlockSizeMismatch"/>
-  <int value="-67809" label="errSecQuerySizeUnknown"/>
-  <int value="-67808" label="errSecVerifyFailed"/>
-  <int value="-67807" label="errSecStagedOperationNotStarted"/>
-  <int value="-67806" label="errSecStagedOperationInProgress"/>
-  <int value="-67805" label="errSecMissingAttributeWrappedKeyFormat"/>
-  <int value="-67804" label="errSecInvalidAttributeWrappedKeyFormat"/>
-  <int value="-67803" label="errSecMissingAttributeSymmetricKeyFormat"/>
-  <int value="-67802" label="errSecInvalidAttributeSymmetricKeyFormat"/>
-  <int value="-67801" label="errSecMissingAttributePrivateKeyFormat"/>
-  <int value="-67800" label="errSecInvalidAttributePrivateKeyFormat"/>
-  <int value="-67799" label="errSecMissingAttributePublicKeyFormat"/>
-  <int value="-67798" label="errSecInvalidAttributePublicKeyFormat"/>
-  <int value="-67797" label="errSecMissingAttributeAccessCredentials"/>
-  <int value="-67796" label="errSecInvalidAttributeAccessCredentials"/>
-  <int value="-67795" label="errSecMissingAttributeDLDBHandle"/>
-  <int value="-67794" label="errSecInvalidAttributeDLDBHandle"/>
-  <int value="-67793" label="errSecMissingAttributeIterationCount"/>
-  <int value="-67792" label="errSecInvalidAttributeIterationCount"/>
-  <int value="-67791" label="errSecMissingAttributeSubprime"/>
-  <int value="-67790" label="errSecInvalidAttributeSubprime"/>
-  <int value="-67789" label="errSecMissingAttributeBase"/>
-  <int value="-67788" label="errSecInvalidAttributeBase"/>
-  <int value="-67787" label="errSecMissingAttributePrime"/>
-  <int value="-67786" label="errSecInvalidAttributePrime"/>
-  <int value="-67785" label="errSecMissingAttributeVersion"/>
-  <int value="-67784" label="errSecInvalidAttributeVersion"/>
-  <int value="-67783" label="errSecMissingAttributeEndDate"/>
-  <int value="-67782" label="errSecInvalidAttributeEndDate"/>
-  <int value="-67781" label="errSecMissingAttributeStartDate"/>
-  <int value="-67780" label="errSecInvalidAttributeStartDate"/>
-  <int value="-67779" label="errSecMissingAttributeEffectiveBits"/>
-  <int value="-67778" label="errSecInvalidAttributeEffectiveBits"/>
-  <int value="-67777" label="errSecMissingAttributeMode"/>
-  <int value="-67776" label="errSecInvalidAttributeMode"/>
-  <int value="-67775" label="errSecMissingAttributeKeyType"/>
-  <int value="-67774" label="errSecInvalidAttributeKeyType"/>
-  <int value="-67773" label="errSecMissingAttributeLabel"/>
-  <int value="-67772" label="errSecInvalidAttributeLabel"/>
-  <int value="-67771" label="errSecMissingAlgorithmParms"/>
-  <int value="-67770" label="errSecInvalidAlgorithmParms"/>
-  <int value="-67769" label="errSecMissingAttributeRounds"/>
-  <int value="-67768" label="errSecInvalidAttributeRounds"/>
-  <int value="-67767" label="errSecMissingAttributeOutputSize"/>
-  <int value="-67766" label="errSecInvalidAttributeOutputSize"/>
-  <int value="-67765" label="errSecMissingAttributeBlockSize"/>
-  <int value="-67764" label="errSecInvalidAttributeBlockSize"/>
-  <int value="-67763" label="errSecMissingAttributeKeyLength"/>
-  <int value="-67762" label="errSecInvalidAttributeKeyLength"/>
-  <int value="-67761" label="errSecMissingAttributePassphrase"/>
-  <int value="-67760" label="errSecInvalidAttributePassphrase"/>
-  <int value="-67759" label="errSecMissingAttributeSeed"/>
-  <int value="-67758" label="errSecInvalidAttributeSeed"/>
-  <int value="-67757" label="errSecMissingAttributeRandom"/>
-  <int value="-67756" label="errSecInvalidAttributeRandom"/>
-  <int value="-67755" label="errSecMissingAttributePadding"/>
-  <int value="-67754" label="errSecInvalidAttributePadding"/>
-  <int value="-67753" label="errSecMissingAttributeSalt"/>
-  <int value="-67752" label="errSecInvalidAttributeSalt"/>
-  <int value="-67751" label="errSecMissingAttributeInitVector"/>
-  <int value="-67750" label="errSecInvalidAttributeInitVector"/>
-  <int value="-67749" label="errSecMissingAttributeKey"/>
-  <int value="-67748" label="errSecInvalidAttributeKey"/>
-  <int value="-67747" label="errSecInvalidAlgorithm"/>
-  <int value="-67746" label="errSecInvalidContext"/>
-  <int value="-67745" label="errSecInvalidOutputVector"/>
-  <int value="-67744" label="errSecInvalidInputVector"/>
-  <int value="-67743" label="errSecUnsupportedVectorOfBuffers"/>
-  <int value="-67742" label="errSecInvalidKeyFormat"/>
-  <int value="-67741" label="errSecUnsupportedKeyLabel"/>
-  <int value="-67740" label="errSecInvalidKeyLabel"/>
-  <int value="-67739" label="errSecUnsupportedKeyAttributeMask"/>
-  <int value="-67738" label="errSecInvalidKeyAttributeMask"/>
-  <int value="-67737" label="errSecUnsupportedKeyUsageMask"/>
-  <int value="-67736" label="errSecInvalidKeyUsageMask"/>
-  <int value="-67735" label="errSecUnsupportedKeySize"/>
-  <int value="-67734" label="errSecUnsupportedKeyFormat"/>
-  <int value="-67733" label="errSecKeyHeaderInconsistent"/>
-  <int value="-67732" label="errSecKeyBlobTypeIncorrect"/>
-  <int value="-67731" label="errSecKeyUsageIncorrect"/>
-  <int value="-67730" label="errSecAlgorithmMismatch"/>
-  <int value="-67729" label="errSecNotLoggedIn"/>
-  <int value="-67728" label="errSecAttachHandleBusy"/>
-  <int value="-67727" label="errSecDeviceError"/>
-  <int value="-67726" label="errSecPrivilegeNotSupported"/>
-  <int value="-67725" label="errSecOutputLengthError"/>
-  <int value="-67724" label="errSecInputLengthError"/>
-  <int value="-67723" label="errSecEventNotificationCallbackNotFound"/>
-  <int value="-67722" label="errSecModuleManagerNotFound"/>
-  <int value="-67721" label="errSecModuleManagerInitializeFailed"/>
-  <int value="-67720" label="errSecAttributeNotInContext"/>
-  <int value="-67719" label="errSecInvalidSubServiceID"/>
-  <int value="-67718" label="errSecModuleNotLoaded"/>
-  <int value="-67717" label="errSecInvalidServiceMask"/>
-  <int value="-67716" label="errSecInvalidAddinFunctionTable"/>
-  <int value="-67715" label="errSecLibraryReferenceNotFound"/>
-  <int value="-67714" label="errSecAddinUnloadFailed"/>
-  <int value="-67713" label="errSecInvalidKeyHierarchy"/>
-  <int value="-67712" label="errSecInvalidKeyRef"/>
-  <int value="-67711" label="errSecAddinLoadFailed"/>
-  <int value="-67710" label="errSecEMMUnloadFailed"/>
-  <int value="-67709" label="errSecEMMLoadFailed"/>
-  <int value="-67708" label="errSecInvalidPVC"/>
-  <int value="-67707" label="errSecPVCAlreadyConfigured"/>
-  <int value="-67706" label="errSecInvalidScope"/>
-  <int value="-67705" label="errSecPrivilegeNotGranted"/>
-  <int value="-67704" label="errSecIncompatibleVersion"/>
-  <int value="-67703" label="errSecInvalidSampleValue"/>
-  <int value="-67702" label="errSecInvalidACL"/>
-  <int value="-67701" label="errSecInvalidRecord"/>
-  <int value="-67700" label="errSecInvalidAccessCredentials"/>
-  <int value="-67699" label="errSecACLChangeFailed"/>
-  <int value="-67698" label="errSecACLAddFailed"/>
-  <int value="-67697" label="errSecACLReplaceFailed"/>
-  <int value="-67696" label="errSecACLDeleteFailed"/>
-  <int value="-67695" label="errSecCallbackFailed"/>
-  <int value="-67694" label="errSecInvalidValue"/>
-  <int value="-67693" label="errSecInvalidQuery"/>
-  <int value="-67692" label="errSecTagNotFound"/>
-  <int value="-67691" label="errSecInvalidCertificateGroup"/>
-  <int value="-67690" label="errSecInvalidCertificateRef"/>
-  <int value="-67689" label="errSecInvalidName"/>
-  <int value="-67688" label="errSecInvalidSignature"/>
-  <int value="-67687" label="errSecUnknownTag"/>
-  <int value="-67686" label="errSecVerificationFailure"/>
-  <int value="-67685" label="errSecInvalidNumberOfFields"/>
-  <int value="-67684" label="errSecCRLAlreadySigned"/>
-  <int value="-67683" label="errSecInvalidNetworkAddress"/>
-  <int value="-67682" label="errSecInvalidPassthroughID"/>
-  <int value="-67681" label="errSecInvalidDBList"/>
-  <int value="-67680" label="errSecInvalidHandle"/>
-  <int value="-67679" label="errSecInvalidGUID"/>
-  <int value="-67678" label="errSecModuleManifestVerifyFailed"/>
-  <int value="-67677" label="errSecFunctionFailed"/>
-  <int value="-67676" label="errSecSelfCheckFailed"/>
-  <int value="-67675" label="errSecInvalidPointer"/>
-  <int value="-67674" label="errSecMDSError"/>
-  <int value="-67673" label="errSecInvalidData"/>
-  <int value="-67672" label="errSecMemoryError"/>
-  <int value="-67671" label="errSecInternalError"/>
-  <int value="-67670" label="errSecFunctionIntegrityFail"/>
-  <int value="-67669" label="errSecPVCReferentNotFound"/>
-  <int value="-67668" label="errSecInvalidHandleUsage"/>
-  <int value="-67667" label="errSecNotInitialized"/>
-  <int value="-67666" label="errSecMobileMeFailedConsistencyCheck"/>
-  <int value="-67665" label="errSecMobileMeCSRVerifyFailure"/>
-  <int value="-67664" label="errSecMobileMeNoRequestPending"/>
-  <int value="-67663" label="errSecMobileMeRequestAlreadyPending"/>
-  <int value="-67662" label="errSecMobileMeServerServiceErr"/>
-  <int value="-67661" label="errSecMobileMeServerAlreadyExists"/>
-  <int value="-67660" label="errSecMobileMeServerNotAvailable"/>
-  <int value="-67659" label="errSecMobileMeServerError"/>
-  <int value="-67658" label="errSecMobileMeRequestRedirected"/>
-  <int value="-67657" label="errSecMobileMeRequestQueued"/>
-  <int value="-67656" label="errSecUnknownQualifiedCertStatement"/>
-  <int value="-67655" label="errSecInvalidSubjectName"/>
-  <int value="-67654" label="errSecTrustSettingDeny"/>
-  <int value="-67653" label="errSecResourceSignBadExtKeyUsage"/>
-  <int value="-67652" label="errSecResourceSignBadCertChainLength"/>
-  <int value="-67651" label="errSecCodeSigningDevelopment"/>
-  <int value="-67650" label="errSecCodeSigningNoExtendedKeyUsage"/>
-  <int value="-67649" label="errSecCodeSigningBadPathLengthConstraint"/>
-  <int value="-67648" label="errSecCodeSigningNoBasicConstraints"/>
-  <int value="-67647" label="errSecCodeSigningBadCertChainLength"/>
-  <int value="-67646" label="errSecOCSPResponseNonceMismatch"/>
-  <int value="-67645" label="errSecOCSPResponderUnauthorized"/>
-  <int value="-67644" label="errSecOCSPResponderSignatureRequired"/>
-  <int value="-67643" label="errSecOCSPResponderTryLater"/>
-  <int value="-67642" label="errSecOCSPResponderInternalError"/>
-  <int value="-67641" label="errSecOCSPResponderMalformedReq"/>
-  <int value="-67640" label="errSecOCSPNoSigner"/>
-  <int value="-67639" label="errSecOCSPSignatureError"/>
-  <int value="-67638" label="errSecRecordModified"/>
-  <int value="-67637" label="errSecOCSPNotTrustedToAnchor"/>
-  <int value="-67636" label="errSecNetworkFailure"/>
-  <int value="-67635" label="errSecIncompleteCertRevocationCheck"/>
-  <int value="-67634" label="errSecEndOfData"/>
-  <int value="-67633" label="errSecOCSPStatusUnrecognized"/>
-  <int value="-67632" label="errSecOCSPUnavailable"/>
-  <int value="-67631" label="errSecOCSPBadRequest"/>
-  <int value="-67630" label="errSecOCSPBadResponse"/>
-  <int value="-67629" label="errSecSSLBadExtendedKeyUsage"/>
-  <int value="-67628" label="errSecSMIMESubjAltNameNotCritical"/>
-  <int value="-67627" label="errSecSMIMENoEmailAddress"/>
-  <int value="-67626" label="errSecSMIMEKeyUsageNotCritical"/>
-  <int value="-67625" label="errSecSMIMEBadKeyUsage"/>
-  <int value="-67624" label="errSecSMIMEBadExtendedKeyUsage"/>
-  <int value="-67623" label="errSecSMIMEEmailAddressesNotFound"/>
-  <int value="-67622" label="errSecIDPFailure"/>
-  <int value="-67621" label="errSecCRLPolicyFailed"/>
-  <int value="-67620" label="errSecCRLNotTrusted"/>
-  <int value="-67619" label="errSecUnknownCRLExtension"/>
-  <int value="-67618" label="errSecUnknownCertExtension"/>
-  <int value="-67617" label="errSecCRLBadURI"/>
-  <int value="-67616" label="errSecCRLServerDown"/>
-  <int value="-67615" label="errSecCRLNotFound"/>
-  <int value="-67614" label="errSecCRLNotValidYet"/>
-  <int value="-67613" label="errSecCRLExpired"/>
-  <int value="-67612" label="errSecInvalidRoot"/>
-  <int value="-67611" label="errSecPathLengthConstraintExceeded"/>
-  <int value="-67610" label="errSecInvalidIDLinkage"/>
-  <int value="-67609" label="errSecInvalidExtendedKeyUsage"/>
-  <int value="-67608" label="errSecInvalidKeyUsageForPolicy"/>
-  <int value="-67607" label="errSecInvalidSubjectKeyID"/>
-  <int value="-67606" label="errSecInvalidAuthorityKeyID"/>
-  <int value="-67605" label="errSecNoBasicConstraintsCA"/>
-  <int value="-67604" label="errSecNoBasicConstraints"/>
-  <int value="-67603" label="errSecUnknownCriticalExtensionFlag"/>
-  <int value="-67602" label="errSecHostNameMismatch"/>
-  <int value="-67601" label="errSecIncompatibleKeyBlob"/>
-  <int value="-67600" label="errSecIncompatibleDatabaseBlob"/>
-  <int value="-67599" label="errSecInvalidKeyBlob"/>
-  <int value="-67598" label="errSecInvalidDatabaseBlob"/>
-  <int value="-67597" label="errSecFileTooBig"/>
-  <int value="-67596" label="errSecQuotaExceeded"/>
-  <int value="-67595" label="errSecAppleSSLv2Rollback"/>
-  <int value="-67594" label="errSecConversionError"/>
-  <int value="-67593" label="errSecAppleInvalidKeyEndDate"/>
-  <int value="-67592" label="errSecAppleInvalidKeyStartDate"/>
-  <int value="-67591" label="errSecAppleSignatureMismatch"/>
-  <int value="-67590" label="errSecApplePublicKeyIncomplete"/>
-  <int value="-67589" label="errSecAppleAddAppACLSubject"/>
-  <int value="-67588" label="errSecDeviceFailed"/>
-  <int value="-67587" label="errSecDeviceReset"/>
-  <int value="-67586" label="errSecInsufficientClientID"/>
-  <int value="-67585" label="errSecServiceNotAvailable"/>
-  <int value="-34018" label="errSecMissingEntitlement"/>
-  <int value="-26275" label="errSecDecode"/>
-  <int value="-26267" label="errSecNotSigner"/>
-  <int value="-25320" label="errSecInDarkWake"/>
-  <int value="-25319" label="errSecInvalidPrefsDomain"/>
-  <int value="-25318" label="errSecCreateChainFailed"/>
-  <int value="-25317" label="errSecDataNotModifiable"/>
-  <int value="-25316" label="errSecDataNotAvailable"/>
-  <int value="-25315" label="errSecInteractionRequired"/>
-  <int value="-25314" label="errSecNoPolicyModule"/>
-  <int value="-25313" label="errSecNoCertificateModule"/>
-  <int value="-25312" label="errSecNoStorageModule"/>
-  <int value="-25311" label="errSecKeySizeNotAllowed"/>
-  <int value="-25310" label="errSecWrongSecVersion"/>
-  <int value="-25309" label="errSecReadOnlyAttr"/>
-  <int value="-25308" label="errSecInteractionNotAllowed"/>
-  <int value="-25307" label="errSecNoDefaultKeychain"/>
-  <int value="-25306" label="errSecNoSuchClass"/>
-  <int value="-25305" label="errSecInvalidSearchRef"/>
-  <int value="-25304" label="errSecInvalidItemRef"/>
-  <int value="-25303" label="errSecNoSuchAttr"/>
-  <int value="-25302" label="errSecDataTooLarge"/>
-  <int value="-25301" label="errSecBufferTooSmall"/>
-  <int value="-25300" label="errSecItemNotFound"/>
-  <int value="-25299" label="errSecDuplicateItem"/>
-  <int value="-25298" label="errSecInvalidCallback"/>
-  <int value="-25297" label="errSecDuplicateCallback"/>
-  <int value="-25296" label="errSecDuplicateKeychain"/>
-  <int value="-25295" label="errSecInvalidKeychain"/>
-  <int value="-25294" label="errSecNoSuchKeychain"/>
-  <int value="-25293" label="errSecAuthFailed"/>
-  <int value="-25292" label="errSecReadOnly"/>
-  <int value="-25291" label="errSecNotAvailable"/>
-  <int value="-25264" label="errSecPkcs12VerifyFailure"/>
-  <int value="-25263" label="errSecNoTrustSettings"/>
-  <int value="-25262" label="errSecInvalidTrustSettings"/>
-  <int value="-25261" label="errSecInvalidPasswordRef"/>
-  <int value="-25260" label="errSecPassphraseRequired"/>
-  <int value="-25259" label="errSecMultiplePrivKeys"/>
-  <int value="-25258" label="errSecKeyIsSensitive"/>
-  <int value="-25257" label="errSecUnknownFormat"/>
-  <int value="-25256" label="errSecUnsupportedFormat"/>
-  <int value="-25245" label="errSecTrustNotAvailable"/>
-  <int value="-25244" label="errSecInvalidOwnerEdit"/>
-  <int value="-25243" label="errSecNoAccessForItem"/>
-  <int value="-25242" label="errSecInvalidTrustSetting"/>
-  <int value="-25241" label="errSecPolicyNotFound"/>
-  <int value="-25240" label="errSecACLNotSimple"/>
-  <int value="-4960" label="errSecCoreFoundationUnknown"/>
-  <int value="-2070" label="errSecInternalComponent"/>
-  <int value="-909" label="errSecBadReq"/>
-  <int value="-128" label="errSecUserCanceled"/>
-  <int value="-108" label="errSecAllocate"/>
-  <int value="-61" label="errSecWrPerm"/>
-  <int value="-50" label="errSecParam"/>
-  <int value="-49" label="errSecOpWr"/>
-  <int value="-36" label="errSecIO"/>
-  <int value="-34" label="errSecDiskFull"/>
-  <int value="-4" label="errSecUnimplemented"/>
-  <int value="0" label="errSecSuccess"/>
-</enum>
-
 <enum name="EventLatencyEventType">
   <int value="0" label="MousePressed"/>
   <int value="1" label="MouseReleased"/>
@@ -5833,6 +5456,16 @@
   <int value="9" label="kErrorDeserializingJSonLogs"/>
 </enum>
 
+<!-- LINT.IfChange(GLICConsentType) -->
+
+<enum name="GLICConsentType">
+  <int value="0" label="kCancel"/>
+  <int value="1" label="kDismiss"/>
+  <int value="2" label="kAccept"/>
+</enum>
+
+<!-- LINT.ThenChange(//ios/chrome/browser/intelligence/glic/metrics/glic_metrics.h:GLICConsentType) -->
+
 <enum name="GoogleRpcCode">
   <summary>Canonical error codes for gRPC APIs.</summary>
   <int value="0" label="OK"/>
@@ -15757,6 +15390,7 @@
       label="ComputePressureBreakCalibrationMitigation:disabled"/>
   <int value="527140412"
       label="AndroidAutofillViewStructureWithFormHierarchyLayer:disabled"/>
+  <int value="528614527" label="LeftClickOpensTabGroupBubble:disabled"/>
   <int value="529235584" label="PhoneHub:enabled"/>
   <int value="529665808"
       label="AutofillEnableVirtualCardManagementInDesktopSettingsPage:disabled"/>
@@ -15930,6 +15564,7 @@
   <int value="601417094" label="SupportF11AndF12KeyShortcuts:disabled"/>
   <int value="601510870" label="EcheLauncherIconsInMoreAppsButton:disabled"/>
   <int value="601688485" label="EnterpriseFileObfuscation:enabled"/>
+  <int value="601689207" label="TabGroupHome:disabled"/>
   <int value="601875786" label="PageVisibilityPageContentAnnotations:enabled"/>
   <int value="602117675" label="NTPBookmarkSuggestions:enabled"/>
   <int value="602452004" label="WebBluetoothBondOnDemand:disabled"/>
@@ -19082,6 +18717,7 @@
   <int value="1785093465" label="enable-document-passive-event-listeners"/>
   <int value="1785274337"
       label="AutofillEnableOfferNotificationCrossTabTracking:disabled"/>
+  <int value="1785884719" label="TabGroupHome:enabled"/>
   <int value="1785914867"
       label="DeprecateOldKeyboardShortcutsAccelerator:disabled"/>
   <int value="1786182939" label="ExoPointerLock:enabled"/>
@@ -19831,6 +19467,7 @@
   <int value="2058283872" label="CCTModuleCache:disabled"/>
   <int value="2058425230" label="LauncherNudgeSessionReset:enabled"/>
   <int value="2058439723" label="CalculateNativeWinOcclusion:disabled"/>
+  <int value="2058921256" label="LeftClickOpensTabGroupBubble:enabled"/>
   <int value="2059322877" label="new-avatar-menu"/>
   <int value="2060099975" label="AndroidBottomToolbar:enabled"/>
   <int value="2061585881" label="EnableAIPromptAPI:enabled"/>
diff --git a/tools/metrics/histograms/metadata/android/enums.xml b/tools/metrics/histograms/metadata/android/enums.xml
index 5a46dc1..4a802d6 100644
--- a/tools/metrics/histograms/metadata/android/enums.xml
+++ b/tools/metrics/histograms/metadata/android/enums.xml
@@ -645,6 +645,12 @@
   <int value="143" label="WebSettingsCompat.setHasEnrolledInstrumentEnabled"/>
   <int value="144" label="WebSettingsCompat.getHasEnrolledInstrumentEnabled"/>
   <int value="145" label="WebViewBuilder"/>
+  <int value="146" label="WebSettingsCompat.setIncludeCookiesOnIntercept"/>
+  <int value="147" label="WebSettingsCompat.getIncludeCookiesOnIntercept"/>
+  <int value="148"
+      label="ServiceWorkerWebSettingsCompat.setIncludeCookiesOnIntercept"/>
+  <int value="149"
+      label="ServiceWorkerWebSettingsCompat.getIncludeCookiesOnIntercept"/>
 <!-- LINT.ThenChange(/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java:ApiCall) -->
 
 </enum>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 20c4665e..ec2947a 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -273,6 +273,9 @@
                in group'"/>
   <variant name="LongpressForeground"
       summary="tabs opened from the long press context menu"/>
+  <variant name="LongpressForegroundInGroup"
+      summary="For tabs which must be opened in the foreground in the same
+               group as the parent"/>
   <variant name="LongpressIncognito"
       summary="tabs opened from the long press context menu item 'Open in
                Incognito Tab'"/>
@@ -7212,18 +7215,6 @@
   </summary>
 </histogram>
 
-<histogram name="Android.WebView.RegisterResourcePathsAvailable" enum="Boolean"
-    expires_after="2025-10-22">
-  <owner>alexmitra@chromium.org</owner>
-  <owner>src/android_webview/OWNERS</owner>
-  <summary>
-    On Android V and above, this histogram logs whether the
-    registerResourcePaths method is available once per startup after startup
-    completes. It will be unavailable if the OEM has disabled the flag that
-    gates the API.
-  </summary>
-</histogram>
-
 <histogram name="Android.WebView.RelatedSitesVisitedWeekly" units="sites"
     expires_after="2025-06-25">
   <owner>bewise@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/enterprise/enums.xml b/tools/metrics/histograms/metadata/enterprise/enums.xml
index ffbfd0d..83ea423 100644
--- a/tools/metrics/histograms/metadata/enterprise/enums.xml
+++ b/tools/metrics/histograms/metadata/enterprise/enums.xml
@@ -2228,6 +2228,7 @@
   <int value="1358" label="TabGroupSharingSettings"/>
   <int value="1359" label="NTPFootermanagementNoticeEnabled"/>
   <int value="1360" label="NTPFooterThemeAttributionEnabled"/>
+  <int value="1361" label="ClearWindowNameForNewBrowsingContextGroup"/>
 </enum>
 
 <enum name="EnterprisePoliciesSources">
diff --git a/tools/metrics/histograms/metadata/enterprise/histograms.xml b/tools/metrics/histograms/metadata/enterprise/histograms.xml
index 9fca4a3d..88bb75e 100644
--- a/tools/metrics/histograms/metadata/enterprise/histograms.xml
+++ b/tools/metrics/histograms/metadata/enterprise/histograms.xml
@@ -62,6 +62,11 @@
   <variant name="CreateCertificate" summary="certificate creation"/>
 </variants>
 
+<variants name="ClientCertLevel">
+  <variant name="Browser" summary="browser"/>
+  <variant name="Profile" summary="profile"/>
+</variants>
+
 <variants name="ContentAnalysisProtocol">
   <variant name="Multipart"
       summary="Content analysis of user action is done with the Multipart
@@ -425,93 +430,101 @@
 </histogram>
 
 <histogram
-    name="Enterprise.ClientCertificate.Profile.CreateCertificate.Success.HasCert"
+    name="Enterprise.ClientCertificate.{Level}.CreateCertificate.Success.HasCert"
     enum="BooleanSuccess" expires_after="2026-02-10">
   <owner>seblalancette@chromium.org</owner>
   <owner>cbe-device-trust-eng@google.com</owner>
   <summary>
-    Captures whether the HTTP response body for the Profile certificate creation
+    Captures whether the HTTP response body for the {Level} certificate creation
     request had a certificate or not.
   </summary>
+  <token key="Level" variants="ClientCertLevel"/>
 </histogram>
 
-<histogram name="Enterprise.ClientCertificate.Profile.CreatePrivateKey.Source"
+<histogram name="Enterprise.ClientCertificate.{Level}.CreatePrivateKey.Source"
     enum="CertificatePrivateKeySource" expires_after="2026-02-10">
   <owner>seblalancette@chromium.org</owner>
   <owner>cbe-device-trust-eng@google.com</owner>
   <summary>
     Captures the source of a private key creation after it was created (e.g.
     TPM-backed or not) as part of the client certificate provisioning flow for a
-    Profile.
+    {Level}.
   </summary>
+  <token key="Level" variants="ClientCertLevel"/>
 </histogram>
 
-<histogram name="Enterprise.ClientCertificate.Profile.Provisioning.Error"
+<histogram name="Enterprise.ClientCertificate.{Level}.Provisioning.Error"
     enum="CertificateProvisioningError" expires_after="2026-02-10">
   <owner>seblalancette@chromium.org</owner>
   <owner>cbe-device-trust-eng@google.com</owner>
   <summary>
     Captures a terminal error that occured when trying to provision a client
-    certificate for a Profile.
+    certificate for a {Level}.
   </summary>
+  <token key="Level" variants="ClientCertLevel"/>
 </histogram>
 
-<histogram name="Enterprise.ClientCertificate.Profile.Provisioning.Store.Error"
+<histogram name="Enterprise.ClientCertificate.{Level}.Provisioning.Store.Error"
     enum="CertificateStoreError" expires_after="2026-02-10">
   <owner>seblalancette@chromium.org</owner>
   <owner>cbe-device-trust-eng@google.com</owner>
   <summary>
     Captures a terminal store error that occured when trying to provision a
-    client certificate for a Profile.
+    client certificate for a {Level}.
   </summary>
+  <token key="Level" variants="ClientCertLevel"/>
 </histogram>
 
 <histogram
-    name="Enterprise.ClientCertificate.Profile.Provisioning.{ProvisioningScenario}.Outcome"
+    name="Enterprise.ClientCertificate.{Level}.Provisioning.{ProvisioningScenario}.Outcome"
     enum="BooleanSuccess" expires_after="2026-02-10">
   <owner>seblalancette@chromium.org</owner>
   <owner>cbe-device-trust-eng@google.com</owner>
   <summary>
     Captures whether the {ProvisioningScenario} provisioning scenario was
-    successful or not.
+    successful or not for {Level} level client certificates.
   </summary>
+  <token key="Level" variants="ClientCertLevel"/>
   <token key="ProvisioningScenario" variants="CertificateProvisioningScenario"/>
 </histogram>
 
 <histogram
-    name="Enterprise.ClientCertificate.Profile.Provisioning.{ProvisioningScenario}.{ProvisioningOutcome}.Latency"
+    name="Enterprise.ClientCertificate.{Level}.Provisioning.{ProvisioningScenario}.{ProvisioningOutcome}.Latency"
     units="ms" expires_after="2026-02-10">
   <owner>seblalancette@chromium.org</owner>
   <owner>cbe-device-trust-eng@google.com</owner>
   <summary>
     Captures how long a {ProvisioningOutcome} {ProvisioningScenario}
-    provisioning scenario took.
+    provisioning scenario took for {Level} level client certificates.
   </summary>
+  <token key="Level" variants="ClientCertLevel"/>
   <token key="ProvisioningScenario" variants="CertificateProvisioningScenario"/>
   <token key="ProvisioningOutcome" variants="CertificateProvisioningOutcome"/>
 </histogram>
 
 <histogram
-    name="Enterprise.ClientCertificate.Profile.{UploadScenario}.ClientError"
+    name="Enterprise.ClientCertificate.{Level}.{UploadScenario}.ClientError"
     enum="CertificateUploadClientError" expires_after="2026-02-10">
   <owner>seblalancette@chromium.org</owner>
   <owner>cbe-device-trust-eng@google.com</owner>
   <summary>
-    Captures a client error that occured when trying to upload a Profile's
+    Captures a client error that occured when trying to upload a {Level}'s
     public key for {UploadScenario}.
   </summary>
+  <token key="Level" variants="ClientCertLevel"/>
   <token key="UploadScenario" variants="ClientCertificateUploadScenario"/>
 </histogram>
 
 <histogram
-    name="Enterprise.ClientCertificate.Profile.{UploadScenario}.UploadCode"
+    name="Enterprise.ClientCertificate.{Level}.{UploadScenario}.UploadCode"
     enum="CombinedHttpResponseAndNetErrorCode" expires_after="2025-08-10">
   <owner>seblalancette@chromium.org</owner>
   <owner>cbe-device-trust-eng@google.com</owner>
   <summary>
-    Captures the HTTP response code for the upload of a Profile's public key for
+    Captures the HTTP response code for the upload of a {Level}'s public key for
     {UploadScenario}.
   </summary>
+  <token key="Level" variants="ClientCertLevel"/>
   <token key="UploadScenario" variants="ClientCertificateUploadScenario"/>
 </histogram>
 
@@ -1107,7 +1120,7 @@
 
 <histogram
     name="Enterprise.DeviceTrust.Mac.KeychainOSStatus.{KeyType}.{Operation}"
-    enum="ErrSecOSStatus" expires_after="2026-02-10">
+    enum="MacSecurityFrameworkOSStatus" expires_after="2026-02-10">
   <owner>seblalancette@chromium.org</owner>
   <owner>hmare@google.com</owner>
   <owner>cbe-device-trust-eng@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index 8a2ad52..4a457972 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -2047,6 +2047,13 @@
   </token>
 </histogram>
 
+<histogram name="IOS.GLICConsent" enum="GLICConsentType"
+    expires_after="2026-05-04">
+  <owner>prasanaa@google.com</owner>
+  <owner>bling-alchemy-eng@google.com</owner>
+  <summary>Records the consent response for GLIC.</summary>
+</histogram>
+
 <histogram name="IOS.GoogleOne.Outcome.{EntryPoint}" enum="GoogleOneOutcome"
     expires_after="2026-03-12">
   <owner>olivierrobin@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/mac/enums.xml b/tools/metrics/histograms/metadata/mac/enums.xml
index 59332c7..0cd6cf3 100644
--- a/tools/metrics/histograms/metadata/mac/enums.xml
+++ b/tools/metrics/histograms/metadata/mac/enums.xml
@@ -26,94 +26,6 @@
 
 <enums>
 
-<!-- https://github.com/apple-oss-distributions/Security/blob/Security-61040.80.10.0.1/OSX/libsecurity_codesigning/lib/CSCommon.h#L64 -->
-
-<enum name="CodeSigningOSStatus">
-  <int value="-67072" label="errSecCSUnimplemented"/>
-  <int value="-67071" label="errSecCSInvalidObjectRef"/>
-  <int value="-67070" label="errSecCSInvalidFlags"/>
-  <int value="-67069" label="errSecCSObjectRequired"/>
-  <int value="-67068" label="errSecCSStaticCodeNotFound"/>
-  <int value="-67067" label="errSecCSUnsupportedGuestAttributes"/>
-  <int value="-67066" label="errSecCSInvalidAttributeValues"/>
-  <int value="-67065" label="errSecCSNoSuchCode"/>
-  <int value="-67064" label="errSecCSMultipleGuests"/>
-  <int value="-67063" label="errSecCSGuestInvalid"/>
-  <int value="-67062" label="errSecCSUnsigned"/>
-  <int value="-67061" label="errSecCSSignatureFailed"/>
-  <int value="-67060" label="errSecCSSignatureNotVerifiable"/>
-  <int value="-67059" label="errSecCSSignatureUnsupported"/>
-  <int value="-67058" label="errSecCSBadDictionaryFormat"/>
-  <int value="-67057" label="errSecCSResourcesNotSealed"/>
-  <int value="-67056" label="errSecCSResourcesNotFound"/>
-  <int value="-67055" label="errSecCSResourcesInvalid"/>
-  <int value="-67054" label="errSecCSBadResource"/>
-  <int value="-67053" label="errSecCSResourceRulesInvalid"/>
-  <int value="-67052" label="errSecCSReqInvalid"/>
-  <int value="-67051" label="errSecCSReqUnsupported"/>
-  <int value="-67050" label="errSecCSReqFailed"/>
-  <int value="-67049" label="errSecCSBadObjectFormat"/>
-  <int value="-67048" label="errSecCSInternalError"/>
-  <int value="-67047" label="errSecCSHostReject"/>
-  <int value="-67046" label="errSecCSNotAHost"/>
-  <int value="-67045" label="errSecCSSignatureInvalid"/>
-  <int value="-67044" label="errSecCSHostProtocolRelativePath"/>
-  <int value="-67043" label="errSecCSHostProtocolContradiction"/>
-  <int value="-67042" label="errSecCSHostProtocolDedicationError"/>
-  <int value="-67041" label="errSecCSHostProtocolNotProxy"/>
-  <int value="-67040" label="errSecCSHostProtocolStateError"/>
-  <int value="-67039" label="errSecCSHostProtocolUnrelated"/>
-  <int value="-67037" label="errSecCSNotSupported"/>
-  <int value="-67036" label="errSecCSCMSTooLarge"/>
-  <int value="-67035" label="errSecCSHostProtocolInvalidHash"/>
-  <int value="-67034" label="errSecCSStaticCodeChanged"/>
-  <int value="-67033" label="errSecCSDBDenied"/>
-  <int value="-67032" label="errSecCSDBAccess"/>
-  <int value="-67031" label="errSecCSHostProtocolInvalidAttribute"/>
-  <int value="-67030" label="errSecCSInfoPlistFailed"/>
-  <int value="-67029" label="errSecCSNoMainExecutable"/>
-  <int value="-67028" label="errSecCSBadBundleFormat"/>
-  <int value="-67027" label="errSecCSNoMatches"/>
-  <int value="-67026" label="errSecCSFileHardQuarantined"/>
-  <int value="-67025" label="errSecCSOutdated"/>
-  <int value="-67024" label="errSecCSDbCorrupt"/>
-  <int value="-67023" label="errSecCSResourceDirectoryFailed"/>
-  <int value="-67022" label="errSecCSUnsignedNestedCode"/>
-  <int value="-67021" label="errSecCSBadNestedCode"/>
-  <int value="-67020" label="errSecCSBadCallbackValue"/>
-  <int value="-67019" label="errSecCSHelperFailed"/>
-  <int value="-67018" label="errSecCSVetoed"/>
-  <int value="-67017" label="errSecCSBadLVArch"/>
-  <int value="-67016" label="errSecCSResourceNotSupported"/>
-  <int value="-67015" label="errSecCSRegularFile"/>
-  <int value="-67014" label="errSecCSUnsealedAppRoot"/>
-  <int value="-67013" label="errSecCSWeakResourceRules"/>
-  <int value="-67012" label="errSecCSDSStoreSymlink"/>
-  <int value="-67011" label="errSecCSAmbiguousBundleFormat"/>
-  <int value="-67010" label="errSecCSBadMainExecutable"/>
-  <int value="-67009" label="errSecCSBadFrameworkVersion"/>
-  <int value="-67008" label="errSecCSUnsealedFrameworkRoot"/>
-  <int value="-67007" label="errSecCSWeakResourceEnvelope"/>
-  <int value="-67006" label="errSecCSCancelled"/>
-  <int value="-67005" label="errSecCSInvalidPlatform"/>
-  <int value="-67004" label="errSecCSTooBig"/>
-  <int value="-67003" label="errSecCSInvalidSymlink"/>
-  <int value="-67002" label="errSecCSNotAppLike"/>
-  <int value="-67001" label="errSecCSBadDiskImageFormat"/>
-  <int value="-67000" label="errSecCSUnsupportedDigestAlgorithm"/>
-  <int value="-66999" label="errSecCSInvalidAssociatedFileData"/>
-  <int value="-66998" label="errSecCSInvalidTeamIdentifier"/>
-  <int value="-66997" label="errSecCSBadTeamIdentifier"/>
-  <int value="-66996" label="errSecCSSignatureUntrusted"/>
-  <int value="-66995" label="errSecMultipleExecSegments"/>
-  <int value="-66994" label="errSecCSInvalidEntitlements"/>
-  <int value="-66993" label="errSecCSInvalidRuntimeVersion"/>
-  <int value="-66992" label="errSecCSRevokedNotarization"/>
-  <int value="-66991" label="errSecCSCMSConstructionFailed"/>
-  <int value="-66990" label="errSecCSRemoteSignerFailed"/>
-  <int value="0" label="errSecSuccess"/>
-</enum>
-
 <enum name="MacCloneExists">
   <int value="0" label="EXISTS"/>
   <int value="1" label="MISSING_MAIN_EXECUTABLE"/>
@@ -239,6 +151,601 @@
   <int value="2" label="OTHER"/>
 </enum>
 
+<!--
+
+Error values returned by the Security.framework code signing APIs, keychain
+access APIs, as well as general API.
+
+This enum comprises values from the following headers in the macOS 15.4 SDK,
+which were chosen to cover actual errors logged in the field:
+- <CarbonCore.framework/MacErrors.h>
+- <Security.framework/Authorization.h>
+- <Security.framework/CSCommon.h>
+- <Security.framework/SecBase.h>
+-->
+
+<enum name="MacSecurityFrameworkOSStatus">
+  <int value="-67904" label="errSecMissingQualifiedCertStatement"/>
+  <int value="-67903" label="errSecCertificateDuplicateExtension"/>
+  <int value="-67902" label="errSecCertificateIsCA"/>
+  <int value="-67901" label="errSecCertificateValidityPeriodTooLong"/>
+  <int value="-67900" label="errSecCertificateNameNotAllowed"/>
+  <int value="-67899" label="errSecCertificatePolicyNotAllowed"/>
+  <int value="-67898" label="errSecTimestampRevocationNotification"/>
+  <int value="-67897" label="errSecTimestampRevocationWarning"/>
+  <int value="-67896" label="errSecTimestampWaiting"/>
+  <int value="-67895" label="errSecTimestampRejection"/>
+  <int value="-67894" label="errSecSigningTimeMissing"/>
+  <int value="-67893" label="errSecTimestampSystemFailure"/>
+  <int value="-67892" label="errSecTimestampAddInfoNotAvailable"/>
+  <int value="-67891" label="errSecTimestampUnacceptedExtension"/>
+  <int value="-67890" label="errSecTimestampUnacceptedPolicy"/>
+  <int value="-67889" label="errSecTimestampTimeNotAvailable"/>
+  <int value="-67888" label="errSecTimestampBadDataFormat"/>
+  <int value="-67887" label="errSecTimestampBadRequest"/>
+  <int value="-67886" label="errSecTimestampBadAlg"/>
+  <int value="-67885" label="errSecTimestampServiceNotAvailable"/>
+  <int value="-67884" label="errSecTimestampNotTrusted"/>
+  <int value="-67883" label="errSecTimestampInvalid"/>
+  <int value="-67882" label="errSecTimestampMissing"/>
+  <int value="-67881" label="errSecExtendedKeyUsageNotCritical"/>
+  <int value="-67880" label="errSecMissingRequiredExtension"/>
+  <int value="-67879" label="errSecInvalidModifyMode"/>
+  <int value="-67878" label="errSecInvalidNewOwner"/>
+  <int value="-67877" label="errSecInvalidIndexInfo"/>
+  <int value="-67876" label="errSecInvalidAccessRequest"/>
+  <int value="-67875" label="errSecInvalidDBLocation"/>
+  <int value="-67874" label="errSecUnsupportedOperator"/>
+  <int value="-67873" label="errSecUnsupportedNumSelectionPreds"/>
+  <int value="-67872" label="errSecUnsupportedQueryLimits"/>
+  <int value="-67871" label="errSecMissingValue"/>
+  <int value="-67870" label="errSecDatastoreIsOpen"/>
+  <int value="-67869" label="errSecDatabaseLocked"/>
+  <int value="-67868" label="errSecInvalidParsingModule"/>
+  <int value="-67867" label="errSecIncompatibleFieldFormat"/>
+  <int value="-67866" label="errSecFieldSpecifiedMultiple"/>
+  <int value="-67865" label="errSecUnsupportedNumRecordTypes"/>
+  <int value="-67864" label="errSecUnsupportedNumIndexes"/>
+  <int value="-67863" label="errSecUnsupportedNumAttributes"/>
+  <int value="-67862" label="errSecUnsupportedLocality"/>
+  <int value="-67861" label="errSecUnsupportedIndexInfo"/>
+  <int value="-67860" label="errSecUnsupportedFieldFormat"/>
+  <int value="-67859" label="errSecNoFieldValues"/>
+  <int value="-67858" label="errSecInvalidCRLIndex"/>
+  <int value="-67857" label="errSecInvalidBundleInfo"/>
+  <int value="-67856" label="errSecRequestDescriptor"/>
+  <int value="-67855" label="errSecInvalidRequestor"/>
+  <int value="-67854" label="errSecInvalidValidityPeriod"/>
+  <int value="-67853" label="errSecInvalidEncoding"/>
+  <int value="-67852" label="errSecInvalidTupleCredentials"/>
+  <int value="-67851" label="errSecInvalidBaseACLs"/>
+  <int value="-67850" label="errSecInvalidTupleGroup"/>
+  <int value="-67849" label="errSecUnsupportedService"/>
+  <int value="-67848" label="errSecUnsupportedAddressType"/>
+  <int value="-67847" label="errSecRequestRejected"/>
+  <int value="-67846" label="errSecRequestLost"/>
+  <int value="-67845" label="errSecRejectedForm"/>
+  <int value="-67844" label="errSecNoDefaultAuthority"/>
+  <int value="-67843" label="errSecNotTrusted"/>
+  <int value="-67842" label="errSecMultipleValuesUnsupported"/>
+  <int value="-67841" label="errSecInvalidTuple"/>
+  <int value="-67840" label="errSecInvalidStopOnPolicy"/>
+  <int value="-67839" label="errSecInvalidResponseVector"/>
+  <int value="-67838" label="errSecInvalidRequestInputs"/>
+  <int value="-67837" label="errSecInvalidReason"/>
+  <int value="-67836" label="errSecInvalidTimeString"/>
+  <int value="-67835" label="errSecInvalidPolicyIdentifiers"/>
+  <int value="-67834" label="errSecInvalidIndex"/>
+  <int value="-67833" label="errSecInvalidIdentifier"/>
+  <int value="-67832" label="errSecInvalidID"/>
+  <int value="-67831" label="errSecInvalidFormType"/>
+  <int value="-67830" label="errSecInvalidCRL"/>
+  <int value="-67829" label="errSecInvalidCRLType"/>
+  <int value="-67828" label="errSecInvalidCRLEncoding"/>
+  <int value="-67827" label="errSecInvalidCRLAuthority"/>
+  <int value="-67826" label="errSecInvalidCertAuthority"/>
+  <int value="-67825" label="errSecVerifyActionFailed"/>
+  <int value="-67824" label="errSecInvalidAuthority"/>
+  <int value="-67823" label="errSecInvalidAction"/>
+  <int value="-67822" label="errSecInsufficientCredentials"/>
+  <int value="-67821" label="errSecCertificateSuspended"/>
+  <int value="-67820" label="errSecCertificateRevoked"/>
+  <int value="-67819" label="errSecCertificateNotValidYet"/>
+  <int value="-67818" label="errSecCertificateExpired"/>
+  <int value="-67817" label="errSecCertificateCannotOperate"/>
+  <int value="-67816" label="errSecInvalidCRLGroup"/>
+  <int value="-67815" label="errSecInvalidDigestAlgorithm"/>
+  <int value="-67814" label="errSecAlreadyLoggedIn"/>
+  <int value="-67813" label="errSecInvalidLoginName"/>
+  <int value="-67812" label="errSecDeviceVerifyFailed"/>
+  <int value="-67811" label="errSecPublicKeyInconsistent"/>
+  <int value="-67810" label="errSecBlockSizeMismatch"/>
+  <int value="-67809" label="errSecQuerySizeUnknown"/>
+  <int value="-67808" label="errSecVerifyFailed"/>
+  <int value="-67807" label="errSecStagedOperationNotStarted"/>
+  <int value="-67806" label="errSecStagedOperationInProgress"/>
+  <int value="-67805" label="errSecMissingAttributeWrappedKeyFormat"/>
+  <int value="-67804" label="errSecInvalidAttributeWrappedKeyFormat"/>
+  <int value="-67803" label="errSecMissingAttributeSymmetricKeyFormat"/>
+  <int value="-67802" label="errSecInvalidAttributeSymmetricKeyFormat"/>
+  <int value="-67801" label="errSecMissingAttributePrivateKeyFormat"/>
+  <int value="-67800" label="errSecInvalidAttributePrivateKeyFormat"/>
+  <int value="-67799" label="errSecMissingAttributePublicKeyFormat"/>
+  <int value="-67798" label="errSecInvalidAttributePublicKeyFormat"/>
+  <int value="-67797" label="errSecMissingAttributeAccessCredentials"/>
+  <int value="-67796" label="errSecInvalidAttributeAccessCredentials"/>
+  <int value="-67795" label="errSecMissingAttributeDLDBHandle"/>
+  <int value="-67794" label="errSecInvalidAttributeDLDBHandle"/>
+  <int value="-67793" label="errSecMissingAttributeIterationCount"/>
+  <int value="-67792" label="errSecInvalidAttributeIterationCount"/>
+  <int value="-67791" label="errSecMissingAttributeSubprime"/>
+  <int value="-67790" label="errSecInvalidAttributeSubprime"/>
+  <int value="-67789" label="errSecMissingAttributeBase"/>
+  <int value="-67788" label="errSecInvalidAttributeBase"/>
+  <int value="-67787" label="errSecMissingAttributePrime"/>
+  <int value="-67786" label="errSecInvalidAttributePrime"/>
+  <int value="-67785" label="errSecMissingAttributeVersion"/>
+  <int value="-67784" label="errSecInvalidAttributeVersion"/>
+  <int value="-67783" label="errSecMissingAttributeEndDate"/>
+  <int value="-67782" label="errSecInvalidAttributeEndDate"/>
+  <int value="-67781" label="errSecMissingAttributeStartDate"/>
+  <int value="-67780" label="errSecInvalidAttributeStartDate"/>
+  <int value="-67779" label="errSecMissingAttributeEffectiveBits"/>
+  <int value="-67778" label="errSecInvalidAttributeEffectiveBits"/>
+  <int value="-67777" label="errSecMissingAttributeMode"/>
+  <int value="-67776" label="errSecInvalidAttributeMode"/>
+  <int value="-67775" label="errSecMissingAttributeKeyType"/>
+  <int value="-67774" label="errSecInvalidAttributeKeyType"/>
+  <int value="-67773" label="errSecMissingAttributeLabel"/>
+  <int value="-67772" label="errSecInvalidAttributeLabel"/>
+  <int value="-67771" label="errSecMissingAlgorithmParms"/>
+  <int value="-67770" label="errSecInvalidAlgorithmParms"/>
+  <int value="-67769" label="errSecMissingAttributeRounds"/>
+  <int value="-67768" label="errSecInvalidAttributeRounds"/>
+  <int value="-67767" label="errSecMissingAttributeOutputSize"/>
+  <int value="-67766" label="errSecInvalidAttributeOutputSize"/>
+  <int value="-67765" label="errSecMissingAttributeBlockSize"/>
+  <int value="-67764" label="errSecInvalidAttributeBlockSize"/>
+  <int value="-67763" label="errSecMissingAttributeKeyLength"/>
+  <int value="-67762" label="errSecInvalidAttributeKeyLength"/>
+  <int value="-67761" label="errSecMissingAttributePassphrase"/>
+  <int value="-67760" label="errSecInvalidAttributePassphrase"/>
+  <int value="-67759" label="errSecMissingAttributeSeed"/>
+  <int value="-67758" label="errSecInvalidAttributeSeed"/>
+  <int value="-67757" label="errSecMissingAttributeRandom"/>
+  <int value="-67756" label="errSecInvalidAttributeRandom"/>
+  <int value="-67755" label="errSecMissingAttributePadding"/>
+  <int value="-67754" label="errSecInvalidAttributePadding"/>
+  <int value="-67753" label="errSecMissingAttributeSalt"/>
+  <int value="-67752" label="errSecInvalidAttributeSalt"/>
+  <int value="-67751" label="errSecMissingAttributeInitVector"/>
+  <int value="-67750" label="errSecInvalidAttributeInitVector"/>
+  <int value="-67749" label="errSecMissingAttributeKey"/>
+  <int value="-67748" label="errSecInvalidAttributeKey"/>
+  <int value="-67747" label="errSecInvalidAlgorithm"/>
+  <int value="-67746" label="errSecInvalidContext"/>
+  <int value="-67745" label="errSecInvalidOutputVector"/>
+  <int value="-67744" label="errSecInvalidInputVector"/>
+  <int value="-67743" label="errSecUnsupportedVectorOfBuffers"/>
+  <int value="-67742" label="errSecInvalidKeyFormat"/>
+  <int value="-67741" label="errSecUnsupportedKeyLabel"/>
+  <int value="-67740" label="errSecInvalidKeyLabel"/>
+  <int value="-67739" label="errSecUnsupportedKeyAttributeMask"/>
+  <int value="-67738" label="errSecInvalidKeyAttributeMask"/>
+  <int value="-67737" label="errSecUnsupportedKeyUsageMask"/>
+  <int value="-67736" label="errSecInvalidKeyUsageMask"/>
+  <int value="-67735" label="errSecUnsupportedKeySize"/>
+  <int value="-67734" label="errSecUnsupportedKeyFormat"/>
+  <int value="-67733" label="errSecKeyHeaderInconsistent"/>
+  <int value="-67732" label="errSecKeyBlobTypeIncorrect"/>
+  <int value="-67731" label="errSecKeyUsageIncorrect"/>
+  <int value="-67730" label="errSecAlgorithmMismatch"/>
+  <int value="-67729" label="errSecNotLoggedIn"/>
+  <int value="-67728" label="errSecAttachHandleBusy"/>
+  <int value="-67727" label="errSecDeviceError"/>
+  <int value="-67726" label="errSecPrivilegeNotSupported"/>
+  <int value="-67725" label="errSecOutputLengthError"/>
+  <int value="-67724" label="errSecInputLengthError"/>
+  <int value="-67723" label="errSecEventNotificationCallbackNotFound"/>
+  <int value="-67722" label="errSecModuleManagerNotFound"/>
+  <int value="-67721" label="errSecModuleManagerInitializeFailed"/>
+  <int value="-67720" label="errSecAttributeNotInContext"/>
+  <int value="-67719" label="errSecInvalidSubServiceID"/>
+  <int value="-67718" label="errSecModuleNotLoaded"/>
+  <int value="-67717" label="errSecInvalidServiceMask"/>
+  <int value="-67716" label="errSecInvalidAddinFunctionTable"/>
+  <int value="-67715" label="errSecLibraryReferenceNotFound"/>
+  <int value="-67714" label="errSecAddinUnloadFailed"/>
+  <int value="-67713" label="errSecInvalidKeyHierarchy"/>
+  <int value="-67712" label="errSecInvalidKeyRef"/>
+  <int value="-67711" label="errSecAddinLoadFailed"/>
+  <int value="-67710" label="errSecEMMUnloadFailed"/>
+  <int value="-67709" label="errSecEMMLoadFailed"/>
+  <int value="-67708" label="errSecInvalidPVC"/>
+  <int value="-67707" label="errSecPVCAlreadyConfigured"/>
+  <int value="-67706" label="errSecInvalidScope"/>
+  <int value="-67705" label="errSecPrivilegeNotGranted"/>
+  <int value="-67704" label="errSecIncompatibleVersion"/>
+  <int value="-67703" label="errSecInvalidSampleValue"/>
+  <int value="-67702" label="errSecInvalidACL"/>
+  <int value="-67701" label="errSecInvalidRecord"/>
+  <int value="-67700" label="errSecInvalidAccessCredentials"/>
+  <int value="-67699" label="errSecACLChangeFailed"/>
+  <int value="-67698" label="errSecACLAddFailed"/>
+  <int value="-67697" label="errSecACLReplaceFailed"/>
+  <int value="-67696" label="errSecACLDeleteFailed"/>
+  <int value="-67695" label="errSecCallbackFailed"/>
+  <int value="-67694" label="errSecInvalidValue"/>
+  <int value="-67693" label="errSecInvalidQuery"/>
+  <int value="-67692" label="errSecTagNotFound"/>
+  <int value="-67691" label="errSecInvalidCertificateGroup"/>
+  <int value="-67690" label="errSecInvalidCertificateRef"/>
+  <int value="-67689" label="errSecInvalidName"/>
+  <int value="-67688" label="errSecInvalidSignature"/>
+  <int value="-67687" label="errSecUnknownTag"/>
+  <int value="-67686" label="errSecVerificationFailure"/>
+  <int value="-67685" label="errSecInvalidNumberOfFields"/>
+  <int value="-67684" label="errSecCRLAlreadySigned"/>
+  <int value="-67683" label="errSecInvalidNetworkAddress"/>
+  <int value="-67682" label="errSecInvalidPassthroughID"/>
+  <int value="-67681" label="errSecInvalidDBList"/>
+  <int value="-67680" label="errSecInvalidHandle"/>
+  <int value="-67679" label="errSecInvalidGUID"/>
+  <int value="-67678" label="errSecModuleManifestVerifyFailed"/>
+  <int value="-67677" label="errSecFunctionFailed"/>
+  <int value="-67676" label="errSecSelfCheckFailed"/>
+  <int value="-67675" label="errSecInvalidPointer"/>
+  <int value="-67674" label="errSecMDSError"/>
+  <int value="-67673" label="errSecInvalidData"/>
+  <int value="-67672" label="errSecMemoryError"/>
+  <int value="-67671" label="errSecInternalError"/>
+  <int value="-67670" label="errSecFunctionIntegrityFail"/>
+  <int value="-67669" label="errSecPVCReferentNotFound"/>
+  <int value="-67668" label="errSecInvalidHandleUsage"/>
+  <int value="-67667" label="errSecNotInitialized"/>
+  <int value="-67666" label="errSecMobileMeFailedConsistencyCheck"/>
+  <int value="-67665" label="errSecMobileMeCSRVerifyFailure"/>
+  <int value="-67664" label="errSecMobileMeNoRequestPending"/>
+  <int value="-67663" label="errSecMobileMeRequestAlreadyPending"/>
+  <int value="-67662" label="errSecMobileMeServerServiceErr"/>
+  <int value="-67661" label="errSecMobileMeServerAlreadyExists"/>
+  <int value="-67660" label="errSecMobileMeServerNotAvailable"/>
+  <int value="-67659" label="errSecMobileMeServerError"/>
+  <int value="-67658" label="errSecMobileMeRequestRedirected"/>
+  <int value="-67657" label="errSecMobileMeRequestQueued"/>
+  <int value="-67656" label="errSecUnknownQualifiedCertStatement"/>
+  <int value="-67655" label="errSecInvalidSubjectName"/>
+  <int value="-67654" label="errSecTrustSettingDeny"/>
+  <int value="-67653" label="errSecResourceSignBadExtKeyUsage"/>
+  <int value="-67652" label="errSecResourceSignBadCertChainLength"/>
+  <int value="-67651" label="errSecCodeSigningDevelopment"/>
+  <int value="-67650" label="errSecCodeSigningNoExtendedKeyUsage"/>
+  <int value="-67649" label="errSecCodeSigningBadPathLengthConstraint"/>
+  <int value="-67648" label="errSecCodeSigningNoBasicConstraints"/>
+  <int value="-67647" label="errSecCodeSigningBadCertChainLength"/>
+  <int value="-67646" label="errSecOCSPResponseNonceMismatch"/>
+  <int value="-67645" label="errSecOCSPResponderUnauthorized"/>
+  <int value="-67644" label="errSecOCSPResponderSignatureRequired"/>
+  <int value="-67643" label="errSecOCSPResponderTryLater"/>
+  <int value="-67642" label="errSecOCSPResponderInternalError"/>
+  <int value="-67641" label="errSecOCSPResponderMalformedReq"/>
+  <int value="-67640" label="errSecOCSPNoSigner"/>
+  <int value="-67639" label="errSecOCSPSignatureError"/>
+  <int value="-67638" label="errSecRecordModified"/>
+  <int value="-67637" label="errSecOCSPNotTrustedToAnchor"/>
+  <int value="-67636" label="errSecNetworkFailure"/>
+  <int value="-67635" label="errSecIncompleteCertRevocationCheck"/>
+  <int value="-67634" label="errSecEndOfData"/>
+  <int value="-67633" label="errSecOCSPStatusUnrecognized"/>
+  <int value="-67632" label="errSecOCSPUnavailable"/>
+  <int value="-67631" label="errSecOCSPBadRequest"/>
+  <int value="-67630" label="errSecOCSPBadResponse"/>
+  <int value="-67629" label="errSecSSLBadExtendedKeyUsage"/>
+  <int value="-67628" label="errSecSMIMESubjAltNameNotCritical"/>
+  <int value="-67627" label="errSecSMIMENoEmailAddress"/>
+  <int value="-67626" label="errSecSMIMEKeyUsageNotCritical"/>
+  <int value="-67625" label="errSecSMIMEBadKeyUsage"/>
+  <int value="-67624" label="errSecSMIMEBadExtendedKeyUsage"/>
+  <int value="-67623" label="errSecSMIMEEmailAddressesNotFound"/>
+  <int value="-67622" label="errSecIDPFailure"/>
+  <int value="-67621" label="errSecCRLPolicyFailed"/>
+  <int value="-67620" label="errSecCRLNotTrusted"/>
+  <int value="-67619" label="errSecUnknownCRLExtension"/>
+  <int value="-67618" label="errSecUnknownCertExtension"/>
+  <int value="-67617" label="errSecCRLBadURI"/>
+  <int value="-67616" label="errSecCRLServerDown"/>
+  <int value="-67615" label="errSecCRLNotFound"/>
+  <int value="-67614" label="errSecCRLNotValidYet"/>
+  <int value="-67613" label="errSecCRLExpired"/>
+  <int value="-67612" label="errSecInvalidRoot"/>
+  <int value="-67611" label="errSecPathLengthConstraintExceeded"/>
+  <int value="-67610" label="errSecInvalidIDLinkage"/>
+  <int value="-67609" label="errSecInvalidExtendedKeyUsage"/>
+  <int value="-67608" label="errSecInvalidKeyUsageForPolicy"/>
+  <int value="-67607" label="errSecInvalidSubjectKeyID"/>
+  <int value="-67606" label="errSecInvalidAuthorityKeyID"/>
+  <int value="-67605" label="errSecNoBasicConstraintsCA"/>
+  <int value="-67604" label="errSecNoBasicConstraints"/>
+  <int value="-67603" label="errSecUnknownCriticalExtensionFlag"/>
+  <int value="-67602" label="errSecHostNameMismatch"/>
+  <int value="-67601" label="errSecIncompatibleKeyBlob"/>
+  <int value="-67600" label="errSecIncompatibleDatabaseBlob"/>
+  <int value="-67599" label="errSecInvalidKeyBlob"/>
+  <int value="-67598" label="errSecInvalidDatabaseBlob"/>
+  <int value="-67597" label="errSecFileTooBig"/>
+  <int value="-67596" label="errSecQuotaExceeded"/>
+  <int value="-67595" label="errSecAppleSSLv2Rollback"/>
+  <int value="-67594" label="errSecConversionError"/>
+  <int value="-67593" label="errSecAppleInvalidKeyEndDate"/>
+  <int value="-67592" label="errSecAppleInvalidKeyStartDate"/>
+  <int value="-67591" label="errSecAppleSignatureMismatch"/>
+  <int value="-67590" label="errSecApplePublicKeyIncomplete"/>
+  <int value="-67589" label="errSecAppleAddAppACLSubject"/>
+  <int value="-67588" label="errSecDeviceFailed"/>
+  <int value="-67587" label="errSecDeviceReset"/>
+  <int value="-67586" label="errSecInsufficientClientID"/>
+  <int value="-67585" label="errSecServiceNotAvailable"/>
+  <int value="-67072" label="errSecCSUnimplemented"/>
+  <int value="-67071" label="errSecCSInvalidObjectRef"/>
+  <int value="-67070" label="errSecCSInvalidFlags"/>
+  <int value="-67069" label="errSecCSObjectRequired"/>
+  <int value="-67068" label="errSecCSStaticCodeNotFound"/>
+  <int value="-67067" label="errSecCSUnsupportedGuestAttributes"/>
+  <int value="-67066" label="errSecCSInvalidAttributeValues"/>
+  <int value="-67065" label="errSecCSNoSuchCode"/>
+  <int value="-67064" label="errSecCSMultipleGuests"/>
+  <int value="-67063" label="errSecCSGuestInvalid"/>
+  <int value="-67062" label="errSecCSUnsigned"/>
+  <int value="-67061" label="errSecCSSignatureFailed"/>
+  <int value="-67060" label="errSecCSSignatureNotVerifiable"/>
+  <int value="-67059" label="errSecCSSignatureUnsupported"/>
+  <int value="-67058" label="errSecCSBadDictionaryFormat"/>
+  <int value="-67057" label="errSecCSResourcesNotSealed"/>
+  <int value="-67056" label="errSecCSResourcesNotFound"/>
+  <int value="-67055" label="errSecCSResourcesInvalid"/>
+  <int value="-67054" label="errSecCSBadResource"/>
+  <int value="-67053" label="errSecCSResourceRulesInvalid"/>
+  <int value="-67052" label="errSecCSReqInvalid"/>
+  <int value="-67051" label="errSecCSReqUnsupported"/>
+  <int value="-67050" label="errSecCSReqFailed"/>
+  <int value="-67049" label="errSecCSBadObjectFormat"/>
+  <int value="-67048" label="errSecCSInternalError"/>
+  <int value="-67047" label="errSecCSHostReject"/>
+  <int value="-67046" label="errSecCSNotAHost"/>
+  <int value="-67045" label="errSecCSSignatureInvalid"/>
+  <int value="-67044" label="errSecCSHostProtocolRelativePath"/>
+  <int value="-67043" label="errSecCSHostProtocolContradiction"/>
+  <int value="-67042" label="errSecCSHostProtocolDedicationError"/>
+  <int value="-67041" label="errSecCSHostProtocolNotProxy"/>
+  <int value="-67040" label="errSecCSHostProtocolStateError"/>
+  <int value="-67039" label="errSecCSHostProtocolUnrelated"/>
+  <int value="-67037" label="errSecCSNotSupported"/>
+  <int value="-67036" label="errSecCSCMSTooLarge"/>
+  <int value="-67035" label="errSecCSHostProtocolInvalidHash"/>
+  <int value="-67034" label="errSecCSStaticCodeChanged"/>
+  <int value="-67033" label="errSecCSSigDBDenied"/>
+  <int value="-67032" label="errSecCSSigDBAccess"/>
+  <int value="-67031" label="errSecCSHostProtocolInvalidAttribute"/>
+  <int value="-67030" label="errSecCSInfoPlistFailed"/>
+  <int value="-67029" label="errSecCSNoMainExecutable"/>
+  <int value="-67028" label="errSecCSBadBundleFormat"/>
+  <int value="-67027" label="errSecCSNoMatches"/>
+  <int value="-67026" label="errSecCSFileHardQuarantined"/>
+  <int value="-67025" label="errSecCSOutdated"/>
+  <int value="-67024" label="errSecCSDbCorrupt"/>
+  <int value="-67023" label="errSecCSResourceDirectoryFailed"/>
+  <int value="-67022" label="errSecCSUnsignedNestedCode"/>
+  <int value="-67021" label="errSecCSBadNestedCode"/>
+  <int value="-67020" label="errSecCSBadCallbackValue"/>
+  <int value="-67019" label="errSecCSHelperFailed"/>
+  <int value="-67018" label="errSecCSVetoed"/>
+  <int value="-67017" label="errSecCSBadLVArch"/>
+  <int value="-67016" label="errSecCSResourceNotSupported"/>
+  <int value="-67015" label="errSecCSRegularFile"/>
+  <int value="-67014" label="errSecCSUnsealedAppRoot"/>
+  <int value="-67013" label="errSecCSWeakResourceRules"/>
+  <int value="-67012" label="errSecCSDSStoreSymlink"/>
+  <int value="-67011" label="errSecCSAmbiguousBundleFormat"/>
+  <int value="-67010" label="errSecCSBadMainExecutable"/>
+  <int value="-67009" label="errSecCSBadFrameworkVersion"/>
+  <int value="-67008" label="errSecCSUnsealedFrameworkRoot"/>
+  <int value="-67007" label="errSecCSWeakResourceEnvelope"/>
+  <int value="-67006" label="errSecCSCancelled"/>
+  <int value="-67005" label="errSecCSInvalidPlatform"/>
+  <int value="-67004" label="errSecCSTooBig"/>
+  <int value="-67003" label="errSecCSInvalidSymlink"/>
+  <int value="-67002" label="errSecCSNotAppLike"/>
+  <int value="-67001" label="errSecCSBadDiskImageFormat"/>
+  <int value="-67000" label="errSecCSUnsupportedDigestAlgorithm"/>
+  <int value="-66999" label="errSecCSInvalidAssociatedFileData"/>
+  <int value="-66998" label="errSecCSInvalidTeamIdentifier"/>
+  <int value="-66997" label="errSecCSBadTeamIdentifier"/>
+  <int value="-66996" label="errSecCSSignatureUntrusted"/>
+  <int value="-66995" label="errSecMultipleExecSegments"/>
+  <int value="-66994" label="errSecCSInvalidEntitlements"/>
+  <int value="-66993" label="errSecCSInvalidRuntimeVersion"/>
+  <int value="-66992" label="errSecCSRevokedNotarization"/>
+  <int value="-66991" label="errSecCSCMSConstructionFailed"/>
+  <int value="-66990" label="errSecCSRemoteSignerFailed"/>
+  <int value="-60033" label="errAuthorizationBadAddress"/>
+  <int value="-60032" label="errAuthorizationToolEnvironmentError"/>
+  <int value="-60031" label="errAuthorizationToolExecuteFailure"/>
+  <int value="-60011" label="errAuthorizationInvalidFlags"/>
+  <int value="-60010" label="errAuthorizationInternalizeNotAllowed"/>
+  <int value="-60009" label="errAuthorizationExternalizeNotAllowed"/>
+  <int value="-60008" label="errAuthorizationInternal"/>
+  <int value="-60007" label="errAuthorizationInteractionNotAllowed"/>
+  <int value="-60006" label="errAuthorizationCanceled"/>
+  <int value="-60005" label="errAuthorizationDenied"/>
+  <int value="-60004" label="errAuthorizationInvalidPointer"/>
+  <int value="-60003" label="errAuthorizationInvalidTag"/>
+  <int value="-60002" label="errAuthorizationInvalidRef"/>
+  <int value="-60001" label="errAuthorizationInvalidSet"/>
+  <int value="-34020" label="errSecRestrictedAPI"/>
+  <int value="-34018" label="errSecMissingEntitlement"/>
+  <int value="-26275" label="errSecDecode"/>
+  <int value="-26267" label="errSecNotSigner"/>
+  <int value="-25320" label="errSecInDarkWake"/>
+  <int value="-25319" label="errSecInvalidPrefsDomain"/>
+  <int value="-25318" label="errSecCreateChainFailed"/>
+  <int value="-25317" label="errSecDataNotModifiable"/>
+  <int value="-25316" label="errSecDataNotAvailable"/>
+  <int value="-25315" label="errSecInteractionRequired"/>
+  <int value="-25314" label="errSecNoPolicyModule"/>
+  <int value="-25313" label="errSecNoCertificateModule"/>
+  <int value="-25312" label="errSecNoStorageModule"/>
+  <int value="-25311" label="errSecKeySizeNotAllowed"/>
+  <int value="-25310" label="errSecWrongSecVersion"/>
+  <int value="-25309" label="errSecReadOnlyAttr"/>
+  <int value="-25308" label="errSecInteractionNotAllowed"/>
+  <int value="-25307" label="errSecNoDefaultKeychain"/>
+  <int value="-25306" label="errSecNoSuchClass"/>
+  <int value="-25305" label="errSecInvalidSearchRef"/>
+  <int value="-25304" label="errSecInvalidItemRef"/>
+  <int value="-25303" label="errSecNoSuchAttr"/>
+  <int value="-25302" label="errSecDataTooLarge"/>
+  <int value="-25301" label="errSecBufferTooSmall"/>
+  <int value="-25300" label="errSecItemNotFound"/>
+  <int value="-25299" label="errSecDuplicateItem"/>
+  <int value="-25298" label="errSecInvalidCallback"/>
+  <int value="-25297" label="errSecDuplicateCallback"/>
+  <int value="-25296" label="errSecDuplicateKeychain"/>
+  <int value="-25295" label="errSecInvalidKeychain"/>
+  <int value="-25294" label="errSecNoSuchKeychain"/>
+  <int value="-25293" label="errSecAuthFailed"/>
+  <int value="-25292" label="errSecReadOnly"/>
+  <int value="-25291" label="errSecNotAvailable"/>
+  <int value="-25264" label="errSecPkcs12VerifyFailure"/>
+  <int value="-25263" label="errSecNoTrustSettings"/>
+  <int value="-25262" label="errSecInvalidTrustSettings"/>
+  <int value="-25261" label="errSecInvalidPasswordRef"/>
+  <int value="-25260" label="errSecPassphraseRequired"/>
+  <int value="-25259" label="errSecMultiplePrivKeys"/>
+  <int value="-25258" label="errSecKeyIsSensitive"/>
+  <int value="-25257" label="errSecUnknownFormat"/>
+  <int value="-25256" label="errSecUnsupportedFormat"/>
+  <int value="-25245" label="errSecTrustNotAvailable"/>
+  <int value="-25244" label="errSecInvalidOwnerEdit"/>
+  <int value="-25243" label="errSecNoAccessForItem"/>
+  <int value="-25242" label="errSecInvalidTrustSetting"/>
+  <int value="-25241" label="errSecPolicyNotFound"/>
+  <int value="-25240" label="errSecACLNotSimple"/>
+  <int value="-4960" label="errSecCoreFoundationUnknown"/>
+  <int value="-2070" label="errSecInternalComponent"/>
+  <int value="-909" label="errSecBadReq"/>
+  <int value="-128" label="errSecUserCanceled"/>
+  <int value="-108" label="errSecAllocate"/>
+  <int value="-61" label="errSecWrPerm"/>
+  <int value="-50" label="errSecParam"/>
+  <int value="-49" label="errSecOpWr"/>
+  <int value="-36" label="errSecIO"/>
+  <int value="-34" label="errSecDiskFull"/>
+  <int value="-4" label="errSecUnimplemented"/>
+  <int value="0" label="errSecSuccess"/>
+  <int value="100001" label="kPOSIXErrorEPERM"/>
+  <int value="100002" label="kPOSIXErrorENOENT"/>
+  <int value="100003" label="kPOSIXErrorESRCH"/>
+  <int value="100004" label="kPOSIXErrorEINTR"/>
+  <int value="100005" label="kPOSIXErrorEIO"/>
+  <int value="100006" label="kPOSIXErrorENXIO"/>
+  <int value="100007" label="kPOSIXErrorE2BIG"/>
+  <int value="100008" label="kPOSIXErrorENOEXEC"/>
+  <int value="100009" label="kPOSIXErrorEBADF"/>
+  <int value="100010" label="kPOSIXErrorECHILD"/>
+  <int value="100011" label="kPOSIXErrorEDEADLK"/>
+  <int value="100012" label="kPOSIXErrorENOMEM"/>
+  <int value="100013" label="kPOSIXErrorEACCES"/>
+  <int value="100014" label="kPOSIXErrorEFAULT"/>
+  <int value="100015" label="kPOSIXErrorENOTBLK"/>
+  <int value="100016" label="kPOSIXErrorEBUSY"/>
+  <int value="100017" label="kPOSIXErrorEEXIST"/>
+  <int value="100018" label="kPOSIXErrorEXDEV"/>
+  <int value="100019" label="kPOSIXErrorENODEV"/>
+  <int value="100020" label="kPOSIXErrorENOTDIR"/>
+  <int value="100021" label="kPOSIXErrorEISDIR"/>
+  <int value="100022" label="kPOSIXErrorEINVAL"/>
+  <int value="100023" label="kPOSIXErrorENFILE"/>
+  <int value="100024" label="kPOSIXErrorEMFILE"/>
+  <int value="100025" label="kPOSIXErrorENOTTY"/>
+  <int value="100026" label="kPOSIXErrorETXTBSY"/>
+  <int value="100027" label="kPOSIXErrorEFBIG"/>
+  <int value="100028" label="kPOSIXErrorENOSPC"/>
+  <int value="100029" label="kPOSIXErrorESPIPE"/>
+  <int value="100030" label="kPOSIXErrorEROFS"/>
+  <int value="100031" label="kPOSIXErrorEMLINK"/>
+  <int value="100032" label="kPOSIXErrorEPIPE"/>
+  <int value="100033" label="kPOSIXErrorEDOM"/>
+  <int value="100034" label="kPOSIXErrorERANGE"/>
+  <int value="100035" label="kPOSIXErrorEAGAIN"/>
+  <int value="100036" label="kPOSIXErrorEINPROGRESS"/>
+  <int value="100037" label="kPOSIXErrorEALREADY"/>
+  <int value="100038" label="kPOSIXErrorENOTSOCK"/>
+  <int value="100039" label="kPOSIXErrorEDESTADDRREQ"/>
+  <int value="100040" label="kPOSIXErrorEMSGSIZE"/>
+  <int value="100041" label="kPOSIXErrorEPROTOTYPE"/>
+  <int value="100042" label="kPOSIXErrorENOPROTOOPT"/>
+  <int value="100043" label="kPOSIXErrorEPROTONOSUPPORT"/>
+  <int value="100044" label="kPOSIXErrorESOCKTNOSUPPORT"/>
+  <int value="100045" label="kPOSIXErrorENOTSUP"/>
+  <int value="100046" label="kPOSIXErrorEPFNOSUPPORT"/>
+  <int value="100047" label="kPOSIXErrorEAFNOSUPPORT"/>
+  <int value="100048" label="kPOSIXErrorEADDRINUSE"/>
+  <int value="100049" label="kPOSIXErrorEADDRNOTAVAIL"/>
+  <int value="100050" label="kPOSIXErrorENETDOWN"/>
+  <int value="100051" label="kPOSIXErrorENETUNREACH"/>
+  <int value="100052" label="kPOSIXErrorENETRESET"/>
+  <int value="100053" label="kPOSIXErrorECONNABORTED"/>
+  <int value="100054" label="kPOSIXErrorECONNRESET"/>
+  <int value="100055" label="kPOSIXErrorENOBUFS"/>
+  <int value="100056" label="kPOSIXErrorEISCONN"/>
+  <int value="100057" label="kPOSIXErrorENOTCONN"/>
+  <int value="100058" label="kPOSIXErrorESHUTDOWN"/>
+  <int value="100059" label="kPOSIXErrorETOOMANYREFS"/>
+  <int value="100060" label="kPOSIXErrorETIMEDOUT"/>
+  <int value="100061" label="kPOSIXErrorECONNREFUSED"/>
+  <int value="100062" label="kPOSIXErrorELOOP"/>
+  <int value="100063" label="kPOSIXErrorENAMETOOLONG"/>
+  <int value="100064" label="kPOSIXErrorEHOSTDOWN"/>
+  <int value="100065" label="kPOSIXErrorEHOSTUNREACH"/>
+  <int value="100066" label="kPOSIXErrorENOTEMPTY"/>
+  <int value="100067" label="kPOSIXErrorEPROCLIM"/>
+  <int value="100068" label="kPOSIXErrorEUSERS"/>
+  <int value="100069" label="kPOSIXErrorEDQUOT"/>
+  <int value="100070" label="kPOSIXErrorESTALE"/>
+  <int value="100071" label="kPOSIXErrorEREMOTE"/>
+  <int value="100072" label="kPOSIXErrorEBADRPC"/>
+  <int value="100073" label="kPOSIXErrorERPCMISMATCH"/>
+  <int value="100074" label="kPOSIXErrorEPROGUNAVAIL"/>
+  <int value="100075" label="kPOSIXErrorEPROGMISMATCH"/>
+  <int value="100076" label="kPOSIXErrorEPROCUNAVAIL"/>
+  <int value="100077" label="kPOSIXErrorENOLCK"/>
+  <int value="100078" label="kPOSIXErrorENOSYS"/>
+  <int value="100079" label="kPOSIXErrorEFTYPE"/>
+  <int value="100080" label="kPOSIXErrorEAUTH"/>
+  <int value="100081" label="kPOSIXErrorENEEDAUTH"/>
+  <int value="100082" label="kPOSIXErrorEPWROFF"/>
+  <int value="100083" label="kPOSIXErrorEDEVERR"/>
+  <int value="100084" label="kPOSIXErrorEOVERFLOW"/>
+  <int value="100085" label="kPOSIXErrorEBADEXEC"/>
+  <int value="100086" label="kPOSIXErrorEBADARCH"/>
+  <int value="100087" label="kPOSIXErrorESHLIBVERS"/>
+  <int value="100088" label="kPOSIXErrorEBADMACHO"/>
+  <int value="100089" label="kPOSIXErrorECANCELED"/>
+  <int value="100090" label="kPOSIXErrorEIDRM"/>
+  <int value="100091" label="kPOSIXErrorENOMSG"/>
+  <int value="100092" label="kPOSIXErrorEILSEQ"/>
+  <int value="100093" label="kPOSIXErrorENOATTR"/>
+  <int value="100094" label="kPOSIXErrorEBADMSG"/>
+  <int value="100095" label="kPOSIXErrorEMULTIHOP"/>
+  <int value="100096" label="kPOSIXErrorENODATA"/>
+  <int value="100097" label="kPOSIXErrorENOLINK"/>
+  <int value="100098" label="kPOSIXErrorENOSR"/>
+  <int value="100099" label="kPOSIXErrorENOSTR"/>
+  <int value="100100" label="kPOSIXErrorEPROTO"/>
+  <int value="100101" label="kPOSIXErrorETIME"/>
+  <int value="100102" label="kPOSIXErrorEOPNOTSUPP"/>
+</enum>
+
 </enums>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/mac/histograms.xml b/tools/metrics/histograms/metadata/mac/histograms.xml
index cbeff1d..4036062 100644
--- a/tools/metrics/histograms/metadata/mac/histograms.xml
+++ b/tools/metrics/histograms/metadata/mac/histograms.xml
@@ -84,7 +84,7 @@
 </histogram>
 
 <histogram name="Mac.AppUpgradeCodeSignatureValidationStatus"
-    enum="CodeSigningOSStatus" expires_after="2025-09-14">
+    enum="MacSecurityFrameworkOSStatus" expires_after="2025-09-14">
   <owner>bur@google.com</owner>
   <owner>src/ui/base/cocoa/OWNERS</owner>
   <summary>
@@ -167,7 +167,7 @@
 </histogram>
 
 <histogram name="Mac.ProcessRequirement.FallbackValidationCategory.Result"
-    enum="CodeSigningOSStatus" expires_after="2025-10-05">
+    enum="MacSecurityFrameworkOSStatus" expires_after="2025-10-05">
   <owner>markrowe@chromium.org</owner>
   <owner>src/base/mac/OWNERS</owner>
   <summary>
@@ -228,7 +228,7 @@
 </histogram>
 
 <histogram name="Mac.ProcessRequirement.ValidationResult"
-    enum="CodeSigningOSStatus" expires_after="2025-10-05">
+    enum="MacSecurityFrameworkOSStatus" expires_after="2025-10-05">
   <owner>markrowe@chromium.org</owner>
   <owner>src/base/mac/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index ee8632d..e346fe6 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -82,6 +82,12 @@
   <variant name=".DawnSharedContext.MSAA"
       summary="Only counting MSAA memory used by the Graphite Dawn shared
                context."/>
+  <variant name=".DawnSharedContext.MSAA.Count"
+      summary="Only counting number of MSAA textures used by the Graphite
+               Dawn shared context."/>
+  <variant name=".DawnSharedContext.MSAA.Largest"
+      summary="Only record the biggest MSAA texture's memory used by the
+               Graphite Dawn shared context."/>
   <variant name=".DawnSharedContext.Textures"
       summary="Only counting textures' memory used by the Graphite Dawn
                shared context."/>
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index 14ca9d9..3c2d520 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -169,7 +169,7 @@
 </variants>
 
 <histogram name="Crypto.SecureEnclaveOperation.Mac.{Operation}.Error"
-    enum="ErrSecOSStatus" expires_after="2025-10-05">
+    enum="MacSecurityFrameworkOSStatus" expires_after="2025-10-05">
   <owner>seblalancette@chromium.org</owner>
   <owner>nsatragno@chromium.org</owner>
   <owner>src/crypto/OWNERS</owner>
diff --git a/tools/metrics/histograms/metadata/new_tab_page/enums.xml b/tools/metrics/histograms/metadata/new_tab_page/enums.xml
index 5375490..ddea51c8 100644
--- a/tools/metrics/histograms/metadata/new_tab_page/enums.xml
+++ b/tools/metrics/histograms/metadata/new_tab_page/enums.xml
@@ -596,7 +596,8 @@
   <int value="27" label="From Reparenting Background"/>
   <int value="28" label="From History Navigation Background"/>
   <int value="29" label="From History Navigation Foreground"/>
-  <int value="30" label="Size"/>
+  <int value="30" label="From Longpress Foreground In Group"/>
+  <int value="31" label="Size"/>
 </enum>
 
 <!-- LINT.ThenChange(//chrome/browser/ui/android/tab_model/tab_model.h:TabLaunchType) -->
diff --git a/tools/metrics/histograms/metadata/others/enums.xml b/tools/metrics/histograms/metadata/others/enums.xml
index 8f63b99..57b19b97 100644
--- a/tools/metrics/histograms/metadata/others/enums.xml
+++ b/tools/metrics/histograms/metadata/others/enums.xml
@@ -235,596 +235,6 @@
   <int value="12" label="SignalParseFailed"/>
 </enum>
 
-<!-- Values from various error header files to cover errors reported from the
-field, as of the macOS 15.4 SDK:
-- <CarbonCore.framework/MacErrors.h>
-- <Security.framework/Authorization.h>
-- <Security.framework/CSCommon.h>
-- <Security.framework/SecBase.h>
--->
-
-<enum name="SecurityFrameworkOSStatus">
-  <int value="-67904" label="errSecMissingQualifiedCertStatement"/>
-  <int value="-67903" label="errSecCertificateDuplicateExtension"/>
-  <int value="-67902" label="errSecCertificateIsCA"/>
-  <int value="-67901" label="errSecCertificateValidityPeriodTooLong"/>
-  <int value="-67900" label="errSecCertificateNameNotAllowed"/>
-  <int value="-67899" label="errSecCertificatePolicyNotAllowed"/>
-  <int value="-67898" label="errSecTimestampRevocationNotification"/>
-  <int value="-67897" label="errSecTimestampRevocationWarning"/>
-  <int value="-67896" label="errSecTimestampWaiting"/>
-  <int value="-67895" label="errSecTimestampRejection"/>
-  <int value="-67894" label="errSecSigningTimeMissing"/>
-  <int value="-67893" label="errSecTimestampSystemFailure"/>
-  <int value="-67892" label="errSecTimestampAddInfoNotAvailable"/>
-  <int value="-67891" label="errSecTimestampUnacceptedExtension"/>
-  <int value="-67890" label="errSecTimestampUnacceptedPolicy"/>
-  <int value="-67889" label="errSecTimestampTimeNotAvailable"/>
-  <int value="-67888" label="errSecTimestampBadDataFormat"/>
-  <int value="-67887" label="errSecTimestampBadRequest"/>
-  <int value="-67886" label="errSecTimestampBadAlg"/>
-  <int value="-67885" label="errSecTimestampServiceNotAvailable"/>
-  <int value="-67884" label="errSecTimestampNotTrusted"/>
-  <int value="-67883" label="errSecTimestampInvalid"/>
-  <int value="-67882" label="errSecTimestampMissing"/>
-  <int value="-67881" label="errSecExtendedKeyUsageNotCritical"/>
-  <int value="-67880" label="errSecMissingRequiredExtension"/>
-  <int value="-67879" label="errSecInvalidModifyMode"/>
-  <int value="-67878" label="errSecInvalidNewOwner"/>
-  <int value="-67877" label="errSecInvalidIndexInfo"/>
-  <int value="-67876" label="errSecInvalidAccessRequest"/>
-  <int value="-67875" label="errSecInvalidDBLocation"/>
-  <int value="-67874" label="errSecUnsupportedOperator"/>
-  <int value="-67873" label="errSecUnsupportedNumSelectionPreds"/>
-  <int value="-67872" label="errSecUnsupportedQueryLimits"/>
-  <int value="-67871" label="errSecMissingValue"/>
-  <int value="-67870" label="errSecDatastoreIsOpen"/>
-  <int value="-67869" label="errSecDatabaseLocked"/>
-  <int value="-67868" label="errSecInvalidParsingModule"/>
-  <int value="-67867" label="errSecIncompatibleFieldFormat"/>
-  <int value="-67866" label="errSecFieldSpecifiedMultiple"/>
-  <int value="-67865" label="errSecUnsupportedNumRecordTypes"/>
-  <int value="-67864" label="errSecUnsupportedNumIndexes"/>
-  <int value="-67863" label="errSecUnsupportedNumAttributes"/>
-  <int value="-67862" label="errSecUnsupportedLocality"/>
-  <int value="-67861" label="errSecUnsupportedIndexInfo"/>
-  <int value="-67860" label="errSecUnsupportedFieldFormat"/>
-  <int value="-67859" label="errSecNoFieldValues"/>
-  <int value="-67858" label="errSecInvalidCRLIndex"/>
-  <int value="-67857" label="errSecInvalidBundleInfo"/>
-  <int value="-67856" label="errSecRequestDescriptor"/>
-  <int value="-67855" label="errSecInvalidRequestor"/>
-  <int value="-67854" label="errSecInvalidValidityPeriod"/>
-  <int value="-67853" label="errSecInvalidEncoding"/>
-  <int value="-67852" label="errSecInvalidTupleCredentials"/>
-  <int value="-67851" label="errSecInvalidBaseACLs"/>
-  <int value="-67850" label="errSecInvalidTupleGroup"/>
-  <int value="-67849" label="errSecUnsupportedService"/>
-  <int value="-67848" label="errSecUnsupportedAddressType"/>
-  <int value="-67847" label="errSecRequestRejected"/>
-  <int value="-67846" label="errSecRequestLost"/>
-  <int value="-67845" label="errSecRejectedForm"/>
-  <int value="-67844" label="errSecNoDefaultAuthority"/>
-  <int value="-67843" label="errSecNotTrusted"/>
-  <int value="-67842" label="errSecMultipleValuesUnsupported"/>
-  <int value="-67841" label="errSecInvalidTuple"/>
-  <int value="-67840" label="errSecInvalidStopOnPolicy"/>
-  <int value="-67839" label="errSecInvalidResponseVector"/>
-  <int value="-67838" label="errSecInvalidRequestInputs"/>
-  <int value="-67837" label="errSecInvalidReason"/>
-  <int value="-67836" label="errSecInvalidTimeString"/>
-  <int value="-67835" label="errSecInvalidPolicyIdentifiers"/>
-  <int value="-67834" label="errSecInvalidIndex"/>
-  <int value="-67833" label="errSecInvalidIdentifier"/>
-  <int value="-67832" label="errSecInvalidID"/>
-  <int value="-67831" label="errSecInvalidFormType"/>
-  <int value="-67830" label="errSecInvalidCRL"/>
-  <int value="-67829" label="errSecInvalidCRLType"/>
-  <int value="-67828" label="errSecInvalidCRLEncoding"/>
-  <int value="-67827" label="errSecInvalidCRLAuthority"/>
-  <int value="-67826" label="errSecInvalidCertAuthority"/>
-  <int value="-67825" label="errSecVerifyActionFailed"/>
-  <int value="-67824" label="errSecInvalidAuthority"/>
-  <int value="-67823" label="errSecInvalidAction"/>
-  <int value="-67822" label="errSecInsufficientCredentials"/>
-  <int value="-67821" label="errSecCertificateSuspended"/>
-  <int value="-67820" label="errSecCertificateRevoked"/>
-  <int value="-67819" label="errSecCertificateNotValidYet"/>
-  <int value="-67818" label="errSecCertificateExpired"/>
-  <int value="-67817" label="errSecCertificateCannotOperate"/>
-  <int value="-67816" label="errSecInvalidCRLGroup"/>
-  <int value="-67815" label="errSecInvalidDigestAlgorithm"/>
-  <int value="-67814" label="errSecAlreadyLoggedIn"/>
-  <int value="-67813" label="errSecInvalidLoginName"/>
-  <int value="-67812" label="errSecDeviceVerifyFailed"/>
-  <int value="-67811" label="errSecPublicKeyInconsistent"/>
-  <int value="-67810" label="errSecBlockSizeMismatch"/>
-  <int value="-67809" label="errSecQuerySizeUnknown"/>
-  <int value="-67808" label="errSecVerifyFailed"/>
-  <int value="-67807" label="errSecStagedOperationNotStarted"/>
-  <int value="-67806" label="errSecStagedOperationInProgress"/>
-  <int value="-67805" label="errSecMissingAttributeWrappedKeyFormat"/>
-  <int value="-67804" label="errSecInvalidAttributeWrappedKeyFormat"/>
-  <int value="-67803" label="errSecMissingAttributeSymmetricKeyFormat"/>
-  <int value="-67802" label="errSecInvalidAttributeSymmetricKeyFormat"/>
-  <int value="-67801" label="errSecMissingAttributePrivateKeyFormat"/>
-  <int value="-67800" label="errSecInvalidAttributePrivateKeyFormat"/>
-  <int value="-67799" label="errSecMissingAttributePublicKeyFormat"/>
-  <int value="-67798" label="errSecInvalidAttributePublicKeyFormat"/>
-  <int value="-67797" label="errSecMissingAttributeAccessCredentials"/>
-  <int value="-67796" label="errSecInvalidAttributeAccessCredentials"/>
-  <int value="-67795" label="errSecMissingAttributeDLDBHandle"/>
-  <int value="-67794" label="errSecInvalidAttributeDLDBHandle"/>
-  <int value="-67793" label="errSecMissingAttributeIterationCount"/>
-  <int value="-67792" label="errSecInvalidAttributeIterationCount"/>
-  <int value="-67791" label="errSecMissingAttributeSubprime"/>
-  <int value="-67790" label="errSecInvalidAttributeSubprime"/>
-  <int value="-67789" label="errSecMissingAttributeBase"/>
-  <int value="-67788" label="errSecInvalidAttributeBase"/>
-  <int value="-67787" label="errSecMissingAttributePrime"/>
-  <int value="-67786" label="errSecInvalidAttributePrime"/>
-  <int value="-67785" label="errSecMissingAttributeVersion"/>
-  <int value="-67784" label="errSecInvalidAttributeVersion"/>
-  <int value="-67783" label="errSecMissingAttributeEndDate"/>
-  <int value="-67782" label="errSecInvalidAttributeEndDate"/>
-  <int value="-67781" label="errSecMissingAttributeStartDate"/>
-  <int value="-67780" label="errSecInvalidAttributeStartDate"/>
-  <int value="-67779" label="errSecMissingAttributeEffectiveBits"/>
-  <int value="-67778" label="errSecInvalidAttributeEffectiveBits"/>
-  <int value="-67777" label="errSecMissingAttributeMode"/>
-  <int value="-67776" label="errSecInvalidAttributeMode"/>
-  <int value="-67775" label="errSecMissingAttributeKeyType"/>
-  <int value="-67774" label="errSecInvalidAttributeKeyType"/>
-  <int value="-67773" label="errSecMissingAttributeLabel"/>
-  <int value="-67772" label="errSecInvalidAttributeLabel"/>
-  <int value="-67771" label="errSecMissingAlgorithmParms"/>
-  <int value="-67770" label="errSecInvalidAlgorithmParms"/>
-  <int value="-67769" label="errSecMissingAttributeRounds"/>
-  <int value="-67768" label="errSecInvalidAttributeRounds"/>
-  <int value="-67767" label="errSecMissingAttributeOutputSize"/>
-  <int value="-67766" label="errSecInvalidAttributeOutputSize"/>
-  <int value="-67765" label="errSecMissingAttributeBlockSize"/>
-  <int value="-67764" label="errSecInvalidAttributeBlockSize"/>
-  <int value="-67763" label="errSecMissingAttributeKeyLength"/>
-  <int value="-67762" label="errSecInvalidAttributeKeyLength"/>
-  <int value="-67761" label="errSecMissingAttributePassphrase"/>
-  <int value="-67760" label="errSecInvalidAttributePassphrase"/>
-  <int value="-67759" label="errSecMissingAttributeSeed"/>
-  <int value="-67758" label="errSecInvalidAttributeSeed"/>
-  <int value="-67757" label="errSecMissingAttributeRandom"/>
-  <int value="-67756" label="errSecInvalidAttributeRandom"/>
-  <int value="-67755" label="errSecMissingAttributePadding"/>
-  <int value="-67754" label="errSecInvalidAttributePadding"/>
-  <int value="-67753" label="errSecMissingAttributeSalt"/>
-  <int value="-67752" label="errSecInvalidAttributeSalt"/>
-  <int value="-67751" label="errSecMissingAttributeInitVector"/>
-  <int value="-67750" label="errSecInvalidAttributeInitVector"/>
-  <int value="-67749" label="errSecMissingAttributeKey"/>
-  <int value="-67748" label="errSecInvalidAttributeKey"/>
-  <int value="-67747" label="errSecInvalidAlgorithm"/>
-  <int value="-67746" label="errSecInvalidContext"/>
-  <int value="-67745" label="errSecInvalidOutputVector"/>
-  <int value="-67744" label="errSecInvalidInputVector"/>
-  <int value="-67743" label="errSecUnsupportedVectorOfBuffers"/>
-  <int value="-67742" label="errSecInvalidKeyFormat"/>
-  <int value="-67741" label="errSecUnsupportedKeyLabel"/>
-  <int value="-67740" label="errSecInvalidKeyLabel"/>
-  <int value="-67739" label="errSecUnsupportedKeyAttributeMask"/>
-  <int value="-67738" label="errSecInvalidKeyAttributeMask"/>
-  <int value="-67737" label="errSecUnsupportedKeyUsageMask"/>
-  <int value="-67736" label="errSecInvalidKeyUsageMask"/>
-  <int value="-67735" label="errSecUnsupportedKeySize"/>
-  <int value="-67734" label="errSecUnsupportedKeyFormat"/>
-  <int value="-67733" label="errSecKeyHeaderInconsistent"/>
-  <int value="-67732" label="errSecKeyBlobTypeIncorrect"/>
-  <int value="-67731" label="errSecKeyUsageIncorrect"/>
-  <int value="-67730" label="errSecAlgorithmMismatch"/>
-  <int value="-67729" label="errSecNotLoggedIn"/>
-  <int value="-67728" label="errSecAttachHandleBusy"/>
-  <int value="-67727" label="errSecDeviceError"/>
-  <int value="-67726" label="errSecPrivilegeNotSupported"/>
-  <int value="-67725" label="errSecOutputLengthError"/>
-  <int value="-67724" label="errSecInputLengthError"/>
-  <int value="-67723" label="errSecEventNotificationCallbackNotFound"/>
-  <int value="-67722" label="errSecModuleManagerNotFound"/>
-  <int value="-67721" label="errSecModuleManagerInitializeFailed"/>
-  <int value="-67720" label="errSecAttributeNotInContext"/>
-  <int value="-67719" label="errSecInvalidSubServiceID"/>
-  <int value="-67718" label="errSecModuleNotLoaded"/>
-  <int value="-67717" label="errSecInvalidServiceMask"/>
-  <int value="-67716" label="errSecInvalidAddinFunctionTable"/>
-  <int value="-67715" label="errSecLibraryReferenceNotFound"/>
-  <int value="-67714" label="errSecAddinUnloadFailed"/>
-  <int value="-67713" label="errSecInvalidKeyHierarchy"/>
-  <int value="-67712" label="errSecInvalidKeyRef"/>
-  <int value="-67711" label="errSecAddinLoadFailed"/>
-  <int value="-67710" label="errSecEMMUnloadFailed"/>
-  <int value="-67709" label="errSecEMMLoadFailed"/>
-  <int value="-67708" label="errSecInvalidPVC"/>
-  <int value="-67707" label="errSecPVCAlreadyConfigured"/>
-  <int value="-67706" label="errSecInvalidScope"/>
-  <int value="-67705" label="errSecPrivilegeNotGranted"/>
-  <int value="-67704" label="errSecIncompatibleVersion"/>
-  <int value="-67703" label="errSecInvalidSampleValue"/>
-  <int value="-67702" label="errSecInvalidACL"/>
-  <int value="-67701" label="errSecInvalidRecord"/>
-  <int value="-67700" label="errSecInvalidAccessCredentials"/>
-  <int value="-67699" label="errSecACLChangeFailed"/>
-  <int value="-67698" label="errSecACLAddFailed"/>
-  <int value="-67697" label="errSecACLReplaceFailed"/>
-  <int value="-67696" label="errSecACLDeleteFailed"/>
-  <int value="-67695" label="errSecCallbackFailed"/>
-  <int value="-67694" label="errSecInvalidValue"/>
-  <int value="-67693" label="errSecInvalidQuery"/>
-  <int value="-67692" label="errSecTagNotFound"/>
-  <int value="-67691" label="errSecInvalidCertificateGroup"/>
-  <int value="-67690" label="errSecInvalidCertificateRef"/>
-  <int value="-67689" label="errSecInvalidName"/>
-  <int value="-67688" label="errSecInvalidSignature"/>
-  <int value="-67687" label="errSecUnknownTag"/>
-  <int value="-67686" label="errSecVerificationFailure"/>
-  <int value="-67685" label="errSecInvalidNumberOfFields"/>
-  <int value="-67684" label="errSecCRLAlreadySigned"/>
-  <int value="-67683" label="errSecInvalidNetworkAddress"/>
-  <int value="-67682" label="errSecInvalidPassthroughID"/>
-  <int value="-67681" label="errSecInvalidDBList"/>
-  <int value="-67680" label="errSecInvalidHandle"/>
-  <int value="-67679" label="errSecInvalidGUID"/>
-  <int value="-67678" label="errSecModuleManifestVerifyFailed"/>
-  <int value="-67677" label="errSecFunctionFailed"/>
-  <int value="-67676" label="errSecSelfCheckFailed"/>
-  <int value="-67675" label="errSecInvalidPointer"/>
-  <int value="-67674" label="errSecMDSError"/>
-  <int value="-67673" label="errSecInvalidData"/>
-  <int value="-67672" label="errSecMemoryError"/>
-  <int value="-67671" label="errSecInternalError"/>
-  <int value="-67670" label="errSecFunctionIntegrityFail"/>
-  <int value="-67669" label="errSecPVCReferentNotFound"/>
-  <int value="-67668" label="errSecInvalidHandleUsage"/>
-  <int value="-67667" label="errSecNotInitialized"/>
-  <int value="-67666" label="errSecMobileMeFailedConsistencyCheck"/>
-  <int value="-67665" label="errSecMobileMeCSRVerifyFailure"/>
-  <int value="-67664" label="errSecMobileMeNoRequestPending"/>
-  <int value="-67663" label="errSecMobileMeRequestAlreadyPending"/>
-  <int value="-67662" label="errSecMobileMeServerServiceErr"/>
-  <int value="-67661" label="errSecMobileMeServerAlreadyExists"/>
-  <int value="-67660" label="errSecMobileMeServerNotAvailable"/>
-  <int value="-67659" label="errSecMobileMeServerError"/>
-  <int value="-67658" label="errSecMobileMeRequestRedirected"/>
-  <int value="-67657" label="errSecMobileMeRequestQueued"/>
-  <int value="-67656" label="errSecUnknownQualifiedCertStatement"/>
-  <int value="-67655" label="errSecInvalidSubjectName"/>
-  <int value="-67654" label="errSecTrustSettingDeny"/>
-  <int value="-67653" label="errSecResourceSignBadExtKeyUsage"/>
-  <int value="-67652" label="errSecResourceSignBadCertChainLength"/>
-  <int value="-67651" label="errSecCodeSigningDevelopment"/>
-  <int value="-67650" label="errSecCodeSigningNoExtendedKeyUsage"/>
-  <int value="-67649" label="errSecCodeSigningBadPathLengthConstraint"/>
-  <int value="-67648" label="errSecCodeSigningNoBasicConstraints"/>
-  <int value="-67647" label="errSecCodeSigningBadCertChainLength"/>
-  <int value="-67646" label="errSecOCSPResponseNonceMismatch"/>
-  <int value="-67645" label="errSecOCSPResponderUnauthorized"/>
-  <int value="-67644" label="errSecOCSPResponderSignatureRequired"/>
-  <int value="-67643" label="errSecOCSPResponderTryLater"/>
-  <int value="-67642" label="errSecOCSPResponderInternalError"/>
-  <int value="-67641" label="errSecOCSPResponderMalformedReq"/>
-  <int value="-67640" label="errSecOCSPNoSigner"/>
-  <int value="-67639" label="errSecOCSPSignatureError"/>
-  <int value="-67638" label="errSecRecordModified"/>
-  <int value="-67637" label="errSecOCSPNotTrustedToAnchor"/>
-  <int value="-67636" label="errSecNetworkFailure"/>
-  <int value="-67635" label="errSecIncompleteCertRevocationCheck"/>
-  <int value="-67634" label="errSecEndOfData"/>
-  <int value="-67633" label="errSecOCSPStatusUnrecognized"/>
-  <int value="-67632" label="errSecOCSPUnavailable"/>
-  <int value="-67631" label="errSecOCSPBadRequest"/>
-  <int value="-67630" label="errSecOCSPBadResponse"/>
-  <int value="-67629" label="errSecSSLBadExtendedKeyUsage"/>
-  <int value="-67628" label="errSecSMIMESubjAltNameNotCritical"/>
-  <int value="-67627" label="errSecSMIMENoEmailAddress"/>
-  <int value="-67626" label="errSecSMIMEKeyUsageNotCritical"/>
-  <int value="-67625" label="errSecSMIMEBadKeyUsage"/>
-  <int value="-67624" label="errSecSMIMEBadExtendedKeyUsage"/>
-  <int value="-67623" label="errSecSMIMEEmailAddressesNotFound"/>
-  <int value="-67622" label="errSecIDPFailure"/>
-  <int value="-67621" label="errSecCRLPolicyFailed"/>
-  <int value="-67620" label="errSecCRLNotTrusted"/>
-  <int value="-67619" label="errSecUnknownCRLExtension"/>
-  <int value="-67618" label="errSecUnknownCertExtension"/>
-  <int value="-67617" label="errSecCRLBadURI"/>
-  <int value="-67616" label="errSecCRLServerDown"/>
-  <int value="-67615" label="errSecCRLNotFound"/>
-  <int value="-67614" label="errSecCRLNotValidYet"/>
-  <int value="-67613" label="errSecCRLExpired"/>
-  <int value="-67612" label="errSecInvalidRoot"/>
-  <int value="-67611" label="errSecPathLengthConstraintExceeded"/>
-  <int value="-67610" label="errSecInvalidIDLinkage"/>
-  <int value="-67609" label="errSecInvalidExtendedKeyUsage"/>
-  <int value="-67608" label="errSecInvalidKeyUsageForPolicy"/>
-  <int value="-67607" label="errSecInvalidSubjectKeyID"/>
-  <int value="-67606" label="errSecInvalidAuthorityKeyID"/>
-  <int value="-67605" label="errSecNoBasicConstraintsCA"/>
-  <int value="-67604" label="errSecNoBasicConstraints"/>
-  <int value="-67603" label="errSecUnknownCriticalExtensionFlag"/>
-  <int value="-67602" label="errSecHostNameMismatch"/>
-  <int value="-67601" label="errSecIncompatibleKeyBlob"/>
-  <int value="-67600" label="errSecIncompatibleDatabaseBlob"/>
-  <int value="-67599" label="errSecInvalidKeyBlob"/>
-  <int value="-67598" label="errSecInvalidDatabaseBlob"/>
-  <int value="-67597" label="errSecFileTooBig"/>
-  <int value="-67596" label="errSecQuotaExceeded"/>
-  <int value="-67595" label="errSecAppleSSLv2Rollback"/>
-  <int value="-67594" label="errSecConversionError"/>
-  <int value="-67593" label="errSecAppleInvalidKeyEndDate"/>
-  <int value="-67592" label="errSecAppleInvalidKeyStartDate"/>
-  <int value="-67591" label="errSecAppleSignatureMismatch"/>
-  <int value="-67590" label="errSecApplePublicKeyIncomplete"/>
-  <int value="-67589" label="errSecAppleAddAppACLSubject"/>
-  <int value="-67588" label="errSecDeviceFailed"/>
-  <int value="-67587" label="errSecDeviceReset"/>
-  <int value="-67586" label="errSecInsufficientClientID"/>
-  <int value="-67585" label="errSecServiceNotAvailable"/>
-  <int value="-67072" label="errSecCSUnimplemented"/>
-  <int value="-67071" label="errSecCSInvalidObjectRef"/>
-  <int value="-67070" label="errSecCSInvalidFlags"/>
-  <int value="-67069" label="errSecCSObjectRequired"/>
-  <int value="-67068" label="errSecCSStaticCodeNotFound"/>
-  <int value="-67067" label="errSecCSUnsupportedGuestAttributes"/>
-  <int value="-67066" label="errSecCSInvalidAttributeValues"/>
-  <int value="-67065" label="errSecCSNoSuchCode"/>
-  <int value="-67064" label="errSecCSMultipleGuests"/>
-  <int value="-67063" label="errSecCSGuestInvalid"/>
-  <int value="-67062" label="errSecCSUnsigned"/>
-  <int value="-67061" label="errSecCSSignatureFailed"/>
-  <int value="-67060" label="errSecCSSignatureNotVerifiable"/>
-  <int value="-67059" label="errSecCSSignatureUnsupported"/>
-  <int value="-67058" label="errSecCSBadDictionaryFormat"/>
-  <int value="-67057" label="errSecCSResourcesNotSealed"/>
-  <int value="-67056" label="errSecCSResourcesNotFound"/>
-  <int value="-67055" label="errSecCSResourcesInvalid"/>
-  <int value="-67054" label="errSecCSBadResource"/>
-  <int value="-67053" label="errSecCSResourceRulesInvalid"/>
-  <int value="-67052" label="errSecCSReqInvalid"/>
-  <int value="-67051" label="errSecCSReqUnsupported"/>
-  <int value="-67050" label="errSecCSReqFailed"/>
-  <int value="-67049" label="errSecCSBadObjectFormat"/>
-  <int value="-67048" label="errSecCSInternalError"/>
-  <int value="-67047" label="errSecCSHostReject"/>
-  <int value="-67046" label="errSecCSNotAHost"/>
-  <int value="-67045" label="errSecCSSignatureInvalid"/>
-  <int value="-67044" label="errSecCSHostProtocolRelativePath"/>
-  <int value="-67043" label="errSecCSHostProtocolContradiction"/>
-  <int value="-67042" label="errSecCSHostProtocolDedicationError"/>
-  <int value="-67041" label="errSecCSHostProtocolNotProxy"/>
-  <int value="-67040" label="errSecCSHostProtocolStateError"/>
-  <int value="-67039" label="errSecCSHostProtocolUnrelated"/>
-  <int value="-67037" label="errSecCSNotSupported"/>
-  <int value="-67036" label="errSecCSCMSTooLarge"/>
-  <int value="-67035" label="errSecCSHostProtocolInvalidHash"/>
-  <int value="-67034" label="errSecCSStaticCodeChanged"/>
-  <int value="-67033" label="errSecCSSigDBDenied"/>
-  <int value="-67032" label="errSecCSSigDBAccess"/>
-  <int value="-67031" label="errSecCSHostProtocolInvalidAttribute"/>
-  <int value="-67030" label="errSecCSInfoPlistFailed"/>
-  <int value="-67029" label="errSecCSNoMainExecutable"/>
-  <int value="-67028" label="errSecCSBadBundleFormat"/>
-  <int value="-67027" label="errSecCSNoMatches"/>
-  <int value="-67026" label="errSecCSFileHardQuarantined"/>
-  <int value="-67025" label="errSecCSOutdated"/>
-  <int value="-67024" label="errSecCSDbCorrupt"/>
-  <int value="-67023" label="errSecCSResourceDirectoryFailed"/>
-  <int value="-67022" label="errSecCSUnsignedNestedCode"/>
-  <int value="-67021" label="errSecCSBadNestedCode"/>
-  <int value="-67020" label="errSecCSBadCallbackValue"/>
-  <int value="-67019" label="errSecCSHelperFailed"/>
-  <int value="-67018" label="errSecCSVetoed"/>
-  <int value="-67017" label="errSecCSBadLVArch"/>
-  <int value="-67016" label="errSecCSResourceNotSupported"/>
-  <int value="-67015" label="errSecCSRegularFile"/>
-  <int value="-67014" label="errSecCSUnsealedAppRoot"/>
-  <int value="-67013" label="errSecCSWeakResourceRules"/>
-  <int value="-67012" label="errSecCSDSStoreSymlink"/>
-  <int value="-67011" label="errSecCSAmbiguousBundleFormat"/>
-  <int value="-67010" label="errSecCSBadMainExecutable"/>
-  <int value="-67009" label="errSecCSBadFrameworkVersion"/>
-  <int value="-67008" label="errSecCSUnsealedFrameworkRoot"/>
-  <int value="-67007" label="errSecCSWeakResourceEnvelope"/>
-  <int value="-67006" label="errSecCSCancelled"/>
-  <int value="-67005" label="errSecCSInvalidPlatform"/>
-  <int value="-67004" label="errSecCSTooBig"/>
-  <int value="-67003" label="errSecCSInvalidSymlink"/>
-  <int value="-67002" label="errSecCSNotAppLike"/>
-  <int value="-67001" label="errSecCSBadDiskImageFormat"/>
-  <int value="-67000" label="errSecCSUnsupportedDigestAlgorithm"/>
-  <int value="-66999" label="errSecCSInvalidAssociatedFileData"/>
-  <int value="-66998" label="errSecCSInvalidTeamIdentifier"/>
-  <int value="-66997" label="errSecCSBadTeamIdentifier"/>
-  <int value="-66996" label="errSecCSSignatureUntrusted"/>
-  <int value="-66995" label="errSecMultipleExecSegments"/>
-  <int value="-66994" label="errSecCSInvalidEntitlements"/>
-  <int value="-66993" label="errSecCSInvalidRuntimeVersion"/>
-  <int value="-66992" label="errSecCSRevokedNotarization"/>
-  <int value="-66991" label="errSecCSCMSConstructionFailed"/>
-  <int value="-66990" label="errSecCSRemoteSignerFailed"/>
-  <int value="-60033" label="errAuthorizationBadAddress"/>
-  <int value="-60032" label="errAuthorizationToolEnvironmentError"/>
-  <int value="-60031" label="errAuthorizationToolExecuteFailure"/>
-  <int value="-60011" label="errAuthorizationInvalidFlags"/>
-  <int value="-60010" label="errAuthorizationInternalizeNotAllowed"/>
-  <int value="-60009" label="errAuthorizationExternalizeNotAllowed"/>
-  <int value="-60008" label="errAuthorizationInternal"/>
-  <int value="-60007" label="errAuthorizationInteractionNotAllowed"/>
-  <int value="-60006" label="errAuthorizationCanceled"/>
-  <int value="-60005" label="errAuthorizationDenied"/>
-  <int value="-60004" label="errAuthorizationInvalidPointer"/>
-  <int value="-60003" label="errAuthorizationInvalidTag"/>
-  <int value="-60002" label="errAuthorizationInvalidRef"/>
-  <int value="-60001" label="errAuthorizationInvalidSet"/>
-  <int value="-34020" label="errSecRestrictedAPI"/>
-  <int value="-34018" label="errSecMissingEntitlement"/>
-  <int value="-26275" label="errSecDecode"/>
-  <int value="-26267" label="errSecNotSigner"/>
-  <int value="-25320" label="errSecInDarkWake"/>
-  <int value="-25319" label="errSecInvalidPrefsDomain"/>
-  <int value="-25318" label="errSecCreateChainFailed"/>
-  <int value="-25317" label="errSecDataNotModifiable"/>
-  <int value="-25316" label="errSecDataNotAvailable"/>
-  <int value="-25315" label="errSecInteractionRequired"/>
-  <int value="-25314" label="errSecNoPolicyModule"/>
-  <int value="-25313" label="errSecNoCertificateModule"/>
-  <int value="-25312" label="errSecNoStorageModule"/>
-  <int value="-25311" label="errSecKeySizeNotAllowed"/>
-  <int value="-25310" label="errSecWrongSecVersion"/>
-  <int value="-25309" label="errSecReadOnlyAttr"/>
-  <int value="-25308" label="errSecInteractionNotAllowed"/>
-  <int value="-25307" label="errSecNoDefaultKeychain"/>
-  <int value="-25306" label="errSecNoSuchClass"/>
-  <int value="-25305" label="errSecInvalidSearchRef"/>
-  <int value="-25304" label="errSecInvalidItemRef"/>
-  <int value="-25303" label="errSecNoSuchAttr"/>
-  <int value="-25302" label="errSecDataTooLarge"/>
-  <int value="-25301" label="errSecBufferTooSmall"/>
-  <int value="-25300" label="errSecItemNotFound"/>
-  <int value="-25299" label="errSecDuplicateItem"/>
-  <int value="-25298" label="errSecInvalidCallback"/>
-  <int value="-25297" label="errSecDuplicateCallback"/>
-  <int value="-25296" label="errSecDuplicateKeychain"/>
-  <int value="-25295" label="errSecInvalidKeychain"/>
-  <int value="-25294" label="errSecNoSuchKeychain"/>
-  <int value="-25293" label="errSecAuthFailed"/>
-  <int value="-25292" label="errSecReadOnly"/>
-  <int value="-25291" label="errSecNotAvailable"/>
-  <int value="-25264" label="errSecPkcs12VerifyFailure"/>
-  <int value="-25263" label="errSecNoTrustSettings"/>
-  <int value="-25262" label="errSecInvalidTrustSettings"/>
-  <int value="-25261" label="errSecInvalidPasswordRef"/>
-  <int value="-25260" label="errSecPassphraseRequired"/>
-  <int value="-25259" label="errSecMultiplePrivKeys"/>
-  <int value="-25258" label="errSecKeyIsSensitive"/>
-  <int value="-25257" label="errSecUnknownFormat"/>
-  <int value="-25256" label="errSecUnsupportedFormat"/>
-  <int value="-25245" label="errSecTrustNotAvailable"/>
-  <int value="-25244" label="errSecInvalidOwnerEdit"/>
-  <int value="-25243" label="errSecNoAccessForItem"/>
-  <int value="-25242" label="errSecInvalidTrustSetting"/>
-  <int value="-25241" label="errSecPolicyNotFound"/>
-  <int value="-25240" label="errSecACLNotSimple"/>
-  <int value="-4960" label="errSecCoreFoundationUnknown"/>
-  <int value="-2070" label="errSecInternalComponent"/>
-  <int value="-909" label="errSecBadReq"/>
-  <int value="-128" label="errSecUserCanceled"/>
-  <int value="-108" label="errSecAllocate"/>
-  <int value="-61" label="errSecWrPerm"/>
-  <int value="-50" label="errSecParam"/>
-  <int value="-49" label="errSecOpWr"/>
-  <int value="-36" label="errSecIO"/>
-  <int value="-34" label="errSecDiskFull"/>
-  <int value="-4" label="errSecUnimplemented"/>
-  <int value="0" label="errSecSuccess"/>
-  <int value="100001" label="kPOSIXErrorEPERM"/>
-  <int value="100002" label="kPOSIXErrorENOENT"/>
-  <int value="100003" label="kPOSIXErrorESRCH"/>
-  <int value="100004" label="kPOSIXErrorEINTR"/>
-  <int value="100005" label="kPOSIXErrorEIO"/>
-  <int value="100006" label="kPOSIXErrorENXIO"/>
-  <int value="100007" label="kPOSIXErrorE2BIG"/>
-  <int value="100008" label="kPOSIXErrorENOEXEC"/>
-  <int value="100009" label="kPOSIXErrorEBADF"/>
-  <int value="100010" label="kPOSIXErrorECHILD"/>
-  <int value="100011" label="kPOSIXErrorEDEADLK"/>
-  <int value="100012" label="kPOSIXErrorENOMEM"/>
-  <int value="100013" label="kPOSIXErrorEACCES"/>
-  <int value="100014" label="kPOSIXErrorEFAULT"/>
-  <int value="100015" label="kPOSIXErrorENOTBLK"/>
-  <int value="100016" label="kPOSIXErrorEBUSY"/>
-  <int value="100017" label="kPOSIXErrorEEXIST"/>
-  <int value="100018" label="kPOSIXErrorEXDEV"/>
-  <int value="100019" label="kPOSIXErrorENODEV"/>
-  <int value="100020" label="kPOSIXErrorENOTDIR"/>
-  <int value="100021" label="kPOSIXErrorEISDIR"/>
-  <int value="100022" label="kPOSIXErrorEINVAL"/>
-  <int value="100023" label="kPOSIXErrorENFILE"/>
-  <int value="100024" label="kPOSIXErrorEMFILE"/>
-  <int value="100025" label="kPOSIXErrorENOTTY"/>
-  <int value="100026" label="kPOSIXErrorETXTBSY"/>
-  <int value="100027" label="kPOSIXErrorEFBIG"/>
-  <int value="100028" label="kPOSIXErrorENOSPC"/>
-  <int value="100029" label="kPOSIXErrorESPIPE"/>
-  <int value="100030" label="kPOSIXErrorEROFS"/>
-  <int value="100031" label="kPOSIXErrorEMLINK"/>
-  <int value="100032" label="kPOSIXErrorEPIPE"/>
-  <int value="100033" label="kPOSIXErrorEDOM"/>
-  <int value="100034" label="kPOSIXErrorERANGE"/>
-  <int value="100035" label="kPOSIXErrorEAGAIN"/>
-  <int value="100036" label="kPOSIXErrorEINPROGRESS"/>
-  <int value="100037" label="kPOSIXErrorEALREADY"/>
-  <int value="100038" label="kPOSIXErrorENOTSOCK"/>
-  <int value="100039" label="kPOSIXErrorEDESTADDRREQ"/>
-  <int value="100040" label="kPOSIXErrorEMSGSIZE"/>
-  <int value="100041" label="kPOSIXErrorEPROTOTYPE"/>
-  <int value="100042" label="kPOSIXErrorENOPROTOOPT"/>
-  <int value="100043" label="kPOSIXErrorEPROTONOSUPPORT"/>
-  <int value="100044" label="kPOSIXErrorESOCKTNOSUPPORT"/>
-  <int value="100045" label="kPOSIXErrorENOTSUP"/>
-  <int value="100046" label="kPOSIXErrorEPFNOSUPPORT"/>
-  <int value="100047" label="kPOSIXErrorEAFNOSUPPORT"/>
-  <int value="100048" label="kPOSIXErrorEADDRINUSE"/>
-  <int value="100049" label="kPOSIXErrorEADDRNOTAVAIL"/>
-  <int value="100050" label="kPOSIXErrorENETDOWN"/>
-  <int value="100051" label="kPOSIXErrorENETUNREACH"/>
-  <int value="100052" label="kPOSIXErrorENETRESET"/>
-  <int value="100053" label="kPOSIXErrorECONNABORTED"/>
-  <int value="100054" label="kPOSIXErrorECONNRESET"/>
-  <int value="100055" label="kPOSIXErrorENOBUFS"/>
-  <int value="100056" label="kPOSIXErrorEISCONN"/>
-  <int value="100057" label="kPOSIXErrorENOTCONN"/>
-  <int value="100058" label="kPOSIXErrorESHUTDOWN"/>
-  <int value="100059" label="kPOSIXErrorETOOMANYREFS"/>
-  <int value="100060" label="kPOSIXErrorETIMEDOUT"/>
-  <int value="100061" label="kPOSIXErrorECONNREFUSED"/>
-  <int value="100062" label="kPOSIXErrorELOOP"/>
-  <int value="100063" label="kPOSIXErrorENAMETOOLONG"/>
-  <int value="100064" label="kPOSIXErrorEHOSTDOWN"/>
-  <int value="100065" label="kPOSIXErrorEHOSTUNREACH"/>
-  <int value="100066" label="kPOSIXErrorENOTEMPTY"/>
-  <int value="100067" label="kPOSIXErrorEPROCLIM"/>
-  <int value="100068" label="kPOSIXErrorEUSERS"/>
-  <int value="100069" label="kPOSIXErrorEDQUOT"/>
-  <int value="100070" label="kPOSIXErrorESTALE"/>
-  <int value="100071" label="kPOSIXErrorEREMOTE"/>
-  <int value="100072" label="kPOSIXErrorEBADRPC"/>
-  <int value="100073" label="kPOSIXErrorERPCMISMATCH"/>
-  <int value="100074" label="kPOSIXErrorEPROGUNAVAIL"/>
-  <int value="100075" label="kPOSIXErrorEPROGMISMATCH"/>
-  <int value="100076" label="kPOSIXErrorEPROCUNAVAIL"/>
-  <int value="100077" label="kPOSIXErrorENOLCK"/>
-  <int value="100078" label="kPOSIXErrorENOSYS"/>
-  <int value="100079" label="kPOSIXErrorEFTYPE"/>
-  <int value="100080" label="kPOSIXErrorEAUTH"/>
-  <int value="100081" label="kPOSIXErrorENEEDAUTH"/>
-  <int value="100082" label="kPOSIXErrorEPWROFF"/>
-  <int value="100083" label="kPOSIXErrorEDEVERR"/>
-  <int value="100084" label="kPOSIXErrorEOVERFLOW"/>
-  <int value="100085" label="kPOSIXErrorEBADEXEC"/>
-  <int value="100086" label="kPOSIXErrorEBADARCH"/>
-  <int value="100087" label="kPOSIXErrorESHLIBVERS"/>
-  <int value="100088" label="kPOSIXErrorEBADMACHO"/>
-  <int value="100089" label="kPOSIXErrorECANCELED"/>
-  <int value="100090" label="kPOSIXErrorEIDRM"/>
-  <int value="100091" label="kPOSIXErrorENOMSG"/>
-  <int value="100092" label="kPOSIXErrorEILSEQ"/>
-  <int value="100093" label="kPOSIXErrorENOATTR"/>
-  <int value="100094" label="kPOSIXErrorEBADMSG"/>
-  <int value="100095" label="kPOSIXErrorEMULTIHOP"/>
-  <int value="100096" label="kPOSIXErrorENODATA"/>
-  <int value="100097" label="kPOSIXErrorENOLINK"/>
-  <int value="100098" label="kPOSIXErrorENOSR"/>
-  <int value="100099" label="kPOSIXErrorENOSTR"/>
-  <int value="100100" label="kPOSIXErrorEPROTO"/>
-  <int value="100101" label="kPOSIXErrorETIME"/>
-  <int value="100102" label="kPOSIXErrorEOPNOTSUPP"/>
-</enum>
-
 <enum name="SilentPushEvent">
   <int value="0" label="New Silent Push request"/>
   <int value="1" label="Notification enforcement skipped"/>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 392e95cf3..e7d1920 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -7149,7 +7149,7 @@
 </histogram>
 
 <histogram name="OSCrypt.Mac.FindGenericPasswordError"
-    enum="SecurityFrameworkOSStatus" expires_after="2025-12-01">
+    enum="MacSecurityFrameworkOSStatus" expires_after="2025-12-01">
   <owner>avi@chromium.org</owner>
   <owner>lgrey@chromium.org</owner>
   <owner>markrowe@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/password/histograms.xml b/tools/metrics/histograms/metadata/password/histograms.xml
index 6bf6d6dd..850e748 100644
--- a/tools/metrics/histograms/metadata/password/histograms.xml
+++ b/tools/metrics/histograms/metadata/password/histograms.xml
@@ -2268,7 +2268,7 @@
 </histogram>
 
 <histogram name="PasswordManager.LoginDatabase.DeleteFromKeychain"
-    enum="ErrSecOSStatus" expires_after="2025-10-12">
+    enum="MacSecurityFrameworkOSStatus" expires_after="2025-10-12">
   <owner>vsemeniuk@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -2810,7 +2810,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordCheckup.{Operation}.Error"
-    enum="CredentialManagerError" expires_after="2025-05-11">
+    enum="CredentialManagerError" expires_after="2025-09-14">
   <owner>ioanap@chromium.org</owner>
   <owner>izuzic@google.com</owner>
   <owner>vsemeniuk@google.com</owner>
@@ -2825,7 +2825,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordCheckup.{Operation}.ErrorLatency"
-    units="ms" expires_after="2025-05-11">
+    units="ms" expires_after="2025-09-14">
   <owner>ioanap@chromium.org</owner>
   <owner>izuzic@google.com</owner>
   <summary>
@@ -2839,7 +2839,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordCheckup.{Operation}.Latency"
-    units="ms" expires_after="2025-07-13">
+    units="ms" expires_after="2025-09-14">
   <owner>ioanap@chromium.org</owner>
   <owner>izuzic@google.com</owner>
   <summary>
@@ -3062,7 +3062,7 @@
 
 <histogram
     name="PasswordManager.PasswordSettings.{Function}.{Setting}{Type}.APIError1"
-    enum="PasswordStoreAndroidBackendAPIError" expires_after="2025-05-11">
+    enum="PasswordStoreAndroidBackendAPIError" expires_after="2025-11-11">
   <owner>izuzic@google.com</owner>
   <owner>ioanap@chromium.org</owner>
   <summary>
@@ -3085,7 +3085,7 @@
 
 <histogram
     name="PasswordManager.PasswordSettings.{Function}.{Setting}{Type}.ErrorCode"
-    enum="PasswordStoreAndroidBackendError" expires_after="2025-05-11">
+    enum="PasswordStoreAndroidBackendError" expires_after="2025-11-11">
   <owner>izuzic@google.com</owner>
   <owner>ioanap@chromium.org</owner>
   <summary>
@@ -3106,7 +3106,7 @@
 
 <histogram
     name="PasswordManager.PasswordSettings.{Function}.{Setting}{Type}.ErrorLatency"
-    units="ms" expires_after="2025-05-11">
+    units="ms" expires_after="2025-11-11">
   <owner>izuzic@google.com</owner>
   <owner>ioanap@chromium.org</owner>
   <summary>
@@ -3125,7 +3125,7 @@
 
 <histogram
     name="PasswordManager.PasswordSettings.{Function}.{Setting}{Type}.Latency"
-    units="ms" expires_after="2025-07-13">
+    units="ms" expires_after="2025-11-13">
   <owner>izuzic@google.com</owner>
   <owner>ioanap@chromium.org</owner>
   <summary>
@@ -3144,7 +3144,7 @@
 
 <histogram
     name="PasswordManager.PasswordSettings.{Function}.{Setting}{Type}.Success"
-    enum="BooleanSuccess" expires_after="2025-05-11">
+    enum="BooleanSuccess" expires_after="2025-11-11">
   <owner>izuzic@google.com</owner>
   <owner>ioanap@chromium.org</owner>
   <summary>
@@ -3163,7 +3163,7 @@
 
 <histogram
     name="PasswordManager.PasswordSettingsMigrationFailed.{Setting}.APIError2"
-    enum="PasswordStoreAndroidBackendAPIError" expires_after="2025-06-08">
+    enum="PasswordStoreAndroidBackendAPIError" expires_after="2025-09-08">
   <owner>sygiet@google.com</owner>
   <owner>ioanap@chromium.org</owner>
   <summary>
@@ -3178,7 +3178,7 @@
 </histogram>
 
 <histogram name="PasswordManager.PasswordSettingsMigrationSucceeded2"
-    enum="BooleanYesNo" expires_after="2025-06-22">
+    enum="BooleanYesNo" expires_after="2025-09-22">
   <owner>sygiet@google.com</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/performance_manager/histograms.xml b/tools/metrics/histograms/metadata/performance_manager/histograms.xml
index a529131..0cd9f72 100644
--- a/tools/metrics/histograms/metadata/performance_manager/histograms.xml
+++ b/tools/metrics/histograms/metadata/performance_manager/histograms.xml
@@ -20,12 +20,6 @@
   <variant name="Performance"/>
 </variants>
 
-<variants name="SiteDBWriteTrigger">
-  <variant name="ClearAllSiteData"/>
-  <variant name="ClearSiteDataForOrigins"/>
-  <variant name="WriteSiteDataIntoStore"/>
-</variants>
-
 <histogram name="CPU.Experimental.CpuEstimationTaskMigrated" enum="Boolean"
     expires_after="2025-03-10">
   <owner>anthonyvd@chromium.org</owner>
@@ -263,105 +257,6 @@
   </summary>
 </histogram>
 
-<histogram name="PerformanceManager.SiteDB.DatabaseInit"
-    enum="LocalSiteCharacteristicsDBInitStatus" expires_after="2023-12-28">
-  <owner>joenotcharles@google.com</owner>
-  <owner>chrome-catan@google.com</owner>
-  <summary>
-    The result of opening the Local Site Characteristics database. Recorded at
-    startup when the database gets opened. If the result is not Success,
-    PerformanceManager.SiteDB.DatabaseInitAfterRepair may be logged as well.
-  </summary>
-</histogram>
-
-<histogram name="PerformanceManager.SiteDB.DatabaseInitAfterDelete"
-    enum="LocalSiteCharacteristicsDBInitStatus" expires_after="2023-12-28">
-  <owner>joenotcharles@google.com</owner>
-  <owner>chrome-catan@google.com</owner>
-  <summary>
-    The result of opening the Local Site Characteristics database after deleting
-    it after a failed repair attempt. Recorded at startup when the database gets
-    opened, if the initial open and repair attempts failed.
-  </summary>
-</histogram>
-
-<histogram name="PerformanceManager.SiteDB.DatabaseInitAfterRepair"
-    enum="LocalSiteCharacteristicsDBInitStatus" expires_after="2023-12-28">
-  <owner>joenotcharles@google.com</owner>
-  <owner>chrome-catan@google.com</owner>
-  <summary>
-    The result of opening the Local Site Characteristics database after a repair
-    attempt. Recorded at startup when the database gets opened, if the initial
-    open attempt failed. If the result is not Success,
-    PerformanceManager.SiteDB.DatabaseInitAfterDelete may be logged as well.
-  </summary>
-</histogram>
-
-<histogram name="PerformanceManager.SiteDB.DatabaseRepair"
-    enum="BooleanSuccess" expires_after="2023-12-28">
-  <owner>joenotcharles@google.com</owner>
-  <owner>chrome-catan@google.com</owner>
-  <summary>
-    The result of trying to repair the Local Site Characteristics database after
-    a failed open. Recorded at startup when the database gets opened, if the
-    initial open attempt failed.
-  </summary>
-</histogram>
-
-<histogram
-    name="PerformanceManager.SiteDB.ObservationTimeBeforeFirstUse.{Feature}"
-    units="ms" expires_after="2023-12-28">
-  <owner>joenotcharles@google.com</owner>
-  <owner>chrome-catan@google.com</owner>
-  <summary>
-    The cumulative observation time before the {Feature} feature tracked in the
-    Local Site Characteristics database gets used. Reported the first time a
-    site is observed to use the feature.
-  </summary>
-  <token key="Feature">
-    <variant name="AudioUsageInBackground"/>
-    <variant name="FaviconUpdateInBackground"/>
-    <variant name="TitleUpdateInBackground"/>
-  </token>
-</histogram>
-
-<histogram name="PerformanceManager.SiteDB.ReadHasCompletedBeforeQuery"
-    enum="Boolean" expires_after="2023-12-28">
-  <owner>joenotcharles@google.com</owner>
-  <owner>chrome-catan@google.com</owner>
-  <summary>
-    Boolean indicating if the read operation from the Local Site Characteristics
-    database has completed when we query for the characteristics of a site.
-    Reported every time the database is queried.
-  </summary>
-</histogram>
-
-<histogram name="PerformanceManager.SiteDB.WriteCompleted.{SiteDBWriteTrigger}"
-    enum="BooleanCompleted" expires_after="2025-04-27">
-  <owner>joenotcharles@google.com</owner>
-  <owner>chrome-catan@google.com</owner>
-  <summary>
-    Logged when a write to the Local Site Characteristics database finishes, if
-    the write was scheduled by a call to {SiteDBWriteTrigger}. The count of this
-    should match PerformanceManager.SiteDB.WriteScheduled.{SiteDBWriteTrigger},
-    otherwise writes are being lost.
-  </summary>
-  <token key="SiteDBWriteTrigger" variants="SiteDBWriteTrigger"/>
-</histogram>
-
-<histogram name="PerformanceManager.SiteDB.WriteScheduled.{SiteDBWriteTrigger}"
-    enum="BooleanAttempted" expires_after="2025-04-27">
-  <owner>joenotcharles@google.com</owner>
-  <owner>chrome-catan@google.com</owner>
-  <summary>
-    Logged whenever a write to the Local Site Characteristics database is
-    scheduled by a call to {SiteDBWriteTrigger}. The count of this should match
-    PerformanceManager.SiteDB.WriteCompleted.{SiteDBWriteTrigger}, otherwise
-    writes are being lost.
-  </summary>
-  <token key="SiteDBWriteTrigger" variants="SiteDBWriteTrigger"/>
-</histogram>
-
 <histogram name="PerformanceManager.TabRevisitTracker.TimeToClose2"
     units="seconds" expires_after="2025-02-16">
   <owner>anthonyvd@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index 25d62be..765822a 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -246,8 +246,8 @@
 
 <histogram
     name="SafeBrowsing.AndroidTelemetry.DownloadDirectlyTriggeredByIntent"
-    enum="Boolean" expires_after="2025-06-08">
-  <owner>drubery@chromium.org</owner>
+    enum="Boolean" expires_after="2026-06-08">
+  <owner>chlily@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
     This records whether an APK download on Android was triggered by an intent.
diff --git a/tools/metrics/histograms/metadata/session/enums.xml b/tools/metrics/histograms/metadata/session/enums.xml
index df7a933..0420e59 100644
--- a/tools/metrics/histograms/metadata/session/enums.xml
+++ b/tools/metrics/histograms/metadata/session/enums.xml
@@ -46,8 +46,7 @@
 
 <enum name="SessionRestoreFinishReason">
   <int value="0"
-      label="SessionRestore.ForegroundTabFirstPaint4_XX successfully
-             recorded."/>
+      label="SessionRestore.ForegroundTabFirstPaint4 successfully recorded."/>
   <int value="1"
       label="No tabs were visible the whole time before first paint."/>
   <int value="2" label="No restored tabs were painted."/>
diff --git a/tools/metrics/histograms/metadata/session/histograms.xml b/tools/metrics/histograms/metadata/session/histograms.xml
index 39c3762..600a0b9 100644
--- a/tools/metrics/histograms/metadata/session/histograms.xml
+++ b/tools/metrics/histograms/metadata/session/histograms.xml
@@ -1168,26 +1168,12 @@
   </summary>
   <token key="SessionRestoreTabCounts">
     <variant name=""/>
-    <variant name="_1" summary="1 tab present"/>
-    <variant name="_2" summary="2 tabs present"/>
-    <variant name="_3" summary="3 tabs present"/>
-    <variant name="_4" summary="4 tabs present"/>
-    <variant name="_5" summary="5 tabs present"/>
-    <variant name="_6" summary="6 tabs present"/>
-    <variant name="_7" summary="7 tabs present"/>
-    <variant name="_8" summary="8 tabs present"/>
-    <variant name="_9" summary="9 tabs present"/>
-    <variant name="_10" summary="10 tabs present"/>
-    <variant name="_11" summary="11 tab present"/>
-    <variant name="_12" summary="12 tabs present"/>
-    <variant name="_13" summary="13 tabs present"/>
-    <variant name="_14" summary="14 tabs present"/>
-    <variant name="_15" summary="15 tabs present"/>
-    <variant name="_16" summary="16 tabs present"/>
-    <variant name="_17" summary="17 tabs present"/>
-    <variant name="_18" summary="18 tabs present"/>
-    <variant name="_19" summary="19 tabs present"/>
-    <variant name="_20" summary="20 tabs present"/>
+    <variant name=".1Tab" summary="1 tab present"/>
+    <variant name=".2to3Tabs" summary="2 to 3 tabs present"/>
+    <variant name=".4to7Tabs" summary="4 to 7 tabs present"/>
+    <variant name=".8to15Tabs" summary="8 to 15 tabs present"/>
+    <variant name=".16to31Tabs" summary="16 to 31 tabs present"/>
+    <variant name=".32PlusTabs" summary="32 or more tabs present"/>
   </token>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/signin/histograms.xml b/tools/metrics/histograms/metadata/signin/histograms.xml
index 984a5ea..3872829 100644
--- a/tools/metrics/histograms/metadata/signin/histograms.xml
+++ b/tools/metrics/histograms/metadata/signin/histograms.xml
@@ -2855,8 +2855,8 @@
   <summary>Track how a profile gets signed out.</summary>
 </histogram>
 
-<histogram name="Signin.SSOAuth.GetIdentities.ErrorCode" enum="ErrSecOSStatus"
-    expires_after="2026-03-02">
+<histogram name="Signin.SSOAuth.GetIdentities.ErrorCode"
+    enum="MacSecurityFrameworkOSStatus" expires_after="2026-03-02">
   <owner>jlebel@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sync/enums.xml b/tools/metrics/histograms/metadata/sync/enums.xml
index bd8d5c60..7294bf5a 100644
--- a/tools/metrics/histograms/metadata/sync/enums.xml
+++ b/tools/metrics/histograms/metadata/sync/enums.xml
@@ -1374,9 +1374,10 @@
   <int value="3" label="New Tab Page Infobar"/>
   <int value="4" label="Password Manager Error Message"/>
   <int value="5" label="Account menu"/>
+  <int value="6" label="Password Manager Settings"/>
 </enum>
 
-<!-- LINT.ThenChange(/components/sync/service/sync_service_utils.h:TrustedVaultUserActionTriggerForUMA) -->
+<!-- LINT.ThenChange(/components/sync/service/sync_service_utils.h:TrustedVaultUserActionTrigger) -->
 
 <!-- LINT.IfChange(UserActionableError) -->
 
diff --git a/tools/metrics/histograms/metadata/trusted_vault/enums.xml b/tools/metrics/histograms/metadata/trusted_vault/enums.xml
index 39e18f9..c5e717b 100644
--- a/tools/metrics/histograms/metadata/trusted_vault/enums.xml
+++ b/tools/metrics/histograms/metadata/trusted_vault/enums.xml
@@ -129,6 +129,7 @@
       label="Attempting registration while having persistent authentication
              error (deprecated)"/>
   <int value="6" label="Already registered V1"/>
+  <int value="7" label="Registration with constant key isn't supported"/>
 </enum>
 
 <!-- LINT.ThenChange(/components/trusted_vault/trusted_vault_histograms.h:TrustedVaultRecoveryFactorRegistrationState) -->
diff --git a/tools/search_engine_choice/DIR_METADATA b/tools/search_engine_choice/DIR_METADATA
deleted file mode 100644
index c8383f40..0000000
--- a/tools/search_engine_choice/DIR_METADATA
+++ /dev/null
@@ -1 +0,0 @@
-mixins: "//chrome/browser/search_engine_choice/COMMON_METADATA"
diff --git a/tools/search_engine_choice/OWNERS b/tools/search_engine_choice/OWNERS
deleted file mode 100644
index c7aa42b..0000000
--- a/tools/search_engine_choice/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://components/search_engines/search_engine_choice/COMPLIANCE_OWNERS
diff --git a/tools/search_engine_choice/download_search_engine_icons.py b/tools/search_engine_choice/download_search_engine_icons.py
deleted file mode 100644
index 6a1b5dcb..0000000
--- a/tools/search_engine_choice/download_search_engine_icons.py
+++ /dev/null
@@ -1,224 +0,0 @@
-# Copyright 2024 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-"""Handles the download of the search engine favicons.
-
-For all search engines referenced in regional_settings.json,
-downloads their Favicon, scales it and puts it as resource into the repository
-for display, e.g. in the search engine choice UI and settings.
-
-This should be run whenever regional_settings.json changes the list of
-search engines used per country, or whenever prepopulated_engines.json changes
-a favicon.
-
-To run:
-`python3 tools/search_engine_choice/download_search_engine_icons.py`.
-"""
-
-import hashlib
-import json
-import os
-import sys
-import requests
-
-import search_engine_icons_utils
-
-
-def get_image_hash(image_path):
-  """Gets the hash of the image that's passed as argument.
-
-  This is needed to check whether the downloaded image was already added in the
-  repo or not.
-
-  Args:
-    image_path: The path of the image for which we want to get the hash.
-
-  Returns:
-    The hash of the image that's passed as argument.
-  """
-  with open(image_path, 'rb') as image:
-    return hashlib.sha256(image.read()).hexdigest()
-
-
-def delete_files_in_directory(directory_path):
-  """Deletes previously generated icons.
-
-  Deletes the icons that were previously created and added to directory_path.
-
-  Args:
-    directory_path: The path of the directory where the icons live.
-
-  Raises:
-    OSError: Error occurred while deleting files in {directory_path}
-  """
-  try:
-    files = os.listdir(directory_path)
-    for file in files:
-      file_path = os.path.join(directory_path, file)
-
-      # Only remove pngs.
-      filename = os.path.basename(file_path)
-      if filename.endswith('.png') and os.path.isfile(file_path):
-        os.remove(file_path)
-    print('All files deleted successfully from ' + directory_path)
-  except OSError:
-    print('Error occurred while deleting files in ' + directory_path)
-
-
-def download_icons_from_android_search():
-  """Downloads icons from the android_search gstatic directory.
-
-  Goes through all search engines in `prepopulated_engines.json` and downloads
-  the corresponding 96x96 icon from the appropriate subfolder of
-  https://www.gstatic.com/android_search/search_providers/. Because there is no
-  way to list the contents of a directory on gstatic and because some
-  subfolders are not named exactly the same as in `prepopulated_engines.json`,
-  this function loads a config file `generate_search_engine_icons_config.json`
-  with the extra information needed to locate the icons.
-
-  The Google Search icon is not downloaded because it already exists in the
-  repo. Search engines not relevant to the to the default search engine choice
-  screen are ignored.
-  """
-  for percent in ['100', '200', '300']:
-    delete_files_in_directory(
-        f'components/resources/default_{percent}_percent/search_engine_choice')
-
-  with open(search_engine_icons_utils.config_file_path, 'r',
-            encoding='utf-8') as config_json:
-    config_data = json.loads(json_comment_eater.Nom(config_json.read()))
-    icon_hash_to_name = {}
-
-    for (engine, keyword) in sorted(
-        search_engine_icons_utils.get_used_engines_with_keywords("")):
-      icon_name = search_engine_icons_utils.keyword_to_identifer(keyword)
-      icon_full_path = f'components/resources/default_100_percent/search_engine_choice/{icon_name}.png'
-      if engine in config_data['engine_aliases']:
-        engine = config_data['engine_aliases'][engine]
-
-      directory_url = f'https://www.gstatic.com/android_search/search_providers/{engine}/'
-      try_filenames = []
-      if engine in config_data['non_default_icon_filenames']:
-        try_filenames = [
-            config_data['non_default_icon_filenames'][engine] + 'mdpi.png'
-        ]
-      try_filenames = try_filenames + [
-          f'{engine}_icon_mdpi.png',
-          f'{engine}_mdpi.png',
-          'mdpi.png',
-      ]
-      any_found = False
-      for filename in try_filenames:
-        icon_url = directory_url + filename
-        try:
-          img_data = requests.get(icon_url)
-        except requests.exceptions.RequestException as e:
-          print('Error when loading URL {icon_url}: {e}')
-          continue
-        if img_data.status_code == 200:
-          with open(icon_full_path, 'wb') as icon_file:
-            icon_file.write(img_data.content)
-          any_found = True
-          break
-      if not any_found:
-        print('WARNING: no icon found for search engine: ' + engine)
-        continue
-
-      icon_hash = get_image_hash(icon_full_path)
-      if icon_hash in icon_hash_to_name:
-        # We already have this icon.
-        engine_keyword_to_icon_name[keyword] = icon_hash_to_name[icon_hash]
-        os.remove(icon_full_path)
-        continue
-
-      # Download hidpi versions
-      # If the low dpi version is basename_mdpi.png, download basename_xhdpi.png
-      # and basename_xxhdpi.png.
-      for (resource_path, hidpi) in [('default_200_percent', 'xhdpi'),
-                                     ('default_300_percent', 'xxhdpi')]:
-        # Replace the substring "mdpi" by "xhdpi" or "xxhdpi" from the end.
-        (basename, mdpi_suffix, png_extension) = icon_url.rpartition('mdpi')
-        hidpi_url = basename + hidpi + png_extension
-        hidpi_path = f'components/resources/{resource_path}/search_engine_choice/{icon_name}.png'
-        try:
-          img_data = requests.get(hidpi_url)
-        except requests.exceptions.RequestException as e:
-          print('Error when loading URL {hidpi_url}: {e}')
-          continue
-        if img_data.status_code == 200:
-          with open(hidpi_path, 'wb') as icon_file:
-            icon_file.write(img_data.content)
-        else:
-          print('WARNING: no %s icon found for search engine: %s' %
-                (hidpi, engine))
-
-      engine_keyword_to_icon_name[keyword] = icon_name
-      icon_hash_to_name[icon_hash] = icon_name
-  print('Finished downloading icons')
-  os.system('tools/resources/optimize-png-files.sh search_engine_choice')
-
-
-def generate_icon_resource_code():
-  """Links the downloaded icons to their respective resource id.
-
-  Generates the code to link the icons to a resource ID in
-  `search_engine_choice_scaled_resources.grdp`
-  """
-  print('Writing to search_engine_choice_scaled_resources.grdp...')
-  with open('components/resources/search_engine_choice_scaled_resources.grdp',
-            'w',
-            encoding='utf-8',
-            newline='') as grdp_file:
-    grdp_file.write('<?xml version="1.0" encoding="utf-8"?>\n')
-    grdp_file.write(
-        '<!-- This file is generated using generate_search_engine_icons.py'
-        ' -->\n')
-    grdp_file.write("<!-- Don't modify it manually -->\n")
-    grdp_file.write('<grit-part>\n')
-
-    # Add the google resource id.
-    grdp_file.write('  <if expr="_google_chrome">\n')
-    grdp_file.write('    <structure type="chrome_scaled_image"'
-                    ' name="IDR_GOOGLE_COM_PNG"'
-                    ' file="google_chrome/google_search_logo.png" />\n')
-    grdp_file.write('  </if>\n')
-
-    # Add the remaining resource ids, sorted alphabetically.
-    resources = []
-    for engine_keyword, icon_name in engine_keyword_to_icon_name.items():
-      resource_id = search_engine_icons_utils.keyword_to_resource_name(
-          engine_keyword)
-      resources.append((resource_id, icon_name))
-
-    for resource_id, icon_name in sorted(resources):
-      grdp_file.write(
-          f'  <structure type="chrome_scaled_image" name="{resource_id}" file="search_engine_choice/{icon_name}.png" />\n'
-      )
-
-    grdp_file.write('</grit-part>\n')
-
-
-if sys.platform != 'linux':
-  print(
-      'Warning: This script has not been tested outside of the Linux platform')
-
-# Move to working directory to `src/`.
-current_file_path = os.path.dirname(__file__)
-os.chdir(current_file_path)
-os.chdir('../../')
-
-sys.path.insert(0,
-                os.path.normpath(current_file_path + "/../json_comment_eater"))
-try:
-  import json_comment_eater
-finally:
-  sys.path.pop(0)
-
-# This is a dictionary of engine keyword to corresponding icon name. Have an
-# empty icon name would mean that we weren't able to download the favicon for
-# that engine.
-engine_keyword_to_icon_name = {}
-
-download_icons_from_android_search()
-generate_icon_resource_code()
-print('Icon download completed.')
diff --git a/tools/search_engine_choice/generate_search_engine_icons.py b/tools/search_engine_choice/generate_search_engine_icons.py
deleted file mode 100644
index 34e5f4f..0000000
--- a/tools/search_engine_choice/generate_search_engine_icons.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright 2023 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import sys
-
-import search_engine_icons_utils
-
-
-def generate_icon_path_map(output_filename, engine_keywords):
-  """Generates the `kSearchEngineIconPathMap` map.
-
-  The code is generated in `search_engine_choice/generated_icon_utils-inc.cc`.
-  """
-  with open(output_filename, 'w', encoding='utf-8', newline='') as utils_file:
-    utils_file.write('constexpr auto kSearchEngineIconPathMap =\n')
-    utils_file.write(
-        '\tbase::MakeFixedFlatMap<std::u16string_view, std::string_view>({\n')
-
-    for engine_keyword in engine_keywords:
-      resource_name = search_engine_icons_utils.keyword_to_resource_name(
-          engine_keyword)
-      utils_file.write('\t\t{')
-      utils_file.write(f'u"{engine_keyword}", "chrome://theme/{resource_name}"')
-      utils_file.write('},\n')
-
-    # Add Google to the map
-    utils_file.write('\t\t{u"google.com",\n')
-    utils_file.write('#if BUILDFLAG(GOOGLE_CHROME_BRANDING)\n')
-    utils_file.write('\t\t "chrome://theme/IDR_GOOGLE_COM_PNG"\n')
-    utils_file.write('#else\n')
-    utils_file.write('\t\t "chrome://theme/IDR_DEFAULT_FAVICON"\n')
-    utils_file.write('#endif\n')
-    utils_file.write('\t}});\n')
-
-
-def generate_icon_resource_id_map(output_filename, engine_keywords):
-  """Generates the `kSearchEngineResourceIdMap` map.
-
-  The code is generated in
-  `components/search_engines/generated_search_engine_resource_ids-inc.cc`.
-  """
-  with open(output_filename, 'w', encoding='utf-8', newline='') as utils_file:
-    utils_file.write('constexpr auto kSearchEngineResourceIdMap =\n')
-    utils_file.write('\tbase::MakeFixedFlatMap<std::u16string_view, int>({\n')
-
-    for engine_keyword in engine_keywords:
-      resource_name = search_engine_icons_utils.keyword_to_resource_name(
-          engine_keyword)
-      utils_file.write('\t\t{')
-      utils_file.write(f'u"{engine_keyword}", {resource_name}')
-      utils_file.write('},\n')
-
-    # Add Google to the map
-    utils_file.write('\t\t{u"google.com",\n')
-    utils_file.write('#if BUILDFLAG(GOOGLE_CHROME_BRANDING)\n')
-    utils_file.write('\t\t IDR_GOOGLE_COM_PNG\n')
-    utils_file.write('#else\n')
-    utils_file.write('\t\t IDR_DEFAULT_FAVICON\n')
-    utils_file.write('#endif\n')
-    utils_file.write('\t}});\n\n')
-
-
-if len(sys.argv) >= 3:
-  src_dir = sys.argv[1]
-  generated_search_engine_resource_ids_file = sys.argv[2]
-  generated_icon_utils_file = sys.argv[3]
-
-engine_keywords = {
-    keyword
-    for (engine, keyword
-         ) in search_engine_icons_utils.get_used_engines_with_keywords(src_dir)
-}
-# Sort the engines so that the order of the engines in the generated files is
-# deterministic.
-engine_keywords = sorted(engine_keywords)
-generate_icon_path_map(generated_icon_utils_file, engine_keywords)
-generate_icon_resource_id_map(generated_search_engine_resource_ids_file,
-                              engine_keywords)
diff --git a/tools/search_engine_choice/generate_search_engine_icons_config.json b/tools/search_engine_choice/generate_search_engine_icons_config.json
deleted file mode 100644
index 7bb12c6..0000000
--- a/tools/search_engine_choice/generate_search_engine_icons_config.json
+++ /dev/null
@@ -1,79 +0,0 @@
-// This file is used by `generate_search_engine_icons.py` to locate the search
-// engine icons within https://www.gstatic.com/android_search/search_providers/.
-// These files are checked in at
-// `google3/googledata/html/external_content/gstatic/android_search/search_providers/`.
-// By default it will try to download an icon from
-// "[...]/{engine}/xxxhdpi.png" and from "[...]/{engine}/{engine}_xxxhdpi.png".
-// If the icon is located in a different subfolder, add the engine to
-// `engine_aliases` with the subfolder name. If the icon has a different
-// filename, add that filename to `non_default_icon_filenames`. If we don't need
-// an icon for the engine, add it to `ignored_engines`.
-{
-  // Engines for which we don't need to download icons, either because we
-  // already have them from another source or because they're not relevant to
-  // the default search engine choice screen.
-  "ignored_engines": [
-    "baidu",
-    "coccoc",
-    "daum",
-    "google",
-    "naver",
-    "so_360",
-    "sogou",
-    "yandex_by",
-    "yandex_com",
-    "yandex_kz",
-    "yandex_ru",
-    "yandex_tr"
-  ],
-  // The engine names in `prepopulated_engines.json` are not always the same as
-  // the subfolders with the correct icon. This dictionary associates the engine
-  // name with the subfolder name.
-  "engine_aliases": {
-    "info_com": "info",
-    "mail_ru": "mailru",
-    "metager_de": "metager",
-    "seznam_cz": "seznam",
-    "seznam_sk": "seznam",
-    "yahoo_ar": "yahoo",
-    "yahoo_at": "yahoo",
-    "yahoo_au": "yahoo",
-    "yahoo_br": "yahoo",
-    "yahoo_by": "yahoo",
-    "yahoo_ca": "yahoo",
-    "yahoo_ch": "yahoo",
-    "yahoo_cl": "yahoo",
-    "yahoo_co": "yahoo",
-    "yahoo_de": "yahoo",
-    "yahoo_dk": "yahoo",
-    "yahoo_emea": "yahoo",
-    "yahoo_es": "yahoo",
-    "yahoo_fi": "yahoo",
-    "yahoo_fr": "yahoo",
-    "yahoo_hk": "yahoo",
-    "yahoo_id": "yahoo",
-    "yahoo_in": "yahoo",
-    "yahoo_it": "yahoo",
-    "yahoo_jp": "yahoo",
-    "yahoo_mx": "yahoo",
-    "yahoo_my": "yahoo",
-    "yahoo_nl": "yahoo",
-    "yahoo_nz": "yahoo",
-    "yahoo_pe": "yahoo",
-    "yahoo_ph": "yahoo",
-    "yahoo_se": "yahoo",
-    "yahoo_sg": "yahoo",
-    "yahoo_th": "yahoo",
-    "yahoo_tr": "yahoo",
-    "yahoo_tw": "yahoo",
-    "yahoo_uk": "yahoo"
-  },
-  // The default icon filenames are '{engine}_xxxhdpi.png',
-  // '{engine}_icon_xxxhdpi.png' and 'xxxhdpi.png'.
-  "non_default_icon_filenames": {
-    "brave": "brave_logo_",
-    "duckduckgo": "logo_",
-    "info": "ic_launcher_",
-    "yep": "yep-widget-icon-"
-  }
-}
diff --git a/tools/search_engine_choice/search_engine_icons_utils.py b/tools/search_engine_choice/search_engine_icons_utils.py
deleted file mode 100644
index 42e511f..0000000
--- a/tools/search_engine_choice/search_engine_icons_utils.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# Copyright 2024 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import json
-import os
-import re
-import sys
-
-from typing import Any
-
-Json = dict[str, Any]
-
-config_file_path = 'tools/search_engine_choice/generate_search_engine_icons_config.json'
-regional_settings_file_path = 'third_party/search_engines_data/resources/definitions/regional_settings.json'
-prepopulated_engines_file_path = 'third_party/search_engines_data/resources/definitions/prepopulated_engines.json'
-
-
-def _load_json(src_dir: str, file_name: str) -> Json:
-  with open(src_dir + file_name, 'r', encoding='utf-8') as json_file:
-    return json.loads(json_comment_eater.Nom(json_file.read()))
-
-
-def keyword_to_identifer(keyword):
-  """Sanitized keyword to be used as identifier.
-
-  Replaces characters we find in prepopulates_engines.json's keyword field into
-  ones that are valid in file names and variable names.
-
-  Args:
-    keyword: the keyword string as in the json file.
-
-  Returns:
-    The keyword string with characters replaced that don't work in a variable or
-    file name.
-  """
-  return keyword.replace('.', '_').replace('-', '_')
-
-
-def keyword_to_resource_name(keyword):
-  """Resource name associated with a search engine keyword.
-
-  Args:
-    keyword: the keyword string as in the json file.
-
-  Returns:
-    The resource name for the icon, e.g. IDR_GOOGLE_COM_PNG.
-  """
-  icon_filename = keyword_to_identifer(keyword)
-  return 'IDR_' + icon_filename.upper() + '_PNG'
-
-
-def get_used_engines(src_dir) -> set[str]:
-  """Returns the set of used engines.
-
-  Returns the set of used engines. by checking which engines are used in
-  `regional_settings.json`.
-  """
-  used_engines = set()
-  settings = _load_json(src_dir, regional_settings_file_path)
-
-  for setting in settings['elements'].values():
-    used_engines.update(setting['search_engines'])
-
-  # Strip the reference from engine names.
-  used_engines = {engine.removeprefix('&') for engine in used_engines}
-
-  # Read the list of engines that should be excluded.
-  config = _load_json(src_dir, config_file_path)
-  ignored_engines = set(config['ignored_engines'])
-
-  return used_engines - ignored_engines
-
-
-def get_used_engines_with_keywords(src_dir) -> set[(Json, str)]:
-  """Returns the set of used engines with their keyword.
-
-  Reads the keyword from `components/search_engines/prepopulated_engines.json`.
-  Returns a set of pairs (engine, keyword).
-  """
-  engine_data = _load_json(src_dir, prepopulated_engines_file_path)
-  used_engines = get_used_engines(src_dir)
-
-  return {(used_engine, engine_data['elements'][used_engine]['keyword'])
-          for used_engine in used_engines}
-
-
-current_file_path = os.path.dirname(__file__)
-sys.path.insert(0,
-                os.path.normpath(current_file_path + "/../json_comment_eater"))
-try:
-  import json_comment_eater
-finally:
-  sys.path.pop(0)
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index 135551ed..bb5185a 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -4,7 +4,6 @@
 
 import("//build/buildflag_header.gni")
 import("//build/config/android/rules.gni")
-import("//build/config/locales.gni")
 import("//build/rust/rust_static_library.gni")
 import("//testing/test.gni")
 import("//third_party/jni_zero/jni_zero.gni")
@@ -190,10 +189,6 @@
 
 java_strings_grd("ui_strings_grd") {
   grd_file = "java/strings/android_ui_strings.grd"
-  outputs = [ "values/android_ui_strings.xml" ] +
-            process_file_template(
-                android_bundle_locales_as_resources,
-                [ "values-{{source_name_part}}/android_ui_strings.xml" ])
 }
 
 android_resources("ui_java_resources") {
diff --git a/ui/gtk/gtk.sigs b/ui/gtk/gtk.sigs
index 0b7f0e2..12e0c6b 100644
--- a/ui/gtk/gtk.sigs
+++ b/ui/gtk/gtk.sigs
@@ -92,6 +92,10 @@
 GtkCssProvider* gtk_css_provider_new(void);
 void gtk_disable_setlocale(void);
 void gtk_enumerate_printers(GtkPrinterFunc func, gpointer data, GDestroyNotify destroy, gboolean wait);
+GList* gtk_print_backend_load_modules(void);
+GListModel* gtk_print_backend_get_printers(GtkPrintBackend* print_backend);
+GList* gtk_print_backend_get_printer_list(GtkPrintBackend* print_backend);
+GtkPrinter* gtk_print_backend_find_printer(GtkPrintBackend* print_backend, const char* printer_name);
 void gtk_file_chooser_add_filter(GtkFileChooser* chooser, GtkFileFilter* filter);
 GFile* gtk_file_chooser_get_file(GtkFileChooser* chooser);
 GtkFileFilter* gtk_file_chooser_get_filter(GtkFileChooser* chooser);
diff --git a/ui/gtk/printing/print_dialog_gtk.cc b/ui/gtk/printing/print_dialog_gtk.cc
index 2f9738b..6fc177bd 100644
--- a/ui/gtk/printing/print_dialog_gtk.cc
+++ b/ui/gtk/printing/print_dialog_gtk.cc
@@ -19,7 +19,6 @@
 
 #include "base/check_op.h"
 #include "base/dcheck_is_on.h"
-#include "base/debug/crash_logging.h"
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
@@ -59,6 +58,14 @@
 constexpr int kPaperSizeTresholdMicrons = 100;
 constexpr int kMicronsInMm = 1000;
 
+struct GListDeleter {
+  void operator()(void* ptr) const {
+    if (ptr) {
+      g_list_free(reinterpret_cast<GList*>(ptr));
+    }
+  }
+};
+
 // Checks whether |gtk_paper_size| can be used to represent user selected media.
 // In fuzzy match mode checks that paper sizes are "close enough" (less than
 // 1mm difference). In the exact mode, looks for the paper with the same PPD
@@ -131,50 +138,6 @@
   return *settings;
 }
 
-// Helper class to track GTK printers.
-class GtkPrinterList {
- public:
-  GtkPrinterList() { gtk_enumerate_printers(SetPrinter, this, nullptr, TRUE); }
-
-  ~GtkPrinterList() = default;
-  // Can return nullptr if the printer cannot be found due to:
-  // - Printer list out of sync with printer dialog UI.
-  // - Querying for non-existent printers like 'Print to PDF'.
-  ScopedGObject<GtkPrinter> GetPrinterWithName(const std::string& name) {
-    if (name.empty()) {
-      return nullptr;
-    }
-
-    for (ScopedGObject<GtkPrinter>& printer : printers_) {
-      if (gtk_printer_get_name(printer.get()) == name) {
-        return printer;
-      }
-    }
-
-    return nullptr;
-  }
-
-  // TODO(crbug.com/411336538): Delete when done diagnosing.
-  std::string GetAllPrinterNames() {
-    std::string result;
-    for (ScopedGObject<GtkPrinter>& printer : printers_) {
-      result += gtk_printer_get_name(printer.get());
-      result += ',';
-    }
-    return result;
-  }
-
- private:
-  // Callback function used by gtk_enumerate_printers() to get all printer.
-  static gboolean SetPrinter(GtkPrinter* printer, gpointer data) {
-    GtkPrinterList* printer_list = reinterpret_cast<GtkPrinterList*>(data);
-    printer_list->printers_.push_back(WrapGObject(printer));
-    return FALSE;
-  }
-
-  std::vector<ScopedGObject<GtkPrinter>> printers_;
-};
-
 #if BUILDFLAG(ENABLE_OOP_PRINTING_NO_OOP_BASIC_PRINT_DIALOG)
 ScopedGKeyFile GetGKeyFileFromDict(const base::Value::Dict& data,
                                    std::string_view key) {
@@ -190,6 +153,43 @@
 }
 #endif
 
+std::vector<GtkPrintBackend*> GetPrintBackends() {
+  std::vector<GtkPrintBackend*> backends;
+  std::unique_ptr<GList, GListDeleter> backends_list(
+      gtk_print_backend_load_modules());
+  for (GList* it = backends_list.get(); it; it = it->next) {
+    auto* backend = reinterpret_cast<GtkPrintBackend*>(it->data);
+    CHECK(backend);
+    backends.push_back(backend);
+    // This is required to populate the printer list.
+    if (gtk::GtkCheckVersion(4)) {
+      WrapGObject(gtk_print_backend_get_printers(backend));
+    } else {
+      std::unique_ptr<GList, GListDeleter>(
+          gtk_print_backend_get_printer_list(backend));
+    }
+  }
+
+  // This is required to wait for the printer list to be populated.
+  gtk_enumerate_printers(
+      [](GtkPrinter* printer, gpointer data) -> gboolean { return false; },
+      nullptr, nullptr, true);
+
+  return backends;
+}
+
+ScopedGObject<GtkPrinter> GetPrinterWithName(const char* name) {
+  static base::NoDestructor<std::vector<GtkPrintBackend*>> backends(
+      GetPrintBackends());
+
+  for (GtkPrintBackend* backend : *backends) {
+    if (GtkPrinter* printer = gtk_print_backend_find_printer(backend, name)) {
+      return WrapGObject(printer);
+    }
+  }
+  return nullptr;
+}
+
 }  // namespace
 
 // static
@@ -215,8 +215,7 @@
       parent->RemoveObserver(this);
       gtk::ClearAuraTransientParent(dialog_, parent);
     }
-    gtk::GtkWindowDestroy(dialog_);
-    dialog_ = nullptr;
+    gtk::GtkWindowDestroy(dialog_.ExtractAsDangling());
   }
   if (reenable_parent_events_) {
     std::move(reenable_parent_events_).Run();
@@ -245,9 +244,8 @@
   if (!gtk_settings_)
     gtk_settings_ = gtk_print_settings_copy(GetLastUsedSettings().settings());
 
-  auto printer_list = std::make_unique<GtkPrinterList>();
-  printer_ = printer_list->GetPrinterWithName(
-      base::UTF16ToUTF8(settings->device_name()));
+  printer_ =
+      GetPrinterWithName(base::UTF16ToUTF8(settings->device_name()).c_str());
   if (printer_.get()) {
     gtk_print_settings_set_printer(gtk_settings_,
                                    gtk_printer_get_name(printer_.get()));
@@ -386,16 +384,7 @@
           printing::kLinuxSystemPrintDialogDataPrinter);
   CHECK(printer_name);
 
-  auto printer_list = std::make_unique<GtkPrinterList>();
-  printer_ = printer_list->GetPrinterWithName(*printer_name);
-  if (!printer_) {
-    // TODO(crbug.com/411336538): Delete the crash keys when done diagnosing.
-    // Then either put CHECK(printer_); back, or properly handle the failure.
-    SCOPED_CRASH_KEY_STRING64("PrintDialogGtk", "printer_name", *printer_name);
-    SCOPED_CRASH_KEY_STRING1024("PrintDialogGtk", "all_printer_names",
-                                printer_list->GetAllPrinterNames());
-    CHECK(false);
-  }
+  printer_ = GetPrinterWithName(printer_name->c_str());
 
   if (!gtk_settings_) {
     gtk_settings_ = gtk_print_settings_copy(GetLastUsedSettings().settings());
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
index 38eada8e..06767c5 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -495,6 +495,10 @@
   is_suspended_ = window_states.is_suspended;
 
   // The tiled state affects the window geometry, so apply it here.
+  // TODO(crbug.com/414831391): Remove this and notify in
+  // WindowTreeHostPlatform::OnStateUpdate instead like all other state changes.
+  // The only issue there is when doing that a regression was seen in kwin. See
+  // the bug description for additional details.
   if (window_states.tiled_edges != applied_state().tiled_edges) {
     // This configure changes the decoration insets.  We should adjust the
     // bounds appropriately.
diff --git a/ui/views/bubble/bubble_border.cc b/ui/views/bubble/bubble_border.cc
index 05e70fa26..3e5121b1 100644
--- a/ui/views/bubble/bubble_border.cc
+++ b/ui/views/bubble/bubble_border.cc
@@ -283,10 +283,6 @@
              GetShadowValues(nullptr, elevation, shadow_type));
 }
 
-void BubbleBorder::SetCornerRadius(int corner_radius) {
-  corner_radius_ = corner_radius;
-}
-
 gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect,
                                   const gfx::Size& contents_size) const {
   const gfx::Size size(GetSizeForContentsSize(contents_size));
@@ -673,13 +669,7 @@
 SkRRect BubbleBorder::GetClientRect(const View& view) const {
   gfx::RectF bounds(view.GetLocalBounds());
   bounds.Inset(gfx::InsetsF(GetInsets()));
-
-  // Give precedence to customized rounded corners when non-empty.
-  const gfx::RoundedCornersF corners =
-      rounded_corners_.IsEmpty() ? gfx::RoundedCornersF(corner_radius_)
-                                 : rounded_corners_;
-
-  return SkRRect(gfx::RRectF(bounds, corners));
+  return SkRRect(gfx::RRectF(bounds, rounded_corners_));
 }
 
 bool BubbleBorder::ShouldDrawStroke() const {
@@ -747,13 +737,8 @@
   gfx::RectF bounds(view->GetLocalBounds());
   bounds.Inset(gfx::InsetsF(border_->GetInsets()));
 
-  // Give precedence to customized rounded corners when non-empty.
-  const gfx::RoundedCornersF corners =
-      border_->rounded_corners().IsEmpty()
-          ? gfx::RoundedCornersF(border_->corner_radius())
-          : border_->rounded_corners();
-
-  canvas->sk_canvas()->drawRRect(SkRRect(gfx::RRectF(bounds, corners)), flags);
+  canvas->sk_canvas()->drawRRect(
+      SkRRect(gfx::RRectF(bounds, border_->rounded_corners())), flags);
 }
 
 }  // namespace views
diff --git a/ui/views/bubble/bubble_border.h b/ui/views/bubble/bubble_border.h
index cc93f994..e53fe6e 100644
--- a/ui/views/bubble/bubble_border.h
+++ b/ui/views/bubble/bubble_border.h
@@ -164,16 +164,12 @@
                                   gfx::Canvas* canvas,
                                   const ui::ColorProvider* color_provider);
 
-  // Set the corner radius, enables Material Design.
-  void SetCornerRadius(int radius);
-  int corner_radius() const { return corner_radius_; }
-
-  // Set the customized rounded corners. Takes precedence over `corner_radius_`
-  // when non-empty.
-  void set_rounded_corners(const gfx::RoundedCornersF& rounded_corners) {
-    rounded_corners_ = rounded_corners;
+  void set_rounded_corners(const gfx::RoundedCornersF& radii) {
+    rounded_corners_ = radii;
   }
-  const gfx::RoundedCornersF& rounded_corners() { return rounded_corners_; }
+  const gfx::RoundedCornersF& rounded_corners() const {
+    return rounded_corners_;
+  }
 
   // Get or set the arrow type.
   void set_arrow(Arrow arrow) { arrow_ = arrow; }
@@ -305,12 +301,6 @@
   Arrow arrow_;
   int arrow_offset_ = 0;
 
-  // Corner radius for the bubble border. If supplied the border will use
-  // material design.
-  int corner_radius_ = 0;
-
-  // Customized rounded corners for the bubble border. Takes precedence over
-  // `corner_radius_` when non-empty.
   gfx::RoundedCornersF rounded_corners_;
 
   // Whether a visible arrow should be present.
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc
index 39faa30..66b8dcf 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -590,7 +590,7 @@
   border->SetColor(background_color());
 
   if (GetParams().round_corners) {
-    border->SetCornerRadius(GetCornerRadius());
+    border->set_rounded_corners(gfx::RoundedCornersF(GetCornerRadius()));
   }
 
   frame->SetBubbleBorder(std::move(border));
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc
index 7758af5..f236d2ff 100644
--- a/ui/views/bubble/bubble_frame_view.cc
+++ b/ui/views/bubble/bubble_frame_view.cc
@@ -27,6 +27,7 @@
 #include "ui/display/screen.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/rounded_corners_f.h"
+#include "ui/gfx/geometry/rrect_f.h"
 #include "ui/gfx/geometry/skia_conversions.h"
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/image/image_skia_operations.h"
@@ -266,7 +267,7 @@
   // Convert to RRectF to accurately represent the rounded corners of the
   // dialog and allow events to pass through the shadows.
   gfx::RRectF round_contents_bounds(gfx::RectF(GetContentsBounds()),
-                                    bubble_border_->corner_radius());
+                                    bubble_border_->rounded_corners());
   if (bubble_border_->shadow() != BubbleBorder::NO_SHADOW) {
     round_contents_bounds.Outset(BubbleBorder::kBorderThicknessDip);
   }
@@ -310,7 +311,7 @@
   // Use a window mask roughly matching the border in the image assets.
   const int kBorderStrokeSize =
       bubble_border_->shadow() == BubbleBorder::NO_SHADOW ? 0 : 1;
-  const SkScalar kCornerRadius = SkIntToScalar(bubble_border_->corner_radius());
+  const gfx::RoundedCornersF& border_radii = bubble_border_->rounded_corners();
   const gfx::Insets border_insets = bubble_border_->GetInsets();
   SkRect rect = {
       SkIntToScalar(border_insets.left() - kBorderStrokeSize),
@@ -320,7 +321,13 @@
                     kBorderStrokeSize)};
 
   if (bubble_border_->shadow() == BubbleBorder::NO_SHADOW) {
-    window_mask->addRoundRect(rect, kCornerRadius, kCornerRadius);
+    SkRRect rrect;
+    SkVector radii[4]{{border_radii.upper_left(), border_radii.upper_left()},
+                      {border_radii.upper_right(), border_radii.upper_right()},
+                      {border_radii.lower_right(), border_radii.lower_right()},
+                      {border_radii.lower_left(), border_radii.lower_left()}};
+    rrect.setRectRadii(rect, radii);
+    window_mask->addRRect(rrect);
   } else {
     static const int kBottomBorderShadowSize = 2;
     rect.fBottom += SkIntToScalar(kBottomBorderShadowSize);
@@ -386,8 +393,7 @@
   // to the client view layer or applying a mask.  However, certain
   // implementations of the client view may need to do additional work to have a
   // rounded window.
-  GetWidget()->client_view()->UpdateWindowRoundedCorners(
-      gfx::RoundedCornersF(GetCornerRadius()));
+  GetWidget()->client_view()->UpdateWindowRoundedCorners(GetRoundedCorners());
 }
 
 bool BubbleFrameView::HasWindowTitle() const {
@@ -722,7 +728,9 @@
   bubble_border_ = border.get();
 
   if (footnote_container_) {
-    footnote_container_->SetCornerRadius(border->corner_radius());
+    const gfx::RoundedCornersF& radii = border->rounded_corners();
+    footnote_container_->SetRoundedCorners(radii.lower_left(),
+                                           radii.lower_right());
   }
 
   // Update the background, which relies on the border. First set it to null to
@@ -759,9 +767,12 @@
     RemoveChildViewT(footnote_container_.ExtractAsDangling());
   }
   if (view) {
-    int radius = bubble_border_ ? bubble_border_->corner_radius() : 0;
+    const gfx::RoundedCornersF& radii = bubble_border_
+                                            ? bubble_border_->rounded_corners()
+                                            : gfx::RoundedCornersF();
     footnote_container_ = AddChildView(std::make_unique<FootnoteContainerView>(
-        footnote_margins_, std::move(view), radius));
+        footnote_margins_, std::move(view), radii.lower_left(),
+        radii.lower_right()));
   }
   InvalidateLayout();
 }
@@ -800,13 +811,15 @@
   return preferred_arrow_adjustment_;
 }
 
-void BubbleFrameView::SetCornerRadius(int radius) {
-  bubble_border_->SetCornerRadius(radius);
+void BubbleFrameView::SetRoundedCorners(const gfx::RoundedCornersF& radii) {
+  bubble_border_->set_rounded_corners(radii);
   UpdateClientLayerCornerRadius();
+  SchedulePaint();
 }
 
-int BubbleFrameView::GetCornerRadius() const {
-  return bubble_border_ ? bubble_border_->corner_radius() : 0;
+gfx::RoundedCornersF BubbleFrameView::GetRoundedCorners() const {
+  return bubble_border_ ? bubble_border_->rounded_corners()
+                        : gfx::RoundedCornersF();
 }
 
 void BubbleFrameView::SetArrow(BubbleBorder::Arrow arrow) {
@@ -956,18 +969,23 @@
 }
 
 gfx::RoundedCornersF BubbleFrameView::GetClientCornerRadii() const {
-  DCHECK(bubble_border_);
-  const int radius = bubble_border_->corner_radius();
+  CHECK(bubble_border_);
+
+  const gfx::RoundedCornersF& radii = bubble_border_->rounded_corners();
   const gfx::Insets insets =
       GetClientInsetsForFrameWidth(GetContentsBounds().width());
 
   // Rounded corners do not need to be applied to the client view if the client
-  // view is sufficiently inset such that its unclipped bounds will not
+  // view is sufficiently inset such that its un-clipped bounds will not
   // intersect with the corners of the containing bubble frame view.
-  if ((insets.top() > radius && insets.bottom() > radius) ||
-      (insets.left() > radius && insets.right() > radius)) {
-    return gfx::RoundedCornersF();
-  }
+  bool round_upper_left_corner =
+      insets.top() <= radii.upper_left() || insets.left() <= radii.upper_left();
+  bool round_upper_right_corner = insets.top() <= radii.upper_right() ||
+                                  insets.right() <= radii.upper_right();
+  bool round_lower_left_corner = insets.bottom() <= radii.lower_left() ||
+                                 insets.left() <= radii.lower_left();
+  bool round_lower_right_corner = insets.bottom() <= radii.lower_right() ||
+                                  insets.right() <= radii.lower_right();
 
   // We want to clip the client view to a rounded rect that's consistent with
   // the bubble's rounded border. However, if there is a header, the top of the
@@ -975,10 +993,15 @@
   // a footer, the client view should be straight and flush with that. Therefore
   // we set the corner radii separately for top and bottom.
   gfx::RoundedCornersF corner_radii;
-  corner_radii.set_upper_left(header_view_ ? 0 : radius);
-  corner_radii.set_upper_right(header_view_ ? 0 : radius);
-  corner_radii.set_lower_left(footnote_container_ ? 0 : radius);
-  corner_radii.set_lower_right(footnote_container_ ? 0 : radius);
+  corner_radii.set_upper_left(
+      round_upper_left_corner && !header_view_ ? radii.upper_left() : 0);
+  corner_radii.set_upper_right(
+      round_upper_right_corner && !header_view_ ? radii.upper_right() : 0);
+  corner_radii.set_lower_left(
+      round_lower_left_corner && !footnote_container_ ? radii.lower_left() : 0);
+  corner_radii.set_lower_right(round_lower_right_corner && !footnote_container_
+                                   ? radii.lower_right()
+                                   : 0);
 
   return corner_radii;
 }
@@ -1276,7 +1299,7 @@
 ADD_PROPERTY_METADATA(gfx::Insets, FootnoteMargins)
 ADD_PROPERTY_METADATA(BubbleFrameView::PreferredArrowAdjustment,
                       PreferredArrowAdjustment)
-ADD_PROPERTY_METADATA(int, CornerRadius)
+ADD_PROPERTY_METADATA(gfx::RoundedCornersF, RoundedCorners)
 ADD_PROPERTY_METADATA(BubbleBorder::Arrow, Arrow)
 ADD_PROPERTY_METADATA(bool, DisplayVisibleArrow)
 ADD_PROPERTY_METADATA(SkColor, BackgroundColor, ui::metadata::SkColorConverter)
diff --git a/ui/views/bubble/bubble_frame_view.h b/ui/views/bubble/bubble_frame_view.h
index 27affb4..bfe82b3 100644
--- a/ui/views/bubble/bubble_frame_view.h
+++ b/ui/views/bubble/bubble_frame_view.h
@@ -180,8 +180,8 @@
   }
 
   // Set the corner radius of the bubble border.
-  void SetCornerRadius(int radius);
-  int GetCornerRadius() const;
+  void SetRoundedCorners(const gfx::RoundedCornersF& radii);
+  gfx::RoundedCornersF GetRoundedCorners() const;
 
   // Set the arrow of the bubble border.
   void SetArrow(BubbleBorder::Arrow arrow);
diff --git a/ui/views/bubble/footnote_container_view.cc b/ui/views/bubble/footnote_container_view.cc
index 2735b8bd..37b5376 100644
--- a/ui/views/bubble/footnote_container_view.cc
+++ b/ui/views/bubble/footnote_container_view.cc
@@ -4,81 +4,37 @@
 
 #include "ui/views/bubble/footnote_container_view.h"
 
+#include <algorithm>
 #include <memory>
 #include <utility>
 
-#include "cc/paint/paint_flags.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/color/color_id.h"
-#include "ui/color/color_provider.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/color_palette.h"
-#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/layout/box_layout.h"
 
 namespace views {
 
-namespace {
-
-// A solid color background where the bottom two corners are rounded.
-class HalfRoundedRectBackground : public Background {
- public:
-  HalfRoundedRectBackground(SkColor color, float radius) : radius_(radius) {
-    SetColor(color);
-  }
-
-  HalfRoundedRectBackground() = delete;
-  HalfRoundedRectBackground(const HalfRoundedRectBackground&) = delete;
-  HalfRoundedRectBackground& operator=(const HalfRoundedRectBackground&) =
-      delete;
-
-  ~HalfRoundedRectBackground() override = default;
-
-  // Background:
-  void Paint(gfx::Canvas* canvas, View* view) const override {
-    cc::PaintFlags flags;
-    flags.setAntiAlias(true);
-    flags.setStyle(cc::PaintFlags::kFill_Style);
-    flags.setColor(color().ResolveToSkColor(view->GetColorProvider()));
-    // Draw a rounded rect that spills outside of the clipping area, so that the
-    // rounded corners only show in the bottom 2 corners.
-    gfx::RectF spilling_rect(view->GetLocalBounds());
-    spilling_rect.set_y(spilling_rect.x() - radius_);
-    spilling_rect.set_height(spilling_rect.height() + radius_);
-    canvas->DrawRoundRect(spilling_rect, radius_, flags);
-  }
-
- private:
-  float radius_;
-};
-
-}  // namespace
-
 FootnoteContainerView::FootnoteContainerView(const gfx::Insets& margins,
                                              std::unique_ptr<View> child_view,
-                                             float corner_radius)
-    : corner_radius_(corner_radius) {
+                                             float lower_left_radius,
+                                             float lower_right_radius) {
   SetLayoutManager(std::make_unique<BoxLayout>(
       BoxLayout::Orientation::kVertical, margins, 0));
   auto* child_view_ptr = AddChildView(std::move(child_view));
   SetVisible(child_view_ptr->GetVisible());
+  SetBorder(CreateSolidSidedBorder(gfx::Insets::TLBR(1, 0, 0, 0),
+                                   ui::kColorBubbleFooterBorder));
+  SetRoundedBackground(lower_left_radius, lower_right_radius);
 }
 
 FootnoteContainerView::~FootnoteContainerView() = default;
 
-void FootnoteContainerView::SetCornerRadius(float corner_radius) {
-  corner_radius_ = corner_radius;
-  if (GetWidget()) {
-    ResetBackground();
-  }
-}
-
-void FootnoteContainerView::OnThemeChanged() {
-  View::OnThemeChanged();
-  ResetBorder();
-  ResetBackground();
+void FootnoteContainerView::SetRoundedCorners(float lower_left_radius,
+                                              float lower_right_radius) {
+  SetRoundedBackground(lower_left_radius, lower_right_radius);
 }
 
 void FootnoteContainerView::ChildVisibilityChanged(View* child) {
@@ -86,23 +42,11 @@
   SetVisible(child->GetVisible());
 }
 
-void FootnoteContainerView::ResetBackground() {
-  if (!GetWidget()) {
-    return;
-  }
-  SkColor background_color =
-      GetColorProvider()->GetColor(ui::kColorBubbleFooterBackground);
-  SetBackground(std::make_unique<HalfRoundedRectBackground>(background_color,
-                                                            corner_radius_));
-}
-
-void FootnoteContainerView::ResetBorder() {
-  if (!GetWidget()) {
-    return;
-  }
-  SetBorder(CreateSolidSidedBorder(
-      gfx::Insets::TLBR(1, 0, 0, 0),
-      GetColorProvider()->GetColor(ui::kColorBubbleFooterBorder)));
+void FootnoteContainerView::SetRoundedBackground(float lower_left_radius,
+                                                 float lower_right_radius) {
+  SetBackground(CreateRoundedRectBackground(
+      ui::kColorBubbleFooterBackground,
+      gfx::RoundedCornersF(0, 0, lower_left_radius, lower_right_radius)));
 }
 
 BEGIN_METADATA(FootnoteContainerView)
diff --git a/ui/views/bubble/footnote_container_view.h b/ui/views/bubble/footnote_container_view.h
index 7084aca7..6c009ed4 100644
--- a/ui/views/bubble/footnote_container_view.h
+++ b/ui/views/bubble/footnote_container_view.h
@@ -21,24 +21,20 @@
 
   FootnoteContainerView(const gfx::Insets& margins,
                         std::unique_ptr<View> child_view,
-                        float corner_radius);
+                        float lower_left_radius,
+                        float lower_right_radius);
 
   FootnoteContainerView(const FootnoteContainerView&) = delete;
   FootnoteContainerView& operator=(const FootnoteContainerView&) = delete;
 
   ~FootnoteContainerView() override;
 
-  void SetCornerRadius(float corner_radius);
+  void SetRoundedCorners(float lower_left_radius, float lower_right_radius);
 
-  // View:
-  void OnThemeChanged() override;
   void ChildVisibilityChanged(View* child) override;
 
  private:
-  void ResetBackground();
-  void ResetBorder();
-
-  float corner_radius_;
+  void SetRoundedBackground(float lower_left_radius, float lower_right_radius);
 };
 
 }  // namespace views
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc
index 75433da..a2fe435 100644
--- a/ui/views/controls/menu/menu_item_view.cc
+++ b/ui/views/controls/menu/menu_item_view.cc
@@ -37,6 +37,10 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
+#include "ui/gfx/geometry/rrect_f.h"
+#include "ui/gfx/geometry/skia_conversions.h"
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/paint_vector_icon.h"
@@ -857,9 +861,11 @@
   SchedulePaint();
 }
 
-void MenuItemView::SetCornerRadius(int radius) {
+void MenuItemView::SetBottomCornersRadius(int lower_left_radius,
+                                          int lower_right_radius) {
   DCHECK_EQ(Type::kHighlighted, type_);
-  corner_radius_ = radius;
+  bottom_rounded_corners_ =
+      gfx::RoundedCornersF(0, 0, lower_left_radius, lower_right_radius);
   invalidate_dimensions();  // Triggers preferred size recalculation.
 }
 
@@ -1150,14 +1156,19 @@
     flags.setAntiAlias(true);
     flags.setStyle(cc::PaintFlags::kFill_Style);
     flags.setColor(color);
-    // Draw a rounded rect that spills outside of the clipping area, so that the
-    // rounded corners only show in the bottom 2 corners. Note that
-    // |corner_radius_| should only be set when the highlighted item is at the
-    // end of the menu.
-    gfx::RectF spilling_rect(GetLocalBounds());
-    spilling_rect.set_y(spilling_rect.y() - corner_radius_);
-    spilling_rect.set_height(spilling_rect.height() + corner_radius_);
-    canvas->DrawRoundRect(spilling_rect, corner_radius_, flags);
+
+    // Note that `bottom_rounded_corners_` should only be set when the
+    // highlighted item is at the bottom of the menu.
+    SkRRect rounded_rect;
+    SkVector radii[4]{{0, 0},
+                      {0, 0},
+                      {bottom_rounded_corners_.lower_right(),
+                       bottom_rounded_corners_.lower_right()},
+                      {bottom_rounded_corners_.lower_left(),
+                       bottom_rounded_corners_.lower_left()}};
+    rounded_rect.setRectRadii(gfx::RectFToSkRect(gfx::RectF(GetLocalBounds())),
+                              radii);
+    canvas->sk_canvas()->drawRRect(rounded_rect, flags);
   } else if (paint_as_selected) {
     gfx::Rect item_bounds = GetLocalBounds();
     if (type_ == Type::kActionableSubMenu) {
diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h
index a36ce09..7de07dc 100644
--- a/ui/views/controls/menu/menu_item_view.h
+++ b/ui/views/controls/menu/menu_item_view.h
@@ -22,6 +22,7 @@
 #include "ui/base/models/menu_separator_types.h"
 #include "ui/base/themed_vector_icon.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/strings/grit/ui_strings.h"
@@ -395,10 +396,10 @@
   // there's no way to unset it for this MenuItemView!
   void SetForcedVisualSelection(bool selected);
 
-  // For items of type HIGHLIGHTED only: sets the radius of the item's
+  // For items of type HIGHLIGHTED only: sets the corner radius of the item's
   // background. This makes the menu item's background fit its container's
-  // border radius, if they are both the same value.
-  void SetCornerRadius(int radius);
+  // border radii, if they are both the same value.
+  void SetBottomCornersRadius(int lower_left_radius, int lower_right_radius);
 
   // Sets or removes the expanded/collapsed state of the menu item if it's a
   // submenu.
@@ -677,8 +678,8 @@
 
   std::optional<int> vertical_margin_;
 
-  // Corner radius in pixels, for HIGHLIGHTED items placed at the end of a menu.
-  int corner_radius_ = 0;
+  // Corners radii in pixels, for HIGHLIGHTED items placed at the end of a menu.
+  gfx::RoundedCornersF bottom_rounded_corners_;
 
   // |menu_position_| is the requested position with respect to the bounds.
   // |actual_menu_position_| is used by the controller to cache the
diff --git a/ui/views/controls/menu/menu_scroll_view_container.cc b/ui/views/controls/menu/menu_scroll_view_container.cc
index 32ad7370..63f5dad 100644
--- a/ui/views/controls/menu/menu_scroll_view_container.cc
+++ b/ui/views/controls/menu/menu_scroll_view_container.cc
@@ -10,6 +10,7 @@
 #include <utility>
 #include <variant>
 
+#include "base/check.h"
 #include "base/functional/callback_helpers.h"
 #include "base/memory/raw_ptr.h"
 #include "build/build_config.h"
@@ -315,7 +316,7 @@
   // The controller could be null during context menu being closed.
   auto* menu_controller = content_view_->GetMenuItem()->GetMenuController();
   if (!menu_controller) {
-    return gfx::RoundedCornersF(corner_radius_);
+    return background_rounded_corners_;
   }
 
   std::optional<gfx::RoundedCornersF> rounded_corners =
@@ -324,7 +325,7 @@
     return rounded_corners.value();
   }
 
-  return gfx::RoundedCornersF(corner_radius_);
+  return background_rounded_corners_;
 }
 
 gfx::Insets MenuScrollViewContainer::GetInsets() const {
@@ -389,7 +390,11 @@
 
   MenuItemView* const footnote = GetFootnote();
   if (footnote) {
-    footnote->SetCornerRadius(any_scroll_button_visible ? 0 : corner_radius_);
+    footnote->SetBottomCornersRadius(
+        any_scroll_button_visible ? 0
+                                  : background_rounded_corners_.lower_left(),
+        any_scroll_button_visible ? 0
+                                  : background_rounded_corners_.lower_right());
   }
 }
 
@@ -419,14 +424,14 @@
 
 void MenuScrollViewContainer::CreateDefaultBorder() {
   DCHECK_EQ(arrow_, BubbleBorder::NONE);
-  corner_radius_ = GetCornerRadius();
+  int corner_radius = GetCornerRadius();
   outside_border_insets_ = {};
 
   const auto& menu_config = MenuConfig::instance();
   const int vertical_inset =
-      corner_radius_ ? menu_config.rounded_menu_vertical_border_size.value_or(
-                           corner_radius_)
-                     : menu_config.nonrounded_menu_vertical_border_size;
+      corner_radius ? menu_config.rounded_menu_vertical_border_size.value_or(
+                          corner_radius)
+                    : menu_config.nonrounded_menu_vertical_border_size;
   const int horizontal_inset = menu_config.menu_horizontal_border_size;
   const bool has_footnote = !!GetFootnote();
   auto insets =
@@ -446,7 +451,7 @@
   }
 
   SetBackground(
-      CreateRoundedRectBackground(ui::kColorMenuBackground, corner_radius_,
+      CreateRoundedRectBackground(ui::kColorMenuBackground, corner_radius,
                                   views::RoundRectPainter::kBorderWidth));
 
   const auto* const color_provider = GetColorProvider();
@@ -457,8 +462,9 @@
     insets.set_bottom(views::RoundRectPainter::kBorderWidth);
   }
   SetBorder(views::CreateBorderPainter(
-      std::make_unique<views::RoundRectPainter>(color, corner_radius_),
-      insets));
+      std::make_unique<views::RoundRectPainter>(color, corner_radius), insets));
+
+  background_rounded_corners_ = gfx::RoundedCornersF(corner_radius);
 }
 
 void MenuScrollViewContainer::CreateBubbleBorder() {
@@ -493,7 +499,7 @@
         menu_controller->rounded_corners().has_value()) {
       bubble_border->set_rounded_corners(GetRoundedCorners());
     } else {
-      bubble_border->SetCornerRadius(border_radius);
+      bubble_border->set_rounded_corners(gfx::RoundedCornersF(border_radius));
     }
   }
 
@@ -516,7 +522,8 @@
     additional_insets_.set_bottom(0);
   }
 
-  corner_radius_ = bubble_border->corner_radius();
+  background_rounded_corners_ = bubble_border->rounded_corners();
+
   // If the menu uses Ash system UI layout, use `background_view` to build a
   // blurry background with highlight border. Otherwise, use default
   // BubbleBackground.
@@ -528,7 +535,7 @@
         CreateEmptyBorder(std::exchange(additional_insets_, {})));
 
     background_view_->SetBackground(
-        CreateRoundedRectBackground(id, corner_radius_));
+        CreateRoundedRectBackground(id, background_rounded_corners_));
     background_view_->layer()->SetRoundedCornerRadius(GetRoundedCorners());
 
 #if BUILDFLAG(IS_CHROMEOS)
diff --git a/ui/views/controls/menu/menu_scroll_view_container.h b/ui/views/controls/menu/menu_scroll_view_container.h
index ebf7cb98..75a9a2e3 100644
--- a/ui/views/controls/menu/menu_scroll_view_container.h
+++ b/ui/views/controls/menu/menu_scroll_view_container.h
@@ -104,8 +104,7 @@
   // If set the currently set border is a bubble border.
   BubbleBorder::Arrow arrow_ = BubbleBorder::NONE;
 
-  // Corner radius of the background.
-  int corner_radius_ = 0;
+  gfx::RoundedCornersF background_rounded_corners_;
 
   // The portion of GetInsets() that represent the region outside the border
   // (e.g. any shadows).
diff --git a/ui/views/view_unittest_mac.mm b/ui/views/view_unittest_mac.mm
index 6dbb1205..c979a0b 100644
--- a/ui/views/view_unittest_mac.mm
+++ b/ui/views/view_unittest_mac.mm
@@ -15,7 +15,7 @@
 @interface FakeSwipeEvent : NSEvent
 @property CGFloat deltaX;
 @property CGFloat deltaY;
-@property(assign) NSWindow* window;
+@property(strong) NSWindow* window;
 @property NSPoint locationInWindow;
 @property NSEventModifierFlags modifierFlags;
 @property NSTimeInterval timestamp;
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index 7b22be3..809e00e 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -22,6 +22,7 @@
 #include "ui/base/mojom/ui_base_types.mojom-shared.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/gfx/color_palette.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/bubble/bubble_border.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
@@ -351,7 +352,8 @@
   DialogDelegate* delegate = widget->widget_delegate()->AsDialogDelegate();
   if (delegate) {
     if (delegate->GetParams().round_corners) {
-      border->SetCornerRadius(delegate->GetCornerRadius());
+      border->set_rounded_corners(
+          gfx::RoundedCornersF(delegate->GetCornerRadius()));
     }
     frame->SetFootnoteView(delegate->DisownFootnoteView());
   }
diff --git a/v8 b/v8
index 3005b00..84b3ee9 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 3005b00df2a95266a853252e20e78f637adc5c2f
+Subproject commit 84b3ee97fdd8f2df1e477b0cc79b893707da21b1