diff --git a/DEPS b/DEPS
index 537569d..2a1a07be 100644
--- a/DEPS
+++ b/DEPS
@@ -308,7 +308,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': '5f3259bc268e83f385b3d235a66f8fbd380336f8',
+  'src_internal_revision': '926c416205c73ac88c808786ed9f9f1e659f6bfc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
@@ -316,7 +316,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '3ec501ccaac5680794d8f70459fde1e7c120abe6',
+  'v8_revision': '705aa9d3796d3e20193c302e1a32fa05ab4363dc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
@@ -400,7 +400,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': 'b2a86d09d5c4fa554a9a46cb1e94cf8ab52b2d0c',
+  'devtools_frontend_revision': '48f965098f72daf2d89b5ded468d3264299fb095',
   # 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.
@@ -1198,7 +1198,7 @@
       'packages': [
           {
               'package': 'chromium/chrome/android/orderfiles/arm',
-              'version': '0ZqeWPme_NoWICTn3mP69dUMzNEc8SQg2ncYTUGhuqsC',
+              'version': 'cDOrfG8L66PDDvYfQVMErlbd3NINiQ6d3z2O6s5ihpUC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1209,7 +1209,7 @@
       'packages': [
           {
               'package': 'chromium/chrome/android/orderfiles/arm64',
-              'version': 'jmCOxHNAPV4mccholEiarvq2ltMjET_NGNVgeqkUw30C',
+              'version': 'RqiXQV1FtPrr3h-a-JfJ6FFGJj6daDJM58um0L9zEtIC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1220,7 +1220,7 @@
       'packages': [
           {
               'package': 'chromium/android_webview/tools/orderfiles/arm',
-              'version': 'ItzOXXvcPGAdWaR_lJus3nth8LCuRwlyouye2dX-btoC',
+              'version': '3-7D9jCzhIvN_rL2RLVPkviI4Fn9KiR1utOLwk2PC6sC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1231,7 +1231,7 @@
       'packages': [
           {
               'package': 'chromium/android_webview/tools/orderfiles/arm64',
-              'version': 'LfyrmvIF5uUxUhirLR85g3ddrr5NhRYcvhBlmfQmhHUC',
+              'version': '5QSFarUBfi5vcNoCCTisa4w0aHAjVvVq6WDAA2URqNcC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1403,7 +1403,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/updater/chromium_linux64',
-          'version': 'version:2@1592001',
+          'version': 'version:2@1593011',
         },
       ],
   },
@@ -1536,7 +1536,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/enterprise_companion/chromium_linux64',
-          'version': 'dgdBQfA4t9gSHP-5gcxOXXmtDZ-1ni4w0rzM93QgRkIC',
+          'version': 's2DtB9zKh7jCfWVKXZf18vXBaqdjGZwpdo9VEUXsCSAC',
         },
       ],
   },
@@ -1547,7 +1547,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/enterprise_companion/chromium_mac_amd64',
-          'version': 'sX1BmmBiwsr1e0dhXY707jUkw-3HsdcCsrQELhIsXPAC',
+          'version': 'B0tvS8a1PWD8hFE_XPevWzHTSzHybEJjf8x8ukkNUtAC',
         },
       ],
   },
@@ -1558,7 +1558,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/enterprise_companion/chromium_mac_arm64',
-          'version': 'H_hSnIcVieZKCu4tWZeuQkJskSRFhuox2d2UvU5Roe0C',
+          'version': 'nQdNPlrkBvct2Oz-ni-HKiAAVLzCnB0X_tR8H-bYsgwC',
         },
       ],
   },
@@ -1569,7 +1569,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/enterprise_companion/chromium_win_x86',
-          'version': 'ucyYSu0iSSw6C1FUrErObtSuijTN7a2xPFk-Jq2TpzMC',
+          'version': 'XGIfEvTTy0qwP3uiXnJjK0W2y9JHpRYgiC5e312ssu8C',
         },
       ],
   },
@@ -1580,7 +1580,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/enterprise_companion/chromium_win_x86_64',
-          'version': '9cMPlsepjcQD5_7wiEKc7YE4DVV1I6IasNXKAhd6-eoC',
+          'version': '9sTESi5emsEA5nTDJaItT5Yd4S_WgsGoPdgsa9Pu_IUC',
         },
       ],
   },
@@ -1616,7 +1616,7 @@
     'packages': [
       {
         'package': 'chromium/chrome/test/data/variations/cipd',
-        'version': '3pIEvnJgjs4yPcLeJfcdPSNjvVVzfGu1FMa-TaNlVsoC',
+        'version': 'yjj5vQuYGnuDDrAQQx3iBzCUVs4GbZS0uDHrA5BlLtcC',
       },
     ],
     'condition': 'non_git_source',
@@ -2617,7 +2617,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '08b509bdad26c91f5f34d9ff97c9362a111f3a72',
+    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '3a764e6f6339ae06c19540682dad4a1a9021f072',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2958,16 +2958,16 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@d61243d1b0837231283e456a908637ead7917dd9',
-  'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@d1f52c8993a501bd52d4fbd044bfeb9ecdceb9f4',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@c29f9df0edc12f925253a95526823c15ed249d6a',
+  'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@959ff4a0dba11111c5c999f1235c798431475322',
   'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3',
   'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@f88a2d766840fc825af1fc065977953ba1fa4a91',
-  'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@47eef7be333fe709795fd695ba4f8e58c70a0cf0',
+  'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@6e9d60d80905f561858c2b1234bbb82bb529c044',
   'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@74d8a6cb930c68ef617b202c3ff3c59d919e086b',
   'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@67c09a1a2ebcf0c7179700ebaf2bd3f58f2d4248',
   'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@59f963ce1b1d16cc92137a241a0fe98d637d21f4',
   'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@56773a2b578bb5831a1d8eb1927cd9ea92dddc65',
-  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@562af80edf57126f333036a707da8cb2f9c1522e',
+  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@4fc964ebd2fcad5d65372512bd483767f08dc345',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'cb0597213b0fcb999caa9ed08c2f88dc45eb7d50',
diff --git a/android_webview/browser/aw_settings.cc b/android_webview/browser/aw_settings.cc
index efa92a8..6d47627b 100644
--- a/android_webview/browser/aw_settings.cc
+++ b/android_webview/browser/aw_settings.cc
@@ -212,6 +212,7 @@
   UpdateBackForwardCacheEnabledLocked(env, obj);
   UpdateBackForwardCacheSettingsTimeoutLocked(env, obj);
   UpdateBackForwardCacheSettingsMaxPagesInCacheLocked(env, obj);
+  UpdateBackForwardCacheSettingsKeepForwardEntriesLocked(env, obj);
   UpdateGeolocationEnabledLocked(env, obj);
 }
 
@@ -544,6 +545,22 @@
   back_forward_cache_max_pages_in_cache_ = max_pages_in_cache;
 }
 
+void AwSettings::UpdateBackForwardCacheSettingsKeepForwardEntriesLocked(
+    JNIEnv* env,
+    const JavaRef<jobject>& obj) {
+  bool keep_forward_entries =
+      Java_AwSettings_getBackForwardCacheSettingsKeepForwardEntries(env, obj);
+  if (web_contents()) {
+    if (keep_forward_entries != back_forward_cache_keep_forward_entries_) {
+      web_contents()
+          ->GetController()
+          .GetBackForwardCache()
+          .SetEmbedderSuppliedCacheForwardEntriesAllowed(keep_forward_entries);
+    }
+  }
+  back_forward_cache_keep_forward_entries_ = keep_forward_entries;
+}
+
 void AwSettings::UpdateGeolocationEnabledLocked(JNIEnv* env,
                                                 const JavaRef<jobject>& obj) {
   if (!web_contents()) {
diff --git a/android_webview/browser/aw_settings.h b/android_webview/browser/aw_settings.h
index 5635ab7..41dccc3 100644
--- a/android_webview/browser/aw_settings.h
+++ b/android_webview/browser/aw_settings.h
@@ -135,6 +135,9 @@
   void UpdateBackForwardCacheSettingsMaxPagesInCacheLocked(
       JNIEnv* env,
       const base::android::JavaRef<jobject>& obj);
+  void UpdateBackForwardCacheSettingsKeepForwardEntriesLocked(
+      JNIEnv* env,
+      const base::android::JavaRef<jobject>& obj);
   void UpdateGeolocationEnabledLocked(
       JNIEnv* env,
       const base::android::JavaRef<jobject>& obj);
@@ -188,6 +191,7 @@
   bool bfcache_enabled_in_java_settings_{false};
   int back_forward_cache_timeout_in_seconds_{0};
   int back_forward_cache_max_pages_in_cache_{0};
+  bool back_forward_cache_keep_forward_entries_{true};
   bool geolocation_enabled_{false};
 
   // Whether the settings that would affect the initial page scale is set to a
diff --git a/android_webview/common/aw_feature_map.cc b/android_webview/common/aw_feature_map.cc
index 34df941..6cfa9e49 100644
--- a/android_webview/common/aw_feature_map.cc
+++ b/android_webview/common/aw_feature_map.cc
@@ -44,6 +44,7 @@
     &features::kWebViewCacheBoundaryInterfaceMethods,
     &features::kWebViewCacheSizeLimitDerivedFromAppCacheQuota,
     &features::kWebViewConnectToComponentProviderInBackground,
+    &features::kWebViewContentRestrictionSupport,
     &features::kWebViewDeferStartupGmsCalls,
     &features::kWebViewEarlyStartupTracing,
     &features::kWebViewEarlyTracingInit,
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentRestrictionManagerBridge.java b/android_webview/java/src/org/chromium/android_webview/AwContentRestrictionManagerBridge.java
index 6218d55..72d5a038 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContentRestrictionManagerBridge.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContentRestrictionManagerBridge.java
@@ -3,15 +3,23 @@
 // found in the LICENSE file.
 package org.chromium.android_webview;
 
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
+import org.jni_zero.JniType;
 
 import org.chromium.android_webview.common.AwFeatureMap;
 import org.chromium.android_webview.common.AwFeatures;
 import org.chromium.base.AconfigFlaggedApiDelegate;
+import org.chromium.base.ContextUtils;
+import org.chromium.base.JniOnceCallback;
 import org.chromium.base.Log;
+import org.chromium.base.Promise;
 import org.chromium.base.ServiceLoaderUtil;
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 
 /**
  * JNI bridge for tapping into the ContentRestrictionManager system service in order to enforce
@@ -22,6 +30,13 @@
 public class AwContentRestrictionManagerBridge {
     private static final String TAG = "AwCRMBridge";
 
+    private static @Nullable Uri parseUrl(@Nullable String url) {
+        if (url == null) {
+            return null;
+        }
+        return Uri.parse(url);
+    }
+
     @CalledByNative
     public static boolean isContentRestrictionEnabled() {
         if (!AwFeatureMap.isEnabled(AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT)) {
@@ -38,4 +53,42 @@
         }
         return delegate.isContentRestrictionEnabled();
     }
+
+    @CalledByNative
+    public static void requestContentClassification(
+            @JniType("std::string") String url,
+            @JniType("std::string") String mimeType,
+            JniOnceCallback<Boolean> callback) {
+        Uri uri = parseUrl(url);
+        if (uri == null) {
+            callback.onResult(false);
+            return;
+        }
+        AconfigFlaggedApiDelegate delegate =
+                ServiceLoaderUtil.maybeCreate(AconfigFlaggedApiDelegate.class);
+        if (delegate == null) {
+            Log.w(TAG, "Unable to retrieve the AconfigFlaggedApiDelegate instance.");
+            callback.onResult(false);
+            return;
+        }
+
+        // TODO(crbug.com/481113476): Also process the request body.
+        ParcelFileDescriptor requestBody = null;
+        Promise<Boolean> promise =
+                delegate.requestContentRestrictionClassification(
+                        uri,
+                        requestBody,
+                        mimeType,
+                        ContextUtils.getApplicationContext().getMainExecutor());
+        promise.then(
+                (Boolean isAllowed) -> {
+                    callback.onResult(isAllowed);
+                },
+                (@Nullable Exception error) -> {
+                    if (error != null) {
+                        Log.w(TAG, "Failed to classify content", error);
+                    }
+                    callback.onResult(false);
+                });
+    }
 }
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 8c715428..0b6a10a 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwSettings.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
@@ -216,6 +216,7 @@
 
     private long mBackForwardCacheTimeoutInSeconds;
     private int mBackForwardCacheMaxPagesInCache;
+    private boolean mBackForwardCacheKeepForwardEntries;
 
     private boolean mCssHexAlphaColorEnabled;
     private boolean mScrollTopLeftInteropEnabled;
@@ -373,6 +374,12 @@
                     AwSettings.this::updateBackForwardCacheSettingsMaxPagesInCacheOnUiThreadLocked);
         }
 
+        void updateBackForwardCacheSettingsKeepForwardEntries() {
+            runOnUiThreadBlockingAndLocked(
+                    AwSettings.this
+                            ::updateBackForwardCacheSettingsKeepForwardEntriesOnUiThreadLocked);
+        }
+
         void updateGeolocationEnabled() {
             runOnUiThreadBlockingAndLocked(
                     AwSettings.this::updateGeolocationEnabledOnUiThreadLocked);
@@ -1897,6 +1904,25 @@
         }
     }
 
+    /**
+     * Sets whether to keep forward entries when the user navigates back. Disabling this saves
+     * memory by discarding unreachable pages.
+     */
+    public void setBackForwardCacheKeepForwardEntries(boolean keepForwardEntries) {
+        if (TRACE) {
+            Log.i(TAG, "setBackForwardCacheKeepForwardEntries=" + keepForwardEntries);
+        }
+        // Setting BackForwardCacheSettings implicitly enables BFCache as well.
+        setBackForwardCacheEnabled(true);
+        synchronized (mAwSettingsLock) {
+            if (mBackForwardCacheKeepForwardEntries == keepForwardEntries) {
+                return;
+            }
+            mBackForwardCacheKeepForwardEntries = keepForwardEntries;
+            mEventHandler.updateBackForwardCacheSettingsKeepForwardEntries();
+        }
+    }
+
     @CalledByNative
     public long getBackForwardCacheSettingsTimeout() {
         synchronized (mAwSettingsLock) {
@@ -1913,6 +1939,14 @@
         }
     }
 
+    @CalledByNative
+    public boolean getBackForwardCacheSettingsKeepForwardEntries() {
+        synchronized (mAwSettingsLock) {
+            assert Thread.holdsLock(mAwSettingsLock);
+            return mBackForwardCacheKeepForwardEntries;
+        }
+    }
+
     @ForceDarkMode
     public int getForceDarkMode() {
         synchronized (mAwSettingsLock) {
@@ -2238,6 +2272,15 @@
                         mNativeAwSettings, AwSettings.this);
     }
 
+    private void updateBackForwardCacheSettingsKeepForwardEntriesOnUiThreadLocked() {
+        assert mEventHandler.mHandler != null;
+        ThreadUtils.assertOnUiThread();
+        if (mNativeAwSettings == 0) return;
+        AwSettingsJni.get()
+                .updateBackForwardCacheSettingsKeepForwardEntriesLocked(
+                        mNativeAwSettings, AwSettings.this);
+    }
+
     private void updateGeolocationEnabledOnUiThreadLocked() {
         assert mEventHandler.mHandler != null;
         ThreadUtils.assertOnUiThread();
@@ -2401,6 +2444,9 @@
         void updateBackForwardCacheSettingsMaxPagesInCacheLocked(
                 long nativeAwSettings, AwSettings caller);
 
+        void updateBackForwardCacheSettingsKeepForwardEntriesLocked(
+                long nativeAwSettings, AwSettings caller);
+
         boolean isForceDarkApplied(long nativeAwSettings, AwSettings caller);
 
         boolean prefersDarkFromTheme(long nativeAwSettings, AwSettings caller);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwBackForwardCacheTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwBackForwardCacheTest.java
index 86194a3..5c0fd7a 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwBackForwardCacheTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwBackForwardCacheTest.java
@@ -135,6 +135,31 @@
                 true, pageFullyLoadedFuture.get(SCALED_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
     }
 
+    private void navigateForwardToUrl(String url) throws Throwable {
+        // Create a new future to avoid the future set in the initial load.
+        SettableFuture<Boolean> pageFullyLoadedFuture = SettableFuture.create();
+        mLoadedNotifier.setFuture(pageFullyLoadedFuture);
+        // Traditionally we use onPageFinishedHelper which is no longer
+        // valid with BFCache working.
+        // The onPageFinishedHelper is called in `DidFinishLoad` callback
+        // in the web contents observer. If the page is restored from the
+        // BFCache, this function will not get called since the onload event
+        // is already fired when the page was navigated into for the first time.
+        // We use onPageStartedHelper instead. This function correspond to
+        // `didFinishNavigationInPrimaryMainFrame`.
+        OnPageStartedHelper startHelper = mContentsClient.getOnPageStartedHelper();
+        int originalCallCount = startHelper.getCallCount();
+        HistoryUtils.goForwardSync(
+                InstrumentationRegistry.getInstrumentation(),
+                mAwContents.getWebContents(),
+                startHelper);
+        Assert.assertEquals(startHelper.getUrl(), url);
+        Assert.assertEquals(startHelper.getCallCount(), originalCallCount + 1);
+        // Wait for the page to be fully loaded
+        Assert.assertEquals(
+                true, pageFullyLoadedFuture.get(SCALED_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+    }
+
     private void navigateForwardAndBack() throws Throwable {
         navigateForward();
         navigateBack();
@@ -272,6 +297,56 @@
     @Test
     @LargeTest
     @Feature({"AndroidWebView"})
+    public void testBFCacheWithKeepForwardEntries() throws Exception, Throwable {
+        mAwContents.getSettings().setBackForwardCacheEnabled(true);
+
+        mAwContents.getSettings().setBackForwardCacheMaxPagesInCache(3);
+        mAwContents.getSettings().setBackForwardCacheTimeoutInSeconds(600);
+        mAwContents.getSettings().setBackForwardCacheKeepForwardEntries(true);
+
+        // Navigate to the initial page.
+        mActivityTestRule.loadUrlSync(
+                mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
+
+        // Navigate forward and back. The page should be restored from BFCache.
+        navigateForwardAndBack();
+        Assert.assertEquals("\"null\"", getNotRestoredReasons());
+        Assert.assertTrue(isPageShowPersisted());
+
+        // Navigate forward again to mForwardUrl. The page should be restored from BFCache.
+        navigateForwardToUrl(mForwardUrl);
+        Assert.assertEquals("\"null\"", getNotRestoredReasons());
+        Assert.assertTrue(isPageShowPersisted());
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"AndroidWebView"})
+    public void testBFCacheWithKeepForwardEntriesDisabled() throws Exception, Throwable {
+        mAwContents.getSettings().setBackForwardCacheEnabled(true);
+
+        mAwContents.getSettings().setBackForwardCacheMaxPagesInCache(3);
+        mAwContents.getSettings().setBackForwardCacheTimeoutInSeconds(600);
+        mAwContents.getSettings().setBackForwardCacheKeepForwardEntries(false);
+
+        // Navigate to the initial page.
+        mActivityTestRule.loadUrlSync(
+                mAwContents, mContentsClient.getOnPageFinishedHelper(), mInitialUrl);
+
+        // Navigate forward and back. The page should be restored from BFCache.
+        navigateForwardAndBack();
+        Assert.assertEquals("\"null\"", getNotRestoredReasons());
+        Assert.assertTrue(isPageShowPersisted());
+
+        // Navigate forward again to mForwardUrl. The page should NOT be restored from BFCache.
+        navigateForwardToUrl(mForwardUrl);
+        Assert.assertNotEquals("\"null\"", getNotRestoredReasons());
+        Assert.assertFalse(isPageShowPersisted());
+    }
+
+    @Test
+    @LargeTest
+    @Feature({"AndroidWebView"})
     @CommandLineFlags.Add({"disable-features=WebViewBackForwardCache"})
     public void testBackNavigationFollowsSettings() throws Exception, Throwable {
         mAwContents.getSettings().setBackForwardCacheEnabled(true);
diff --git a/android_webview/junit/src/org/chromium/android_webview/robolectric/AwContentRestrictionManagerBridgeTest.java b/android_webview/junit/src/org/chromium/android_webview/robolectric/AwContentRestrictionManagerBridgeTest.java
index 6df596be..0b2df2f081 100644
--- a/android_webview/junit/src/org/chromium/android_webview/robolectric/AwContentRestrictionManagerBridgeTest.java
+++ b/android_webview/junit/src/org/chromium/android_webview/robolectric/AwContentRestrictionManagerBridgeTest.java
@@ -20,10 +20,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLooper;
 
 import org.chromium.android_webview.AwContentRestrictionManagerBridge;
 import org.chromium.android_webview.ManifestMetadataUtil;
@@ -31,6 +33,8 @@
 import org.chromium.android_webview.test.util.ManifestMetadataMockApplicationContext;
 import org.chromium.base.AconfigFlaggedApiDelegate;
 import org.chromium.base.ContextUtils;
+import org.chromium.base.JniOnceCallback;
+import org.chromium.base.Promise;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Features.DisableFeatures;
@@ -47,12 +51,26 @@
             "android.webkit.WebView.EnableContentRestriction";
     private static final String METADATA_HOLDER_SERVICE_NAME =
             "android.webkit.MetaDataHolderService";
+    private static final String TEST_URL = "https://example.com";
+    private static final String TEST_MIME_TYPE = "text/html";
 
     private ManifestMetadataMockApplicationContext mContext;
     private ComponentName mMetadataServiceName;
+    private Boolean mCallbackResult;
+    private final JniOnceCallback<Boolean> mMockCallback =
+            new JniOnceCallback<Boolean>() {
+                @Override
+                public void onResult(Boolean result) {
+                    mCallbackResult = result;
+                }
+
+                @Override
+                public void destroy() {}
+            };
 
     @Before
     public void setUp() {
+        mCallbackResult = null;
         mContext = new ManifestMetadataMockApplicationContext(RuntimeEnvironment.application);
         mMetadataServiceName = new ComponentName(mContext, METADATA_HOLDER_SERVICE_NAME);
         ContextUtils.initApplicationContextForTests(mContext);
@@ -99,4 +117,60 @@
         Assert.assertFalse(AwContentRestrictionManagerBridge.isContentRestrictionEnabled());
         verifyNoInteractions(mFlaggedApiDelegate);
     }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT})
+    public void testRequestContentClassification_invalidUrl() {
+        AwContentRestrictionManagerBridge.requestContentClassification(
+                /* url= */ null, TEST_MIME_TYPE, mMockCallback);
+        Assert.assertEquals(false, mCallbackResult);
+        verifyNoInteractions(mFlaggedApiDelegate);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT})
+    public void testRequestContentClassification_delegateMissing() {
+        AconfigFlaggedApiDelegate.setInstanceForTesting(null);
+        AwContentRestrictionManagerBridge.requestContentClassification(
+                TEST_URL, TEST_MIME_TYPE, mMockCallback);
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        Assert.assertEquals(false, mCallbackResult);
+        verifyNoInteractions(mFlaggedApiDelegate);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT})
+    public void testRequestContentClassification_allowed() {
+        Promise<Boolean> promise = new Promise<>();
+        when(mFlaggedApiDelegate.requestContentRestrictionClassification(
+                        Mockito.any(), Mockito.eq(null), Mockito.eq(TEST_MIME_TYPE), Mockito.any()))
+                .thenReturn(promise);
+        AwContentRestrictionManagerBridge.requestContentClassification(
+                TEST_URL, TEST_MIME_TYPE, mMockCallback);
+        promise.fulfill(true);
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        Assert.assertEquals(true, mCallbackResult);
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"AndroidWebView"})
+    @EnableFeatures({AwFeatures.WEBVIEW_CONTENT_RESTRICTION_SUPPORT})
+    public void testRequestContentClassification_blocked() {
+        Promise<Boolean> promise = new Promise<>();
+        when(mFlaggedApiDelegate.requestContentRestrictionClassification(
+                        Mockito.any(), Mockito.eq(null), Mockito.eq(TEST_MIME_TYPE), Mockito.any()))
+                .thenReturn(promise);
+        AwContentRestrictionManagerBridge.requestContentClassification(
+                TEST_URL, TEST_MIME_TYPE, mMockCallback);
+        promise.fulfill(false);
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        Assert.assertEquals(false, mCallbackResult);
+    }
 }
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 c9c6263c..e9ba12af 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
@@ -171,7 +171,12 @@
 
     void setBackForwardCacheSettingsMaxPagesInCache(int pagesInCache);
 
+    void setBackForwardCacheSettingsKeepForwardEntries(
+            boolean keepForwardEntries);
+
     long getBackForwardCacheSettingsTimeout();
 
     int getBackForwardCacheSettingsMaxPagesInCache();
+
+    boolean getBackForwardCacheSettingsKeepForwardEntries();
 }
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 35486ed..ad7928c 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
@@ -430,6 +430,10 @@
     // V2 was deleted as it didn't get released and we made a major type change in V3.
     public static final String BACK_FORWARD_CACHE_SETTINGS_V3 = "BACK_FORWARD_CACHE_SETTINGS_V3";
 
+    // BackForwardCacheSettings.setKeepForwardEntries
+    // BackForwardCacheSettings.getKeepForwardEntries
+    public static final String BACK_FORWARD_CACHE_SETTINGS_V4 = "BACK_FORWARD_CACHE_SETTINGS_V4";
+
     // Profile.preconnect
     public static final String PRECONNECT = "PRECONNECT";
 
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 4a3946d..84b6d79 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
@@ -596,6 +596,16 @@
     }
 
     @Override
+    public void setBackForwardCacheSettingsKeepForwardEntries(boolean keepForwardEntries) {
+        try (TraceEvent ignored =
+                TraceEvent.scoped(
+                        "WebView.APICall.AndroidX.BACK_FORWARD_CACHE_SETTINGS_SET_KEEP_FORWARD_ENTRIES")) {
+            recordApiCall(ApiCall.BACK_FORWARD_CACHE_SETTINGS_SET_KEEP_FORWARD_ENTRIES);
+            mAwSettings.setBackForwardCacheKeepForwardEntries(keepForwardEntries);
+        }
+    }
+
+    @Override
     public long getBackForwardCacheSettingsTimeout() {
         try (TraceEvent ignored =
                 TraceEvent.scoped(
@@ -614,4 +624,14 @@
             return mAwSettings.getBackForwardCacheSettingsMaxPagesInCache();
         }
     }
+
+    @Override
+    public boolean getBackForwardCacheSettingsKeepForwardEntries() {
+        try (TraceEvent ignored =
+                TraceEvent.scoped(
+                        "WebView.APICall.AndroidX.BACK_FORWARD_CACHE_SETTINGS_GET_KEEP_FORWARD_ENTRIES")) {
+            recordApiCall(ApiCall.BACK_FORWARD_CACHE_SETTINGS_GET_KEEP_FORWARD_ENTRIES);
+            return mAwSettings.getBackForwardCacheSettingsKeepForwardEntries();
+        }
+    }
 }
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 40a1e3b..8a75068 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
@@ -250,12 +250,26 @@
     }
 
     @Override
+    public void setBackForwardCacheSettingsKeepForwardEntries(
+            boolean keepForwardEntries) {
+        recordApiCall(ApiCall.BACK_FORWARD_CACHE_SETTINGS_SET_KEEP_FORWARD_ENTRIES);
+    }
+
+    @Override
     public long getBackForwardCacheSettingsTimeout() {
+        recordApiCall(ApiCall.BACK_FORWARD_CACHE_SETTINGS_GET_TIMEOUT_IN_SECONDS);
         return 0;
     }
 
     @Override
     public int getBackForwardCacheSettingsMaxPagesInCache() {
+        recordApiCall(ApiCall.BACK_FORWARD_CACHE_SETTINGS_GET_MAX_PAGES_IN_CACHE);
         return 0;
     }
+
+    @Override
+    public boolean getBackForwardCacheSettingsKeepForwardEntries() {
+        recordApiCall(ApiCall.BACK_FORWARD_CACHE_SETTINGS_GET_KEEP_FORWARD_ENTRIES);
+        return true;
+    }
 }
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 7b15baef..e7347bb 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
@@ -140,6 +140,7 @@
                 Features.PAGE_GET_URL,
                 Features.JS_INJECTION_IN_FRAME_AND_WORLD,
                 Features.NAVIGATION_GET_WEB_RESOURCE_ERROR,
+                Features.BACK_FORWARD_CACHE_SETTINGS_V4 + 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.
@@ -330,6 +331,8 @@
         ApiCall.SET_MAX_PRERENDERS,
         ApiCall.SET_MAX_PREFETCHES,
         ApiCall.SET_PREFETCH_TTL_SECONDS,
+        ApiCall.BACK_FORWARD_CACHE_SETTINGS_GET_KEEP_FORWARD_ENTRIES,
+        ApiCall.BACK_FORWARD_CACHE_SETTINGS_SET_KEEP_FORWARD_ENTRIES,
         // Add new constants above. The final constant should have a trailing comma for cleaner
         // diffs.
         ApiCall.COUNT, // Added to suppress WrongConstant in #recordApiCall
@@ -520,8 +523,10 @@
         int SET_MAX_PRERENDERS = 179;
         int SET_MAX_PREFETCHES = 180;
         int SET_PREFETCH_TTL_SECONDS = 181;
+        int BACK_FORWARD_CACHE_SETTINGS_GET_KEEP_FORWARD_ENTRIES = 182;
+        int BACK_FORWARD_CACHE_SETTINGS_SET_KEEP_FORWARD_ENTRIES = 183;
         // Remember to update AndroidXWebkitApiCall in enums.xml when adding new values here
-        int COUNT = 182;
+        int COUNT = 184;
     }
 
     // LINT.ThenChange(/tools/metrics/histograms/metadata/android/enums.xml:AndroidXWebkitApiCall)
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h
index 130161cd..ffb3dad3 100644
--- a/ash/constants/ash_pref_names.h
+++ b/ash/constants/ash_pref_names.h
@@ -3099,6 +3099,75 @@
 inline constexpr char kUserPrintersAllowed[] =
     "native_printing.user_native_printers_allowed";
 
+//-----------------------------------------------------------------------------
+// Child account related Prefs
+//-----------------------------------------------------------------------------
+
+// Amount of screen time that a child user has used in the current day.
+inline constexpr char kChildScreenTimeMilliseconds[] = "child_screen_time";
+
+// TimeDelta pref to record the accumulated Chrome browser app usage for family
+// user metrics.
+inline constexpr char kFamilyUserMetricsChromeBrowserEngagementDuration[] =
+    "family_user.metrics.chrome_browser_engagement_duration";
+
+// Integer pref to record the day id (number of days since origin of time) when
+// family user metrics were last recorded.
+inline constexpr char kFamilyUserMetricsDayId[] = "family_user.metrics.day_id";
+
+// TimeDelta pref to record the accumulated user session duration for family
+// user metrics.
+inline constexpr char kFamilyUserMetricsSessionEngagementDuration[] =
+    "family_user.metrics.session_engagement_duration";
+
+// Dictionary pref containing the configuration used to verify Parent Access
+// Code. The data is sent through the ParentAccessCodeConfig policy, which is
+// set for child users only, and kept on the known user storage.
+inline constexpr char kKnownUserParentAccessCodeConfig[] =
+    "child_user.parent_access_code.config";
+
+// Dictionary pref containing configuration used to verify Parent Access Code.
+// Controlled by ParentAccessCodeConfig policy.
+inline constexpr char kParentAccessCodeConfig[] =
+    "child_user.parent_access_code.config";
+
+// Dictionary pref containing the allowed urls, schemes and applications
+// that would not be blocked by per app time limits.
+inline constexpr char kPerAppTimeLimitsAllowlistPolicy[] =
+    "child_user.per_app_time_limits.allowlist";
+
+// List pref containing app activity and state for each application.
+inline constexpr char kPerAppTimeLimitsAppActivities[] =
+    "child_user.per_app_time_limits.app_activities";
+
+// Int64 to specify the last timestamp the AppActivityRegistry was reset.
+inline constexpr char kPerAppTimeLimitsLastResetTime[] =
+    "child_user.per_app_time_limits.last_reset_time";
+
+// Int64 to specify the last timestamp the app activity has been successfully
+// reported.
+inline constexpr char kPerAppTimeLimitsLastSuccessfulReportTime[] =
+    "child_user.per_app_time_limits.last_successful_report_time";
+
+// Int64 to specify the latest AppLimit update timestamp from.
+inline constexpr char kPerAppTimeLimitsLatestLimitUpdateTime[] =
+    "child_user.per_app_time_limits.latest_limit_update_time";
+
+// Dictionary pref containing the per-app time limits configuration for
+// child user. Controlled by PerAppTimeLimits policy.
+inline constexpr char kPerAppTimeLimitsPolicy[] =
+    "child_user.per_app_time_limits.policy";
+
+// Last state of the screen time limit.
+inline constexpr char kScreenTimeLastState[] = "screen_time.last_state";
+
+// A dictionary containing the latest Time Limits override authorized by parent
+// access code.
+inline constexpr char kTimeLimitLocalOverride[] = "screen_time.local_override";
+
+// A dictionary preference holding the usage time limit definitions for a user.
+inline constexpr char kUsageTimeLimit[] = "screen_time.limit";
+
 // NOTE: New prefs should start with the "ash." prefix. Existing prefs moved
 // into this file should not be renamed, since they may be synced.
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index c9811ba..7ab4379d 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1140,6 +1140,7 @@
     "//base/numerics:base_numerics",
     "//base/third_party/icu",
     "//build:chromecast_buildflags",
+    "//build:robolectric_buildflags",
     "//third_party/abseil-cpp:absl",
   ]
 
@@ -1434,7 +1435,6 @@
       ":library_loader_jni($default_toolchain)",
       ":metrics_jni($default_toolchain)",
       ":tasks_minimal_jni($default_toolchain)",
-      "//build:robolectric_buildflags",
     ]
     public_deps += [ "//third_party/jni_zero" ]
   }  # is_android || is_robolectric
diff --git a/base/android/java/src/org/chromium/base/AconfigFlaggedApiDelegate.java b/base/android/java/src/org/chromium/base/AconfigFlaggedApiDelegate.java
index 6e4019f..a9ef0f0 100644
--- a/base/android/java/src/org/chromium/base/AconfigFlaggedApiDelegate.java
+++ b/base/android/java/src/org/chromium/base/AconfigFlaggedApiDelegate.java
@@ -12,8 +12,10 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.hardware.display.DisplayManager;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.OutcomeReceiver;
+import android.os.ParcelFileDescriptor;
 import android.util.Pair;
 import android.util.SparseArray;
 import android.view.Display;
@@ -463,4 +465,26 @@
     default boolean isContentRestrictionEnabled() {
         return false;
     }
+
+    /**
+     * Calls the platform to determine if the content should be allowed or blocked.
+     *
+     * @param uri The URI of the content to be classified.
+     * @param requestBody The request body of the content to be classified. Can be null for requests
+     *     that have no body (for ex. GET requests).
+     * @param mimeType The MIME type of the content to be classified.
+     * @param executor The executor to run the callback on.
+     * @return A promise fulfilled with the boolean classification result (true if allowed),
+     *     rejected otherwise with {@link UnsupportedOperationException} if not supported or with
+     *     the exception received from the API call.
+     */
+    default Promise<Boolean> requestContentRestrictionClassification(
+            Uri uri,
+            @Nullable ParcelFileDescriptor requestBody,
+            String mimeType,
+            Executor executor) {
+        Promise<Boolean> promise = new Promise<>();
+        promise.reject(new UnsupportedOperationException("Not supported"));
+        return promise;
+    }
 }
diff --git a/base/uuid.h b/base/uuid.h
index 38aa3e5..afa15e8b 100644
--- a/base/uuid.h
+++ b/base/uuid.h
@@ -15,8 +15,14 @@
 #include "base/base_export.h"
 #include "base/compiler_specific.h"
 #include "base/containers/span.h"
+#include "build/robolectric_buildflags.h"
 #include "third_party/abseil-cpp/absl/numeric/int128.h"
 
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_ROBOLECTRIC)
+#include "base/android/jni_string.h"
+#include "third_party/jni_zero/jni_zero.h"
+#endif
+
 namespace base {
 
 class BASE_EXPORT Uuid {
@@ -99,4 +105,26 @@
 
 }  // namespace base
 
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_ROBOLECTRIC)
+namespace jni_zero {
+
+// TODO(agrieve): We map base::Uuid to String. Might be nicer to map it to
+// android.util.UUID instead.
+template <>
+inline base::Uuid FromJniType<base::Uuid>(
+    JNIEnv* env,
+    const jni_zero::JavaRef<jobject>& obj) {
+  return base::Uuid::ParseLowercase(FromJniType<std::string>(env, obj));
+}
+
+template <>
+inline ScopedJavaLocalRef<jobject> ToJniType<base::Uuid>(
+    JNIEnv* env,
+    const base::Uuid& uuid) {
+  return ToJniType(env, uuid.AsLowercaseString());
+}
+
+}  // namespace jni_zero
+#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_ROBOLECTRIC)
+
 #endif  // BASE_UUID_H_
diff --git a/cc/mojo_embedder/viz_layer_context.cc b/cc/mojo_embedder/viz_layer_context.cc
index 594ea24..8342b05b 100644
--- a/cc/mojo_embedder/viz_layer_context.cc
+++ b/cc/mojo_embedder/viz_layer_context.cc
@@ -4,6 +4,7 @@
 
 #include "cc/mojo_embedder/viz_layer_context.h"
 
+#include <cmath>
 #include <cstdint>
 #include <memory>
 #include <string>
@@ -432,9 +433,15 @@
 
   auto wire = viz::mojom::TransformTreeUpdate::New();
   wire->page_scale_factor = new_tree.page_scale_factor();
+  DUMP_WILL_BE_CHECK_GT(wire->page_scale_factor, 0.f);
+  DUMP_WILL_BE_CHECK(std::isfinite(wire->page_scale_factor));
   wire->device_scale_factor = new_tree.device_scale_factor();
+  DUMP_WILL_BE_CHECK_GT(wire->device_scale_factor, 0.f);
+  DUMP_WILL_BE_CHECK(std::isfinite(wire->device_scale_factor));
   wire->device_transform_scale_factor =
       new_tree.device_transform_scale_factor();
+  DUMP_WILL_BE_CHECK_GT(wire->device_transform_scale_factor, 0.f);
+  DUMP_WILL_BE_CHECK(std::isfinite(wire->device_transform_scale_factor));
   wire->nodes_affected_by_outer_viewport_bounds_delta =
       new_tree.nodes_affected_by_outer_viewport_bounds_delta();
   wire->nodes_affected_by_safe_area_bottom =
@@ -1375,14 +1382,28 @@
       tree.primary_main_frame_item_sequence_number();
   update->selection = tree.selection();
   update->page_scale_factor = tree.page_scale_factor()->Current(true);
+  DUMP_WILL_BE_CHECK_GT(update->page_scale_factor, 0.f);
+  DUMP_WILL_BE_CHECK(std::isfinite(update->page_scale_factor));
   update->min_page_scale_factor = tree.min_page_scale_factor();
+  DUMP_WILL_BE_CHECK_GT(update->min_page_scale_factor, 0.f);
+  DUMP_WILL_BE_CHECK(std::isfinite(update->min_page_scale_factor));
   update->max_page_scale_factor = tree.max_page_scale_factor();
+  DUMP_WILL_BE_CHECK_GT(update->max_page_scale_factor, 0.f);
+  DUMP_WILL_BE_CHECK(std::isfinite(update->max_page_scale_factor));
+  DUMP_WILL_BE_CHECK_GE(update->max_page_scale_factor,
+                        update->min_page_scale_factor);
   update->external_page_scale_factor = tree.external_page_scale_factor();
+  DUMP_WILL_BE_CHECK_GT(update->external_page_scale_factor, 0.f);
+  DUMP_WILL_BE_CHECK(std::isfinite(update->external_page_scale_factor));
   update->frame_has_damage = frame_has_damage;
   update->latency_info = std::move(latency_info);
   update->device_viewport = tree.GetDeviceViewport();
   update->device_scale_factor = tree.device_scale_factor();
+  DUMP_WILL_BE_CHECK_GT(update->device_scale_factor, 0.f);
+  DUMP_WILL_BE_CHECK(std::isfinite(update->device_scale_factor));
   update->painted_device_scale_factor = tree.painted_device_scale_factor();
+  DUMP_WILL_BE_CHECK_GT(update->painted_device_scale_factor, 0.f);
+  DUMP_WILL_BE_CHECK(std::isfinite(update->painted_device_scale_factor));
   update->display_color_spaces = tree.display_color_spaces();
   if (tree.local_surface_id_from_parent().is_valid()) {
     update->local_surface_id_from_parent = tree.local_surface_id_from_parent();
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
index 8164a693..b505475c 100644
--- a/cc/scheduler/scheduler_state_machine.cc
+++ b/cc/scheduler/scheduler_state_machine.cc
@@ -520,7 +520,7 @@
   return true;
 }
 
-bool SchedulerStateMachine::ShouldBeginMainFrameWhenIdle() const {
+bool SchedulerStateMachine::ShouldBlockBeginMainFrameWhenIdle() const {
   return (!settings_.using_synchronous_renderer_compositor &&
           begin_impl_frame_state_ == BeginImplFrameState::IDLE);
 }
@@ -577,7 +577,7 @@
   // might have new user input arriving soon.
   // TODO(brianderson): Allow sending BeginMainFrame while idle when the main
   // thread isn't consuming user input for non-synchronous compositor.
-  if (ShouldBeginMainFrameWhenIdle()) {
+  if (ShouldBlockBeginMainFrameWhenIdle()) {
     return false;
   }
 
diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h
index 37826fe23..7facf7e 100644
--- a/cc/scheduler/scheduler_state_machine.h
+++ b/cc/scheduler/scheduler_state_machine.h
@@ -420,7 +420,7 @@
   bool ShouldActivateSyncTree() const;
   virtual bool ShouldActivateSyncTreeBeforeDraw() const;
   bool ShouldSendBeginMainFrame() const;
-  virtual bool ShouldBeginMainFrameWhenIdle() const;
+  virtual bool ShouldBlockBeginMainFrameWhenIdle() const;
   bool ShouldCommit() const;
   bool ShouldRunPostCommit() const;
   virtual bool ShouldPrepareTiles() const;
diff --git a/cc/scheduler/webview_scheduler_state_machine.cc b/cc/scheduler/webview_scheduler_state_machine.cc
index 9d339fb6..b4a12ae 100644
--- a/cc/scheduler/webview_scheduler_state_machine.cc
+++ b/cc/scheduler/webview_scheduler_state_machine.cc
@@ -21,7 +21,7 @@
   return false;
 }
 
-bool WebviewSchedulerStateMachine::ShouldBeginMainFrameWhenIdle() const {
+bool WebviewSchedulerStateMachine::ShouldBlockBeginMainFrameWhenIdle() const {
   // It's okay to send BeginMainFrame for the synchronous compositor
   // because the main thread is always high latency in that case.
   return false;
diff --git a/cc/scheduler/webview_scheduler_state_machine.h b/cc/scheduler/webview_scheduler_state_machine.h
index fcb900b..ab3e422f 100644
--- a/cc/scheduler/webview_scheduler_state_machine.h
+++ b/cc/scheduler/webview_scheduler_state_machine.h
@@ -17,7 +17,7 @@
   ~WebviewSchedulerStateMachine() override;
 
   bool ShouldActivateSyncTreeBeforeDraw() const override;
-  bool ShouldBeginMainFrameWhenIdle() const override;
+  bool ShouldBlockBeginMainFrameWhenIdle() const override;
   bool ShouldInvalidateLayerTreeFrameSink() const override;
   BeginImplFrameDeadlineMode CurrentBeginImplFrameDeadlineMode() const override;
 };
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 1ff726a..c59d0a2 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -844,6 +844,7 @@
   "java/src/org/chromium/chrome/browser/ntp/IncognitoNtpUtils.java",
   "java/src/org/chromium/chrome/browser/ntp/NativePageRootFrameLayout.java",
   "java/src/org/chromium/chrome/browser/ntp/NewTabPage.java",
+  "java/src/org/chromium/chrome/browser/ntp/NewTabPageCoordinator.java",
   "java/src/org/chromium/chrome/browser/ntp/NewTabPageCreationState.java",
   "java/src/org/chromium/chrome/browser/ntp/NewTabPageCreationTracker.java",
   "java/src/org/chromium/chrome/browser/ntp/NewTabPageLaunchOrigin.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
index a021127a..2f22469 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
@@ -201,8 +201,8 @@
     private static final float GLIC_BUTTON_BACKGROUND_Y_OFFSET_DP = 5.f;
     private static final float GLIC_BUTTON_BACKGROUND_WIDTH_DP = 28.f;
     private static final float GLIC_BUTTON_BACKGROUND_HEIGHT_DP = 28.f;
-    private static final float GLIC_BUTTON_HOVER_BACKGROUND_PRESSED_OPACITY = 0.24f;
-    private static final float GLIC_BUTTON_HOVER_BACKGROUND_DEFAULT_OPACITY = 0.16f;
+    private static final float GLIC_BUTTON_HOVER_BACKGROUND_PRESSED_OPACITY = 0.30f;
+    private static final float GLIC_BUTTON_HOVER_BACKGROUND_DEFAULT_OPACITY = 0.20f;
     private static final float GLIC_BUTTON_CLICK_SLOP_DP =
             (BUTTON_DESIRED_TOUCH_TARGET_SIZE - GLIC_BUTTON_BACKGROUND_WIDTH_DP) / 2;
     private static final float GLIC_BUTTON_START_PADDING_DP = 6.f;
@@ -777,24 +777,20 @@
         mGlicButton.setVisible(false);
 
         @ColorInt
-        int backgroundDefaultColor =
-                TabUiThemeUtil.getTabStripSelectedTabColor(context, /* isIncognito= */ false);
+        int backgroundDefaultColor = SemanticColorUtils.getColorSurfaceContainerLow(context);
 
         @ColorInt
         int apsBackgroundHoveredColor =
                 ColorUtils.setAlphaComponentWithFloat(
-                        SemanticColorUtils.getDefaultTextColor(context),
+                        SemanticColorUtils.getColorPrimary(context),
                         GLIC_BUTTON_HOVER_BACKGROUND_DEFAULT_OPACITY);
         @ColorInt
         int backgroundPressedColor =
                 ColorUtils.setAlphaComponentWithFloat(
-                        SemanticColorUtils.getDefaultTextColor(context),
+                        SemanticColorUtils.getColorPrimary(context),
                         GLIC_BUTTON_HOVER_BACKGROUND_PRESSED_OPACITY);
 
-        @ColorInt
-        int iconDefaultColor =
-                AppCompatResources.getColorStateList(context, R.color.default_icon_color_tint_list)
-                        .getDefaultColor();
+        @ColorInt int iconDefaultColor = SemanticColorUtils.getDefaultIconColor(context);
 
         mGlicButton.setTint(
                 iconDefaultColor, iconDefaultColor, Color.TRANSPARENT, Color.TRANSPARENT);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java
index 570e16c3..3e28c4a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/MediaCapturePickerDialog.java
@@ -218,10 +218,7 @@
                     assumeNonNull(mDelegate);
                     switch (action) {
                         case CaptureAction.CAPTURE_CANCELLED:
-                            mDelegate.onCancel();
-                            MediaCapturePickerManager.recordResult(
-                                    MediaCapturePickerManager.Result.CANCELLED);
-                            break;
+                            return;
                         case CaptureAction.CAPTURE_WINDOW:
                             mDelegate.onPickWindow();
                             MediaCapturePickerManager.recordResult(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index d1555078..f3cdee8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -174,6 +174,7 @@
     private FeedSurfaceProvider mFeedSurfaceProvider;
 
     private NewTabPageLayout mNewTabPageLayout;
+    private NewTabPageCoordinator mNewTabPageCoordinator;
     private @Nullable TabObserver mTabObserver;
     private @Nullable LifecycleObserver mLifecycleObserver;
     protected boolean mSearchProviderHasLogo;
@@ -292,7 +293,7 @@
         @Override
         public boolean isLocationBarShownInNtp() {
             if (mIsDestroyed) return false;
-            return isInSingleUrlBarMode() && !mNewTabPageLayout.urlFocusAnimationsDisabled();
+            return isInSingleUrlBarMode() && !mNewTabPageCoordinator.urlFocusAnimationsDisabled();
         }
 
         @Override
@@ -385,7 +386,7 @@
             if (mIsDestroyed) return;
 
             super.onLoadingComplete(tiles);
-            mNewTabPageLayout.onTilesLoaded();
+            mNewTabPageCoordinator.onTilesLoaded();
         }
 
         @Override
@@ -512,7 +513,7 @@
                             recordNtpShown();
                         }
                         maybeUpdateMagicStack();
-                        mNewTabPageLayout.onSwitchToForeground();
+                        mNewTabPageCoordinator.onSwitchToForeground();
                     }
 
                     @Override
@@ -602,9 +603,7 @@
 
         NewTabPageUma.recordContentSuggestionsDisplayStatus(profile);
 
-        mNewTabPageLayout.initialize(
-                mNewTabPageManager,
-                activity,
+        mNewTabPageCoordinator.initialize(
                 mTileGroupDelegate,
                 mSearchProviderHasLogo,
                 mIsDefaultSearchEngineGoogle,
@@ -643,7 +642,7 @@
      * @param edgeToEdgeControllerSupplier The supplier to {@link EdgeToEdgeController}.
      * @param startupMetricsTracker Used to record NTP startup metric.
      */
-    @EnsuresNonNull({"mNewTabPageLayout", "mFeedSurfaceProvider"})
+    @EnsuresNonNull({"mNewTabPageCoordinator", "mNewTabPageLayout", "mFeedSurfaceProvider"})
     protected void initializeMainView(
             Activity activity,
             WindowAndroid windowAndroid,
@@ -661,6 +660,8 @@
         // TODO(crbug.com/347509698): Remove the log statements after fixing the bug.
         Log.i(TAG, "NewTabPageLayout inflate");
         mNewTabPageLayout = (NewTabPageLayout) inflater.inflate(R.layout.new_tab_page_layout, null);
+        mNewTabPageCoordinator =
+                new NewTabPageCoordinator(mNewTabPageManager, activity, mNewTabPageLayout);
 
         FeedSurfaceCoordinator.ActionDelegateFactory createActionDelegate =
                 () ->
@@ -689,7 +690,7 @@
                         activity,
                         snackbarManager,
                         windowAndroid,
-                        new SnapScrollHelperImpl(mNewTabPageManager, mNewTabPageLayout),
+                        new SnapScrollHelperImpl(mNewTabPageManager, mNewTabPageCoordinator),
                         mNewTabPageLayout,
                         mBrowserControlsStateProvider.getTopControlsHeight(),
                         isInNightMode,
@@ -789,7 +790,7 @@
         if (!mIsTablet) {
             mUseLightIconTint = applyWhiteBackgroundOnSearchBox;
         }
-        mNewTabPageLayout.onCustomizedBackgroundChanged(applyWhiteBackgroundOnSearchBox);
+        mNewTabPageCoordinator.onCustomizedBackgroundChanged(applyWhiteBackgroundOnSearchBox);
     }
 
     /** Initializes whether to use a light tint color on icons of toolbar and status bar. */
@@ -821,7 +822,7 @@
         // However, consumeTopInset being false takes priority — if the parent is not consuming top
         // insets (e.g. status indicator is visible), the NTP must not apply its own top inset
         // either, since the content view already has the system top padding.
-        mNewTabPageLayout.onToEdgeChange(
+        mNewTabPageCoordinator.onToEdgeChange(
                 systemTopInset, consumeTopInset && supportsEdgeToEdgeOnTop());
     }
 
@@ -880,16 +881,16 @@
                 - mTabStripHeightSupplier.get();
     }
 
-    /** Returns the instance of {@link NewTabPageLayout}. */
+    /** Returns the instance of {@link NewTabPageCoordinator}. */
     @VisibleForTesting
-    public NewTabPageLayout getNewTabPageLayout() {
-        return mNewTabPageLayout;
+    public NewTabPageCoordinator getNewTabPageCoordinator() {
+        return mNewTabPageCoordinator;
     }
 
     /** Returns the view container for the new tab layout. */
     @VisibleForTesting
     public View getLayout() {
-        return mNewTabPageLayout;
+        return mNewTabPageCoordinator.getNewTabPageLayout();
     }
 
     /**
@@ -898,7 +899,7 @@
      * @param disable Whether to disable the animations.
      */
     public void setUrlFocusAnimationsDisabled(boolean disable) {
-        mNewTabPageLayout.setUrlFocusAnimationsDisabled(disable);
+        mNewTabPageCoordinator.setUrlFocusAnimationsDisabled(disable);
     }
 
     private boolean isInSingleUrlBarMode() {
@@ -914,7 +915,7 @@
     private void onSearchEngineUpdated() {
         updateSearchProvider();
 
-        mNewTabPageLayout.setSearchProviderInfo(
+        mNewTabPageCoordinator.setSearchProviderInfo(
                 mSearchProviderHasLogo, mIsDefaultSearchEngineGoogle);
         // TODO(crbug.com/40226731): Remove this call when the Feed position experiment is
         // cleaned up.
@@ -929,7 +930,7 @@
      * @param percent The percentage of the URL bar focus animation.
      */
     public void setUrlFocusChangeAnimationPercent(float percent) {
-        mNewTabPageLayout.setUrlFocusChangeAnimationPercent(percent);
+        mNewTabPageCoordinator.setUrlFocusChangeAnimationPercent(percent);
     }
 
     /**
@@ -940,7 +941,7 @@
      *     to the NewTabPage view.
      */
     public void getSearchBoxBounds(Rect bounds, Point translation) {
-        mNewTabPageLayout.getSearchBoxBounds(bounds, translation, getView());
+        mNewTabPageCoordinator.getSearchBoxBounds(bounds, translation, getView());
     }
 
     /**
@@ -949,7 +950,7 @@
      * @return The vertical inset in pixels.
      */
     public int getSearchBoxBoundsVerticalInset() {
-        return mNewTabPageLayout.getSearchBoxBoundsVerticalInset();
+        return mNewTabPageCoordinator.getSearchBoxBoundsVerticalInset();
     }
 
     /**
@@ -958,7 +959,7 @@
      * @param alpha opacity (alpha) value to use.
      */
     public void setSearchBoxAlpha(float alpha) {
-        mNewTabPageLayout.setSearchBoxAlpha(alpha);
+        mNewTabPageCoordinator.setSearchBoxAlpha(alpha);
     }
 
     /**
@@ -967,7 +968,7 @@
      * @param alpha opacity (alpha) value to use.
      */
     public void setSearchProviderLogoAlpha(float alpha) {
-        mNewTabPageLayout.setSearchProviderLogoAlpha(alpha);
+        mNewTabPageCoordinator.setSearchProviderLogoAlpha(alpha);
     }
 
     /**
@@ -981,14 +982,14 @@
      * @see org.chromium.chrome.browser.omnibox.NewTabPageDelegate#hasCompletedFirstLayout().
      */
     public boolean hasCompletedFirstLayout() {
-        return mNewTabPageLayout.getHeight() > 0;
+        return mNewTabPageCoordinator.getNewTabPageLayout().getHeight() > 0;
     }
 
     /**
      * @return Whether the location bar has been scrolled to top in the NTP.
      */
     public boolean isLocationBarScrolledToTopInNtp() {
-        return mNewTabPageLayout.getToolbarTransitionPercentage() == 1;
+        return mNewTabPageCoordinator.getToolbarTransitionPercentage() == 1;
     }
 
     /**
@@ -997,7 +998,7 @@
      * @param listener The listener to be notified on changes.
      */
     public void setSearchBoxScrollListener(@Nullable OnSearchBoxScrollListener listener) {
-        mNewTabPageLayout.setSearchBoxScrollListener(listener);
+        mNewTabPageCoordinator.setSearchBoxScrollListener(listener);
     }
 
     /** Sets the OmniboxStub that this page interacts with. */
@@ -1007,7 +1008,7 @@
             // The toolbar can't get the reference to the native page until its initialization is
             // finished, so we can't cache it here and transfer it to the view later. We pull that
             // state from the location bar when we get a reference to it as a workaround.
-            mNewTabPageLayout.setUrlFocusChangeAnimationPercent(
+            mNewTabPageCoordinator.setUrlFocusChangeAnimationPercent(
                     omniboxStub.isUrlBarFocused() ? 1f : 0f);
 
             FeedReliabilityLogger feedReliabilityLogger =
@@ -1020,7 +1021,7 @@
         mVoiceRecognitionHandler = mOmniboxStub.getVoiceRecognitionHandler();
         if (mVoiceRecognitionHandler != null) {
             mVoiceRecognitionHandler.addObserver(this);
-            mNewTabPageLayout.updateActionButtonVisibility();
+            mNewTabPageCoordinator.updateActionButtonVisibility();
         }
     }
 
@@ -1042,7 +1043,7 @@
 
     @Override
     public void onVoiceAvailabilityImpacted() {
-        mNewTabPageLayout.updateActionButtonVisibility();
+        mNewTabPageCoordinator.updateActionButtonVisibility();
     }
 
     /** Adds an observer to be notified on most visited tile clicks. */
@@ -1247,26 +1248,26 @@
     @Override
     public void reload() {
         mFeedSurfaceProvider.reload();
-        mNewTabPageLayout.reload();
+        mNewTabPageCoordinator.reload();
     }
 
     @Override
     public int getTopInset() {
-        return mNewTabPageLayout.getTopInset();
+        return mNewTabPageCoordinator.getTopInset();
     }
 
     // InvalidationAwareThumbnailProvider
 
     @Override
     public boolean shouldCaptureThumbnail() {
-        return mNewTabPageLayout.shouldCaptureThumbnail()
+        return mNewTabPageCoordinator.shouldCaptureThumbnail()
                 || mFeedSurfaceProvider.shouldCaptureThumbnail()
                 || mSnapshotSingleTabCardChanged;
     }
 
     @Override
     public void captureThumbnail(Canvas canvas) {
-        mNewTabPageLayout.onPreCaptureThumbnail();
+        mNewTabPageCoordinator.onPreCaptureThumbnail();
         mFeedSurfaceProvider.captureThumbnail(canvas);
         mSnapshotSingleTabCardChanged = false;
     }
@@ -1331,7 +1332,7 @@
 
         mSearchResumptionModuleCoordinator =
                 SearchResumptionModuleUtils.mayCreateSearchResumptionModule(
-                        mNewTabPageLayout,
+                        mNewTabPageCoordinator.getNewTabPageLayout(),
                         mTabModelSelector.getCurrentModel(),
                         mTab,
                         profile,
@@ -1384,8 +1385,10 @@
         mSingleTabCardContainer =
                 (FrameLayout)
                         ((ViewStub)
-                                        mNewTabPageLayout.findViewById(
-                                                R.id.tab_switcher_module_container_stub))
+                                        mNewTabPageCoordinator
+                                                .getNewTabPageLayout()
+                                                .findViewById(
+                                                        R.id.tab_switcher_module_container_stub))
                                 .inflate();
         mSingleTabSwitcherCoordinator =
                 new SingleTabSwitcherCoordinator(
@@ -1410,10 +1413,11 @@
      */
     @EnsuresNonNull({"mHomeModulesContainer", "mHomeModulesCoordinator"})
     private void initializeMagicStack() {
+        NewTabPageLayout newTabPageLayout = mNewTabPageCoordinator.getNewTabPageLayout();
         mHomeModulesContainer =
                 (ViewGroup)
                         ((ViewStub)
-                                        mNewTabPageLayout.findViewById(
+                                        newTabPageLayout.findViewById(
                                                 R.id.home_modules_recycler_view_stub))
                                 .inflate();
         MonotonicObservableSupplier<Profile> profileSupplier =
@@ -1422,7 +1426,7 @@
                 new HomeModulesCoordinator(
                         mActivity,
                         this,
-                        mNewTabPageLayout,
+                        newTabPageLayout,
                         HomeModulesConfigManager.getInstance(),
                         profileSupplier,
                         assertNonNull(assumeNonNull(mModuleRegistrySupplier).get()));
@@ -1487,7 +1491,7 @@
         return mHomeModulesContainer.getVisibility() == View.VISIBLE;
     }
 
-    /** Destroy the single tab card on the {@link NewTabPageLayout}. */
+    /** Destroy the single tab card on the {@link NewTabPageCoordinator}. */
     @VisibleForTesting
     void destroySingleTabCard() {
         if (mSingleTabCardContainer != null) mSingleTabCardContainer.removeAllViews();
@@ -1586,6 +1590,6 @@
     }
 
     public void enableSearchBoxEditText(boolean enable) {
-        mNewTabPageLayout.enableSearchBoxEditText(enable);
+        mNewTabPageCoordinator.enableSearchBoxEditText(enable);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageCoordinator.java
new file mode 100644
index 0000000..b6856b24b
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageCoordinator.java
@@ -0,0 +1,1205 @@
+// Copyright 2015 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.ntp;
+
+import static org.chromium.build.NullUtil.assumeNonNull;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.text.Editable;
+import android.view.DragEvent;
+import android.view.View;
+import android.view.View.OnLayoutChangeListener;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.Callback;
+import org.chromium.base.CallbackController;
+import org.chromium.base.Log;
+import org.chromium.base.TraceEvent;
+import org.chromium.build.annotations.Initializer;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.composeplate.ComposeplateCoordinator;
+import org.chromium.chrome.browser.composeplate.ComposeplateMetricsUtils;
+import org.chromium.chrome.browser.composeplate.ComposeplateUtils;
+import org.chromium.chrome.browser.device_lock.DeviceLockActivityLauncherImpl;
+import org.chromium.chrome.browser.feed.FeedSurfaceScrollDelegate;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.incognito.IncognitoUtils;
+import org.chromium.chrome.browser.lens.LensEntryPoint;
+import org.chromium.chrome.browser.lens.LensMetrics;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.logo.LogoBridge.Logo;
+import org.chromium.chrome.browser.logo.LogoCoordinator;
+import org.chromium.chrome.browser.logo.LogoUtils;
+import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
+import org.chromium.chrome.browser.ntp.NewTabPage.OnSearchBoxScrollListener;
+import org.chromium.chrome.browser.ntp.search.SearchBoxCoordinator;
+import org.chromium.chrome.browser.ntp_customization.NtpCustomizationConfigManager;
+import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils;
+import org.chromium.chrome.browser.omnibox.SearchEngineUtils;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.setup_list.SetupListModuleUtils;
+import org.chromium.chrome.browser.signin.SigninAndHistorySyncActivityLauncherImpl;
+import org.chromium.chrome.browser.suggestions.tile.MostVisitedTilesCoordinator;
+import org.chromium.chrome.browser.suggestions.tile.TileGroup;
+import org.chromium.chrome.browser.tab_ui.InvalidationAwareThumbnailProvider;
+import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
+import org.chromium.chrome.browser.ui.native_page.TouchEnabledDelegate;
+import org.chromium.chrome.browser.ui.signin.signin_promo.NtpSigninPromoCoordinator;
+import org.chromium.chrome.browser.url_constants.UrlConstantResolver;
+import org.chromium.chrome.browser.url_constants.UrlConstantResolverFactory;
+import org.chromium.chrome.browser.util.BrowserUiUtils;
+import org.chromium.chrome.browser.util.BrowserUiUtils.ModuleTypeOnStartAndNtp;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.components.browser_ui.widget.displaystyle.DisplayStyleObserver;
+import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
+import org.chromium.components.omnibox.AutocompleteRequestType;
+import org.chromium.components.omnibox.OmniboxFeatures;
+import org.chromium.components.signin.SigninFeatureMap;
+import org.chromium.components.signin.SigninFeatures;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.ui.base.ActivityResultTracker;
+import org.chromium.ui.base.MimeTypeUtils;
+import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.text.EmptyTextWatcher;
+import org.chromium.url.GURL;
+
+import java.util.function.Supplier;
+
+/**
+ * Layout for the new tab page. This positions the page elements in the correct vertical positions.
+ * There are no separate phone and tablet UIs; this layout adapts based on the available space.
+ */
+@NullMarked
+public class NewTabPageCoordinator {
+    private static final String TAG = "NewTabPageLayout";
+
+    private final NewTabPageManager mManager;
+    private final Activity mActivity;
+    private final NewTabPageLayout mNewTabPageLayout;
+    private final NewTabPageLayout.Delegate mLayoutDelegate;
+    private LogoCoordinator mLogoCoordinator;
+    private SearchBoxCoordinator mSearchBoxCoordinator;
+    private @Nullable MostVisitedTilesCoordinator mMostVisitedTilesCoordinator;
+
+    private @Nullable OnSearchBoxScrollListener mSearchBoxScrollListener;
+
+    private WindowAndroid mWindowAndroid;
+    private Profile mProfile;
+    private ActivityResultTracker mActivityResultTracker;
+    private BottomSheetController mBottomSheetController;
+    private ModalDialogManager mModalDialogManager;
+    private SnackbarManager mSnackbarManager;
+    private UiConfig mUiConfig;
+    private @Nullable DisplayStyleObserver mDisplayStyleObserver;
+    private CallbackController mCallbackController = new CallbackController();
+    private SearchEngineUtils.@Nullable SearchEngineIconObserver mSearchEngineIconObserver;
+    private SearchEngineUtils.@Nullable SearchBoxHintTextObserver mSearchBoxHintTextObserver;
+
+    /**
+     * Whether the tiles shown in the layout have finished loading. With {@link #mHasShownView},
+     * it's one of the 2 flags used to track initialisation progress.
+     */
+    private boolean mTilesLoaded;
+
+    /**
+     * Whether the view has been shown at least once. With {@link #mTilesLoaded}, it's one of the 2
+     * flags used to track initialization progress.
+     */
+    private boolean mHasShownView;
+
+    private boolean mSearchProviderHasLogo = true;
+    private boolean mSearchProviderIsGoogle;
+    private boolean mShowingNonStandardGoogleLogo;
+
+    private boolean mInitialized;
+
+    private float mUrlFocusChangePercent;
+    private boolean mDisableUrlFocusChangeAnimations;
+
+    /** Flag used to request some layout changes after the next layout pass is completed. */
+    private boolean mTileCountChanged;
+
+    private boolean mSnapshotTileGridChanged;
+    private int mSearchBoxTwoSideMargin;
+
+    /**
+     * Vertical inset to add to the top and bottom of the search box bounds. May be 0 if no inset
+     * should be applied. See {@link Rect#inset(int, int)}.
+     */
+    private int mSearchBoxBoundsVerticalInset;
+
+    private FeedSurfaceScrollDelegate mScrollDelegate;
+
+    private boolean mIsTablet;
+    private @Nullable Supplier<Integer> mTabStripHeightSupplier;
+
+    private Callback<Logo> mOnLogoAvailableCallback;
+
+    // mIsComposeplateEnabled is null before checking whether to initialize composeplate view in
+    // NewTabPageCoordinator#initialize().
+    private @Nullable Boolean mIsComposeplateEnabled;
+    private boolean mIsComposeplatePolicyEnabled;
+    private boolean mIsComposeplateViewInitialized;
+    private Supplier<GURL> mComposeplateUrlSupplier;
+    private @Nullable ComposeplateCoordinator mComposeplateCoordinator;
+    // Previous visibility states for metrics.
+    private @Nullable Boolean mPreviousVoiceSearchButtonVisible;
+    private @Nullable Boolean mPreviousLensButtonVisible;
+    private SearchEngineUtils mSearchEngineUtils;
+    private final int mNtpSearchBoxTransitionStartOffset;
+    private final int mNtpSearchBoxTopMarginWithoutLogo;
+    private final boolean mEnableLogs;
+    private int mCurrentNtpFakeSearchBoxTransitionStartOffset;
+    private int mTopInset;
+    private @Nullable OnLayoutChangeListener mOnLayoutChangeListener;
+    // TODO(crbug.com/451602301): remove @Nullable and all null checks once
+    // ENABLE_SEAMLESS_SIGNIN is removed after the experiment.
+    private @Nullable NtpSigninPromoCoordinator mSigninPromoCoordinator;
+
+    private @Nullable Boolean mIsWhiteBackgroundOnSearchBoxApplied;
+
+    /**
+     * Constructor of the NewTabPageCoordinator.
+     *
+     * @param manager NewTabPageManager used to perform various actions when the user interacts with
+     *     the page.
+     * @param activity The activity that currently owns the new tab page
+     * @param newTabPageLayout The new tab page layout.
+     */
+    public NewTabPageCoordinator(
+            NewTabPageManager manager, Activity activity, NewTabPageLayout newTabPageLayout) {
+        mManager = manager;
+        mActivity = activity;
+        mNewTabPageLayout = newTabPageLayout;
+
+        Resources resources = mActivity.getResources();
+        mNtpSearchBoxTopMarginWithoutLogo =
+                resources.getDimensionPixelSize(R.dimen.mvt_container_top_margin);
+        mNtpSearchBoxTransitionStartOffset =
+                resources.getDimensionPixelSize(R.dimen.ntp_search_box_transition_start_offset);
+
+        mEnableLogs = ChromeFeatureList.sNewTabPageCustomizationV2EnableLogs.getValue();
+
+        mLayoutDelegate =
+                new NewTabPageLayout.Delegate() {
+                    @Override
+                    public void onMeasure(int width) {
+                        NewTabPageCoordinator.this.onMeasure(width);
+                    }
+
+                    @Override
+                    public void onAttachedToWindow() {
+                        NewTabPageCoordinator.this.onAttachedToWindow();
+                    }
+
+                    @Override
+                    public void updateActionButtonVisibility() {
+                        NewTabPageCoordinator.this.updateActionButtonVisibility();
+                    }
+                };
+        mNewTabPageLayout.setDelegate(mLayoutDelegate);
+    }
+
+    /**
+     * Initializes the NewTabPageLayout. This must be called immediately after inflation, before is
+     * object is used in any other way.
+     *
+     * @param tileGroupDelegate Delegate for {@link TileGroup}.
+     * @param searchProviderHasLogo Whether the search provider has a logo.
+     * @param searchProviderIsGoogle Whether the search provider is Google.
+     * @param scrollDelegate The delegate used to obtain information about scroll state.
+     * @param touchEnabledDelegate The {@link TouchEnabledDelegate} for handling whether touch
+     *     events are allowed.
+     * @param uiConfig UiConfig that provides display information about this view.
+     * @param lifecycleDispatcher Activity lifecycle dispatcher.
+     * @param profile The {@link Profile} associated with the NTP.
+     * @param windowAndroid An instance of a {@link WindowAndroid}.
+     * @param activityResultTracker Tracker of activity results.
+     * @param bottomSheetController Used to interact with the bottom sheet.
+     * @param snackbarManager Manages snackbars shown in the app.
+     * @param isTablet {@code true} if the NTP surface is in tablet mode.
+     * @param tabStripHeightSupplier Supplier of the tab strip height.
+     */
+    @Initializer
+    public void initialize(
+            TileGroup.Delegate tileGroupDelegate,
+            boolean searchProviderHasLogo,
+            boolean searchProviderIsGoogle,
+            FeedSurfaceScrollDelegate scrollDelegate,
+            TouchEnabledDelegate touchEnabledDelegate,
+            UiConfig uiConfig,
+            ActivityLifecycleDispatcher lifecycleDispatcher,
+            Profile profile,
+            WindowAndroid windowAndroid,
+            ActivityResultTracker activityResultTracker,
+            BottomSheetController bottomSheetController,
+            ModalDialogManager modalDialogManager,
+            SnackbarManager snackbarManager,
+            boolean isTablet,
+            Supplier<Integer> tabStripHeightSupplier,
+            Supplier<GURL> composeplateUrlSupplier) {
+        TraceEvent.begin(TAG + ".initialize()");
+        mScrollDelegate = scrollDelegate;
+        mProfile = profile;
+        mUiConfig = uiConfig;
+        mWindowAndroid = windowAndroid;
+        mActivityResultTracker = activityResultTracker;
+        mBottomSheetController = bottomSheetController;
+        mModalDialogManager = modalDialogManager;
+        mSnackbarManager = snackbarManager;
+        mIsTablet = isTablet;
+        mTabStripHeightSupplier = tabStripHeightSupplier;
+        mSearchEngineUtils = SearchEngineUtils.getForProfile(mProfile);
+        mComposeplateUrlSupplier = composeplateUrlSupplier;
+
+        if (mIsTablet) {
+            mDisplayStyleObserver = this::onDisplayStyleChanged;
+            mUiConfig.addObserver(mDisplayStyleObserver);
+        } else {
+            // On first run, the NewTabPageLayout is initialized behind the First Run Experience,
+            // meaning the UiConfig will pickup the screen layout then. However
+            // onConfigurationChanged is not called on orientation changes until the FRE is
+            // completed. This means that if a user starts the FRE in one orientation, changes an
+            // orientation and then leaves the FRE the UiConfig will have the wrong orientation.
+            // https://crbug.com/683886.
+            mUiConfig.updateDisplayStyle();
+        }
+
+        mSearchBoxCoordinator = new SearchBoxCoordinator(mActivity, mNewTabPageLayout, mIsTablet);
+        mSearchBoxCoordinator.initialize(
+                lifecycleDispatcher, mProfile.isOffTheRecord(), mWindowAndroid);
+
+        updateSearchBoxTwoSideMargin();
+        initializeLogoCoordinator();
+        setSearchProviderInfo(searchProviderHasLogo, searchProviderIsGoogle);
+        initializeMostVisitedTilesCoordinator(
+                mProfile, lifecycleDispatcher, tileGroupDelegate, touchEnabledDelegate);
+
+        mSearchEngineIconObserver = (newIcon) -> mSearchBoxCoordinator.setSearchEngineIcon(newIcon);
+        mSearchEngineUtils.addIconObserver(mSearchEngineIconObserver);
+        setSearchBoxTextAppearance();
+
+        initializeSearchBoxTextView();
+        initializeVoiceSearchButton();
+        initializeLensButton();
+
+        initializeComposeplateFlags(mProfile);
+        if (assumeNonNull(mIsComposeplateEnabled)) {
+            initializeComposeplate();
+        }
+
+        // This should be called after both mSearchBoxCoordinator and mComposeplateCoordinator are
+        // initialized.
+        onCustomizedBackgroundChanged(
+                NtpCustomizationUtils.shouldApplyWhiteBackgroundOnSearchBox());
+
+        // This should called after flags of composeplate view are initialized.
+        setSearchBoxHeightBoundsVerticalInset();
+
+        updateActionButtonVisibility();
+        initializeLayoutChangeListener();
+        if (SigninFeatureMap.isEnabled(SigninFeatures.ENABLE_SEAMLESS_SIGNIN)) {
+            initializeSigninPromoCoordinator();
+        }
+
+        // Initialize Searchbox observers
+        mSearchBoxHintTextObserver = this::onSearchBoxHintTextChanged;
+        mSearchEngineUtils.addSearchBoxHintTextObserver(mSearchBoxHintTextObserver);
+
+        // TODO(https://crbug.com/487641528): Destroy NewTabPageCoordinator in
+        // NewTabPage#destroy().
+        mManager.addDestructionObserver(NewTabPageCoordinator.this::onDestroy);
+        mInitialized = true;
+
+        TraceEvent.end(TAG + ".initialize()");
+    }
+
+    /** Sets the height of the search box and mSearchBoxBoundsVerticalInset. */
+    private void setSearchBoxHeightBoundsVerticalInset() {
+        Resources resources = mActivity.getResources();
+        int searchBoxHeight =
+                NtpCustomizationUtils.getSearchBoxHeightWithShadows(
+                        resources,
+                        assumeNonNull(mIsComposeplateEnabled),
+                        Boolean.TRUE.equals(mIsWhiteBackgroundOnSearchBoxApplied));
+        mSearchBoxCoordinator.setHeight(searchBoxHeight);
+
+        mSearchBoxBoundsVerticalInset =
+                Math.round(
+                        (searchBoxHeight
+                                        - resources.getDimensionPixelSize(
+                                                R.dimen.toolbar_height_no_shadow))
+                                / 2f);
+    }
+
+    public void reload() {
+        // TODO(crbug.com/41487877): Add handler in Magic Stack and dispatcher.
+    }
+
+    public void enableSearchBoxEditText(boolean enable) {
+        mSearchBoxCoordinator.enableSearchBoxEditText(enable);
+    }
+
+    /**
+     * @return The {@link FeedSurfaceScrollDelegate} for this class.
+     */
+    FeedSurfaceScrollDelegate getScrollDelegate() {
+        return mScrollDelegate;
+    }
+
+    /** Sets up the hint text and event handlers for the search box text view. */
+    private void initializeSearchBoxTextView() {
+        TraceEvent.begin(TAG + ".initializeSearchBoxTextView()");
+
+        mSearchBoxCoordinator.setSearchBoxClickListener(
+                v -> mManager.focusSearchBox(false, AutocompleteRequestType.SEARCH, null));
+
+        // @TODO(crbug.com/41492572): Add test case for search box OnDragListener.
+        mSearchBoxCoordinator.setSearchBoxDragListener(
+                new View.OnDragListener() {
+                    @Override
+                    public boolean onDrag(View view, DragEvent dragEvent) {
+                        // Disable search box EditText when browser content is dropped, its
+                        // re-enabled in {@link ChromeTabbedOnDragListener}, since a disabled view
+                        // will stop receiving further drag events. Given the child-first drag event
+                        // dispatch, disabling the TextView at ACTION_DRAG_STARTED is necessary to
+                        // prevent it from registering as a drop target and consuming the
+                        // ACTION_DROP event, thereby ensuring {@link ChromeTabbedOnDragListener}
+                        // receives it.
+                        if (MimeTypeUtils.clipDescriptionHasBrowserContent(
+                                        dragEvent.getClipDescription())
+                                && dragEvent.getAction() == DragEvent.ACTION_DRAG_STARTED) {
+                            enableSearchBoxEditText(false);
+                        }
+                        return false;
+                    }
+                });
+
+        mSearchBoxCoordinator.setSearchBoxTextWatcher(
+                new EmptyTextWatcher() {
+                    @Override
+                    public void afterTextChanged(Editable s) {
+                        if (s.length() == 0) return;
+                        mManager.focusSearchBox(
+                                false, AutocompleteRequestType.SEARCH, s.toString());
+                        mSearchBoxCoordinator.setSearchText("");
+                    }
+                });
+        TraceEvent.end(TAG + ".initializeSearchBoxTextView()");
+    }
+
+    public void onSearchBoxHintTextChanged() {
+        mSearchBoxCoordinator.setSearchBoxHintText(
+                mSearchEngineUtils.getOmniboxHintText(AutocompleteRequestType.SEARCH));
+    }
+
+    private void setSearchBoxTextAppearance() {
+        boolean shouldApplyWhiteBackground =
+                NtpCustomizationUtils.shouldApplyWhiteBackgroundOnSearchBox();
+
+        if (shouldApplyWhiteBackground) {
+            mSearchBoxCoordinator.setSearchBoxTextAppearance(
+                    R.style.TextAppearance_FakeSearchBoxTextMediumDark);
+        } else {
+            mSearchBoxCoordinator.setSearchBoxTextAppearance(
+                    R.style.TextAppearance_FakeSearchBoxTextMedium);
+        }
+    }
+
+    private void initializeVoiceSearchButton() {
+        TraceEvent.begin(TAG + ".initializeVoiceSearchButton()");
+        View.OnClickListener voiceSearchButtonClickListener =
+                v -> mManager.focusSearchBox(true, AutocompleteRequestType.SEARCH, null);
+        mSearchBoxCoordinator.addVoiceSearchButtonClickListener(voiceSearchButtonClickListener);
+        TraceEvent.end(TAG + ".initializeVoiceSearchButton()");
+    }
+
+    private void initializeLensButton() {
+        TraceEvent.begin(TAG + ".initializeLensButton()");
+        View.OnClickListener lensButtonClickListener =
+                v -> {
+                    LensMetrics.recordClicked(LensEntryPoint.NEW_TAB_PAGE);
+                    mSearchBoxCoordinator.startLens(LensEntryPoint.NEW_TAB_PAGE);
+                };
+        mSearchBoxCoordinator.addLensButtonClickListener(lensButtonClickListener);
+        TraceEvent.end(TAG + ".initializeLensButton()");
+    }
+
+    private void initializeComposeplateFlags(Profile profile) {
+        mIsComposeplateEnabled = ComposeplateUtils.isComposeplateEnabled(mIsTablet, profile);
+        mIsComposeplatePolicyEnabled =
+                mIsComposeplateEnabled && ComposeplateUtils.isEnabledByPolicy(profile);
+    }
+
+    private void initializeComposeplate() {
+        if (mIsComposeplateViewInitialized) return;
+
+        mIsComposeplateViewInitialized = true;
+
+        boolean shouldApplyWhiteBackgroundOnSearchBox =
+                NtpCustomizationUtils.shouldApplyWhiteBackgroundOnSearchBox();
+
+        ViewStub composeplateViewStub =
+                mNewTabPageLayout.findViewById(R.id.composeplate_view_v2_stub);
+        ViewGroup composeplateView = (ViewGroup) composeplateViewStub.inflate();
+        if (NtpCustomizationUtils.isNtpThemeCustomizationEnabled()) {
+            // TODO(https://crbug.com/423579377): Moves the layout parameters to
+            //  composeplate_view_layout_v2.xml after the feature NewTabPageCustomizationV2 is
+            //  launched.
+            NewTabPageUtils.applyUpdatedLayoutParamsForComposeplateView(composeplateView);
+        }
+        mComposeplateCoordinator = new ComposeplateCoordinator(composeplateView, mProfile);
+        mComposeplateCoordinator.setIncognitoClickListener(this::onIncognitoButtonClicked);
+        // Don't log click metrics in this listener, since the mComposeplateCoordinator will
+        // log.
+        mComposeplateCoordinator.setComposeplateButtonClickListener(
+                this::onComposeplateButtonClicked);
+
+        if (shouldApplyWhiteBackgroundOnSearchBox) {
+            // It is safe to call mComposeplateCoordinator.applyWhiteBackgroundWithShadow() again
+            // since it is no-op if the white background has been applied.
+            mComposeplateCoordinator.applyWhiteBackgroundWithShadow(/* apply= */ true);
+        }
+    }
+
+    private void onComposeplateButtonClicked(View view) {
+        if (OmniboxFeatures.sOmniboxMultimodalInput.isEnabled()
+                && OmniboxFeatures.sRedirectComposeplateButton.getValue()
+                && !mIsTablet
+                && mIsComposeplatePolicyEnabled) {
+            mManager.focusSearchBox(false, AutocompleteRequestType.AI_MODE, null);
+            return;
+        }
+
+        GURL composeplateUrl = mComposeplateUrlSupplier.get();
+        if (composeplateUrl == null) return;
+
+        mManager.getNativePageHost()
+                .loadUrl(new LoadUrlParams(composeplateUrl), /* incognito= */ false);
+    }
+
+    private void onIncognitoButtonClicked(View view) {
+        if (!IncognitoUtils.isIncognitoModeEnabled(mProfile)) return;
+
+        UrlConstantResolver resolver = UrlConstantResolverFactory.getForProfile(mProfile);
+        mManager.getNativePageHost().loadUrl(new LoadUrlParams(resolver.getNtpUrl()), true);
+    }
+
+    private void initializeLayoutChangeListener() {
+        TraceEvent.begin(TAG + ".initializeLayoutChangeListener()");
+        mOnLayoutChangeListener = this::onLayoutChanged;
+        mNewTabPageLayout.addOnLayoutChangeListener(mOnLayoutChangeListener);
+        TraceEvent.end(TAG + ".initializeLayoutChangeListener()");
+    }
+
+    private void onLayoutChanged(
+            View view,
+            int left,
+            int top,
+            int right,
+            int bottom,
+            int oldLeft,
+            int oldTop,
+            int oldRight,
+            int oldBottom) {
+        int oldHeight = oldBottom - oldTop;
+        int newHeight = bottom - top;
+
+        if (oldHeight == newHeight && !mTileCountChanged) return;
+        mTileCountChanged = false;
+
+        // Re-apply the url focus change amount after a rotation to ensure the views are
+        // correctly placed with their new layout configurations.
+        onUrlFocusAnimationChanged();
+        updateSearchBoxOnScroll();
+
+        // The positioning of elements may have been changed (since the elements expand
+        // to fill the available vertical space), so adjust the scroll.
+        if (mScrollDelegate.isScrollViewInitialized()) mScrollDelegate.snapScroll();
+    }
+
+    private void initializeLogoCoordinator() {
+        Callback<LoadUrlParams> logoClickedCallback =
+                mCallbackController.makeCancelable(
+                        (urlParams) -> {
+                            mManager.getNativePageHost().loadUrl(urlParams, /* incognito= */ false);
+                            BrowserUiUtils.recordModuleClickHistogram(
+                                    ModuleTypeOnStartAndNtp.DOODLE);
+                        });
+        mOnLogoAvailableCallback =
+                mCallbackController.makeCancelable(
+                        (logo) -> {
+                            mSnapshotTileGridChanged = true;
+                            mShowingNonStandardGoogleLogo = logo != null && mSearchProviderIsGoogle;
+                            NtpCustomizationConfigManager.getInstance()
+                                    .setDefaultSearchEngineLogoBitmap(
+                                            logo == null ? null : logo.image);
+                        });
+
+        mLogoCoordinator =
+                new LogoCoordinator(
+                        mActivity,
+                        logoClickedCallback,
+                        mNewTabPageLayout,
+                        mOnLogoAvailableCallback,
+                        /* visibilityObserver= */ null,
+                        () -> MultiWindowUtils.getInstance().isInMultiWindowMode(mActivity));
+        mLogoCoordinator.initWithNative(mProfile);
+    }
+
+    private void initializeMostVisitedTilesCoordinator(
+            Profile profile,
+            ActivityLifecycleDispatcher activityLifecycleDispatcher,
+            TileGroup.Delegate tileGroupDelegate,
+            TouchEnabledDelegate touchEnabledDelegate) {
+        View mvTilesContainerLayout = mNewTabPageLayout.findViewById(R.id.mv_tiles_container);
+        assert mvTilesContainerLayout != null;
+
+        mMostVisitedTilesCoordinator =
+                new MostVisitedTilesCoordinator(
+                        mActivity,
+                        activityLifecycleDispatcher,
+                        mvTilesContainerLayout,
+                        () -> mSnapshotTileGridChanged = true,
+                        () -> {
+                            if (mUrlFocusChangePercent == 1f) mTileCountChanged = true;
+                        });
+
+        mMostVisitedTilesCoordinator.initWithNative(
+                profile, mManager, tileGroupDelegate, touchEnabledDelegate);
+
+        if (ChromeFeatureList.sNewTabPageCustomizationForMvt.isEnabled()) {
+            mMostVisitedTilesCoordinator.updateMvtVisibility();
+        }
+    }
+
+    private void initializeSigninPromoCoordinator() {
+        ViewStub signinPromoViewContainerStub =
+                mNewTabPageLayout.findViewById(R.id.signin_promo_view_container_stub);
+        mSigninPromoCoordinator =
+                new NtpSigninPromoCoordinator(
+                        mWindowAndroid,
+                        mActivity,
+                        mProfile,
+                        mActivityResultTracker,
+                        SigninAndHistorySyncActivityLauncherImpl.get(),
+                        mBottomSheetController,
+                        mModalDialogManager,
+                        mSnackbarManager,
+                        DeviceLockActivityLauncherImpl.get(),
+                        signinPromoViewContainerStub,
+                        SetupListModuleUtils::isSetupListActive);
+    }
+
+    /** Updates the search box when the parent view's scroll position is changed. */
+    void updateSearchBoxOnScroll() {
+        if (mDisableUrlFocusChangeAnimations) return;
+
+        // When the page changes (tab switching or new page loading), it is possible that events
+        // (e.g. delayed view change notifications) trigger calls to these methods after
+        // the current page changes. We check it again to make sure we don't attempt to update the
+        // wrong page.
+        if (!mManager.isCurrentPage()) return;
+
+        if (mSearchBoxScrollListener != null) {
+            mSearchBoxScrollListener.onNtpScrollChanged(getToolbarTransitionPercentage());
+        }
+    }
+
+    /**
+     * Calculates the percentage (between 0 and 1) of the transition from the search box to the
+     * omnibox at the top of the New Tab Page, which is determined by the amount of scrolling and
+     * the position of the search box.
+     *
+     * @return the transition percentage
+     */
+    float getToolbarTransitionPercentage() {
+        return mSearchBoxCoordinator.getToolbarTransitionPercentage(
+                mScrollDelegate,
+                mTabStripHeightSupplier,
+                mCurrentNtpFakeSearchBoxTransitionStartOffset);
+    }
+
+    /**
+     * @return The fake search box view.
+     */
+    public View getSearchBoxView() {
+        return mSearchBoxCoordinator.getView();
+    }
+
+    public void onSwitchToForeground() {
+        if (mMostVisitedTilesCoordinator != null) {
+            mMostVisitedTilesCoordinator.onSwitchToForeground();
+        }
+    }
+
+    /**
+     * Should be called every time one of the flags used to track initialization progress changes.
+     * Finalizes initialization once all the preliminary steps are complete.
+     *
+     * @see #mHasShownView
+     * @see #mTilesLoaded
+     */
+    private void onInitializationProgressChanged() {
+        if (!hasLoadCompleted()) return;
+
+        mManager.onLoadingComplete();
+
+        // Load the logo after everything else is finished, since it's lower priority.
+        if (mLogoCoordinator != null) mLogoCoordinator.loadSearchProviderLogoWithAnimation();
+    }
+
+    /**
+     * To be called to notify that the tiles have finished loading. Will do nothing if a load was
+     * previously completed.
+     */
+    public void onTilesLoaded() {
+        if (mTilesLoaded) return;
+        mTilesLoaded = true;
+
+        onInitializationProgressChanged();
+    }
+
+    /**
+     * Changes the layout depending on whether the selected search provider (e.g. Google, Bing) has
+     * a logo.
+     *
+     * @param hasLogo Whether the search provider has a logo.
+     * @param isGoogle Whether the search provider is Google.
+     */
+    void setSearchProviderInfo(boolean hasLogo, boolean isGoogle) {
+        if (hasLogo == mSearchProviderHasLogo
+                && isGoogle == mSearchProviderIsGoogle
+                && mInitialized) {
+            return;
+        }
+        boolean isSearchProviderIsGoogleChanged = mSearchProviderIsGoogle != isGoogle;
+        mSearchProviderHasLogo = hasLogo;
+        mSearchProviderIsGoogle = isGoogle;
+
+        if (!mSearchProviderIsGoogle) {
+            mShowingNonStandardGoogleLogo = false;
+        }
+
+        setSearchProviderTopMargin();
+        setLogoViewBottomMargin();
+
+        updateTilesLayoutMargins();
+
+        // Hide or show the views above the most visited tiles as needed, e.g, spacers. The
+        // visibility of Logo is handled by LogoCoordinator.
+        setSearchBoxTextAppearance();
+
+        // Skips if the flag hasn't been initialized since the initialization of the following
+        // components will be called again in #initialize().
+        if (mIsComposeplateEnabled != null) {
+            // when mSearchProviderIsGoogle is changed, mIsComposeplateEnabled might be changed too,
+            // recalculate its value.
+            if (isSearchProviderIsGoogleChanged) {
+                boolean previousIsComposeplateEnabled = mIsComposeplateEnabled;
+                initializeComposeplateFlags(mProfile);
+                if (!previousIsComposeplateEnabled
+                        && mIsComposeplateEnabled
+                        && mComposeplateCoordinator == null) {
+                    // If the composeplate view is enabled while mComposeplateCoordinator hasn't
+                    // been initialized yet, initialize it now.
+                    initializeComposeplate();
+                }
+
+                if (previousIsComposeplateEnabled != mIsComposeplateEnabled) {
+                    // When the flag value is changed, the height of search box might be changed.
+                    setSearchBoxHeightBoundsVerticalInset();
+                    // Updates the composeplate view's visibility.
+                    updateActionButtonVisibility();
+                }
+            }
+        }
+
+        onUrlFocusAnimationChanged();
+
+        mSnapshotTileGridChanged = true;
+    }
+
+    /** Updates the margins for the most visited tiles layout based on what is shown above it. */
+    private void updateTilesLayoutMargins() {
+        if (mMostVisitedTilesCoordinator == null) return;
+
+        mMostVisitedTilesCoordinator.updateTilesLayoutMargins(
+                shouldShowLogo(),
+                mIsWhiteBackgroundOnSearchBoxApplied == null
+                        ? false
+                        : mIsWhiteBackgroundOnSearchBoxApplied,
+                mIsTablet);
+    }
+
+    /**
+     * Updates whether the NewTabPage should animate on URL focus changes.
+     *
+     * @param disable Whether to disable the animations.
+     */
+    void setUrlFocusAnimationsDisabled(boolean disable) {
+        if (disable == mDisableUrlFocusChangeAnimations) return;
+        mDisableUrlFocusChangeAnimations = disable;
+        if (!disable) onUrlFocusAnimationChanged();
+    }
+
+    /**
+     * @return Whether URL focus animations are currently disabled.
+     */
+    boolean urlFocusAnimationsDisabled() {
+        return mDisableUrlFocusChangeAnimations;
+    }
+
+    /**
+     * Specifies the percentage the URL is focused during an animation. 1.0 specifies that the URL
+     * bar has focus and has completed the focus animation. 0 is when the URL bar is does not have
+     * any focus.
+     *
+     * @param percent The percentage of the URL bar focus animation.
+     */
+    void setUrlFocusChangeAnimationPercent(float percent) {
+        mUrlFocusChangePercent = percent;
+        onUrlFocusAnimationChanged();
+    }
+
+    /**
+     * @return The percentage that the URL bar is focused during an animation.
+     */
+    @VisibleForTesting
+    float getUrlFocusChangeAnimationPercent() {
+        return mUrlFocusChangePercent;
+    }
+
+    void onUrlFocusAnimationChanged() {
+        /*
+         * Avoid Y-translation when animation is disabled, view is moving or on tablet form-factor.
+         * Context for tablets - Unlike phones, this method is not called on tablets during URL
+         * focus post NTP load. However when physical keyboard is present, we try to auto-focus URL
+         * during page load causing this method to be called. Disabling this for all cases on this
+         * form-factor since this translation does not WAI. (see crbug.com/40910640)
+         */
+        if (mDisableUrlFocusChangeAnimations || mIsTablet) return;
+
+        // Translate so that the search box is at the top, but only upwards.
+        int basePosition =
+                mScrollDelegate.getVerticalScrollOffset() + mNewTabPageLayout.getPaddingTop();
+        int target =
+                Math.max(
+                        basePosition,
+                        getSearchBoxView().getBottom()
+                                - getSearchBoxView().getPaddingBottom()
+                                - mSearchBoxBoundsVerticalInset);
+
+        float translationY = mUrlFocusChangePercent * (basePosition - target);
+        setTranslationYOfFakeboxAndAbove(translationY);
+    }
+
+    /**
+     * Sets the translation_y of the fakebox and all views above it, but not the views below. Used
+     * when the url focus animation is combined with the omnibox suggestions list animation to
+     * reduce the number of visual elements in motion.
+     */
+    private void setTranslationYOfFakeboxAndAbove(float translationY) {
+        for (int i = 0; i < mNewTabPageLayout.getChildCount(); i++) {
+            View view = mNewTabPageLayout.getChildAt(i);
+            view.setTranslationY(translationY);
+            if (view.getId() == R.id.search_box) return;
+        }
+    }
+
+    /**
+     * Updates the opacity of the search box when scrolling.
+     *
+     * @param alpha opacity (alpha) value to use.
+     */
+    public void setSearchBoxAlpha(float alpha) {
+        mSearchBoxCoordinator.setAlpha(alpha);
+    }
+
+    /**
+     * Updates the opacity of the search provider logo when scrolling.
+     *
+     * @param alpha opacity (alpha) value to use.
+     */
+    public void setSearchProviderLogoAlpha(float alpha) {
+        if (mLogoCoordinator != null) mLogoCoordinator.setAlpha(alpha);
+    }
+
+    /**
+     * Get the bounds of the search box in relation to the top level {@code parentView}.
+     *
+     * @param bounds The current drawing location of the search box.
+     * @param translation The translation applied to the search box by the parent view hierarchy up
+     *     to the {@code parentView}.
+     * @param parentView The top level parent view used to translate search box bounds.
+     */
+    void getSearchBoxBounds(Rect bounds, Point translation, View parentView) {
+        mSearchBoxCoordinator.getSearchBoxBounds(
+                bounds, translation, parentView, mScrollDelegate, mSearchBoxBoundsVerticalInset);
+    }
+
+    /** Returns the fake search box's transition start offset on NTP. */
+    private int getNtpSearchBoxTransitionStartOffset(boolean showFakeSearchBoxWithoutLogo) {
+        if (mIsTablet && showFakeSearchBoxWithoutLogo) {
+            // On tablets, it is possible to show fake search box if DSE doesn't have logo if DSE
+            // mobile parity v2 is enabled. The mNTPFakeSearchBoxTransitionStartOffset is used to
+            // calculate scrolling percentage in getToolbarTransitionPercentage(). Reset to 0 when
+            // no doodle is shown for 3p DSE to prevent the alpha of fake search box being set to 0
+            // (transparent) by ToolbarTablet#updateNtp().
+            return 0;
+        } else {
+            return mNtpSearchBoxTransitionStartOffset;
+        }
+    }
+
+    private void setSearchProviderTopMargin() {
+        boolean showFakeSearchBoxWithoutLogo = !mSearchProviderHasLogo;
+        mCurrentNtpFakeSearchBoxTransitionStartOffset =
+                getNtpSearchBoxTransitionStartOffset(showFakeSearchBoxWithoutLogo);
+
+        int topMargin = showFakeSearchBoxWithoutLogo ? mNtpSearchBoxTopMarginWithoutLogo : 0;
+        mSearchBoxCoordinator.setTopMargin(topMargin);
+
+        if (mLogoCoordinator != null) {
+            mLogoCoordinator.setTopMargin(getLogoTopMargin());
+        }
+    }
+
+    private void setLogoViewBottomMargin() {
+        if (mLogoCoordinator == null) return;
+
+        boolean shouldShowShadow = Boolean.TRUE.equals(mIsWhiteBackgroundOnSearchBoxApplied);
+        int logoViewBottomMarginPx =
+                NtpCustomizationUtils.getLogoViewBottomMarginPx(
+                        mActivity.getResources(), shouldShowShadow);
+        mLogoCoordinator.setBottomMargin(logoViewBottomMarginPx);
+    }
+
+    private int getLogoTopMargin() {
+        Resources resources = mActivity.getResources();
+
+        if (mShowingNonStandardGoogleLogo && mSearchProviderHasLogo) {
+            return LogoUtils.getTopMarginForDoodle(resources);
+        }
+
+        return resources.getDimensionPixelSize(R.dimen.ntp_logo_margin_top);
+    }
+
+    /**
+     * Sets the listener for search box scroll changes.
+     *
+     * @param listener The listener to be notified on changes.
+     */
+    void setSearchBoxScrollListener(@Nullable OnSearchBoxScrollListener listener) {
+        mSearchBoxScrollListener = listener;
+        if (mSearchBoxScrollListener != null) updateSearchBoxOnScroll();
+    }
+
+    // NewTabPageLayout.Delegate implementations.
+    public void onMeasure(int width) {
+        if (mIsTablet && mMostVisitedTilesCoordinator != null) {
+            mMostVisitedTilesCoordinator.calculateTabletMvtWidth(width);
+        }
+
+        unifyElementWidths(width);
+    }
+
+    public void onAttachedToWindow() {
+        if (!mHasShownView) {
+            mHasShownView = true;
+            onInitializationProgressChanged();
+            TraceEvent.instant("NewTabPageSearchAvailable");
+        }
+    }
+
+    /** Update the visibility of the action buttons. */
+    public void updateActionButtonVisibility() {
+        boolean shouldShowVoiceSearchButton = mManager.isVoiceSearchEnabled();
+        boolean shouldShowLensButton =
+                mSearchBoxCoordinator.isLensEnabled(LensEntryPoint.NEW_TAB_PAGE);
+        if (mIsComposeplateEnabled == null) return;
+
+        mSearchBoxCoordinator.setVoiceSearchButtonVisibility(shouldShowVoiceSearchButton);
+        mSearchBoxCoordinator.setLensButtonVisibility(shouldShowLensButton);
+        boolean shouldShowComposeplateButton = false;
+        // As long as mComposeplateCoordinator has been initialized, we should update its
+        // visibility.
+        if (mComposeplateCoordinator != null) {
+            shouldShowComposeplateButton =
+                    mIsComposeplateEnabled
+                            && mSearchProviderIsGoogle
+                            && IncognitoUtils.isIncognitoModeEnabled(mProfile);
+            mComposeplateCoordinator.setVisibility(
+                    shouldShowComposeplateButton, mManager.isCurrentPage());
+        }
+        updatePreviousButtonVisibilityAndRecordMetrics(
+                shouldShowVoiceSearchButton, shouldShowLensButton, shouldShowComposeplateButton);
+    }
+
+    /**
+     * Updates the previous visibility state of the voice search and lens buttons and records
+     * metrics if their visibility has changed on the current page.
+     *
+     * @param isVoiceSearchButtonVisible The new visibility state of the voice search button.
+     * @param isLensButtonVisible The new visibility state of the lens button.
+     * @param isComposeplateButtonVisible The new visibility state of the composeplate button.
+     */
+    private void updatePreviousButtonVisibilityAndRecordMetrics(
+            boolean isVoiceSearchButtonVisible,
+            boolean isLensButtonVisible,
+            boolean isComposeplateButtonVisible) {
+        if (!mManager.isCurrentPage()
+                || (mPreviousVoiceSearchButtonVisible != null
+                        && isVoiceSearchButtonVisible == mPreviousVoiceSearchButtonVisible
+                        && mPreviousLensButtonVisible != null
+                        && isLensButtonVisible == mPreviousLensButtonVisible)) {
+            return;
+        }
+
+        if (mPreviousLensButtonVisible == null
+                || isLensButtonVisible != mPreviousLensButtonVisible) {
+            LensMetrics.recordShown(LensEntryPoint.NEW_TAB_PAGE, isLensButtonVisible);
+        }
+
+        ComposeplateMetricsUtils.recordFakeSearchBoxImpression2();
+        ComposeplateMetricsUtils.recordFakeSearchBoxComposeplateButtonImpression2(
+                isComposeplateButtonVisible);
+
+        mPreviousVoiceSearchButtonVisible = isVoiceSearchButtonVisible;
+        mPreviousLensButtonVisible = isLensButtonVisible;
+    }
+
+    /**
+     * @see InvalidationAwareThumbnailProvider#shouldCaptureThumbnail()
+     */
+    public boolean shouldCaptureThumbnail() {
+        return mSnapshotTileGridChanged;
+    }
+
+    /**
+     * Should be called before a thumbnail of the parent view is captured.
+     *
+     * @see InvalidationAwareThumbnailProvider#captureThumbnail(Canvas)
+     */
+    public void onPreCaptureThumbnail() {
+        if (mLogoCoordinator != null) mLogoCoordinator.endFadeAnimation();
+        mSnapshotTileGridChanged = false;
+    }
+
+    private boolean shouldShowLogo() {
+        return mSearchProviderHasLogo;
+    }
+
+    private boolean hasLoadCompleted() {
+        return mHasShownView && mTilesLoaded;
+    }
+
+    @SuppressWarnings("NullAway")
+    private void onDestroy() {
+        if (mCallbackController != null) {
+            mCallbackController.destroy();
+            mCallbackController = null;
+        }
+
+        if (mLogoCoordinator != null) {
+            mLogoCoordinator.destroy();
+            mLogoCoordinator = null;
+        }
+
+        mSearchBoxCoordinator.destroy();
+        mSearchBoxCoordinator = null;
+
+        if (mMostVisitedTilesCoordinator != null) {
+            mMostVisitedTilesCoordinator.destroyMvtiles();
+            mMostVisitedTilesCoordinator = null;
+        }
+
+        if (mIsTablet) {
+            mUiConfig.removeObserver(mDisplayStyleObserver);
+            mDisplayStyleObserver = null;
+        }
+
+        if (mSearchEngineUtils != null) {
+            if (mSearchBoxHintTextObserver != null) {
+                mSearchEngineUtils.removeSearchBoxHintTextObserver(mSearchBoxHintTextObserver);
+                mSearchBoxHintTextObserver = null;
+            }
+            if (mSearchEngineIconObserver != null) {
+                mSearchEngineUtils.removeIconObserver(mSearchEngineIconObserver);
+                mSearchEngineIconObserver = null;
+            }
+            mSearchEngineUtils = null;
+        }
+
+        mNewTabPageLayout.removeOnLayoutChangeListener(mOnLayoutChangeListener);
+        mOnLayoutChangeListener = null;
+        mNewTabPageLayout.setDelegate(null);
+
+        if (mComposeplateCoordinator != null) {
+            mComposeplateCoordinator.destroy();
+            mComposeplateCoordinator = null;
+        }
+
+        if (mSigninPromoCoordinator != null) {
+            mSigninPromoCoordinator.destroy();
+            mSigninPromoCoordinator = null;
+        }
+
+        mSearchBoxScrollListener = null;
+        mComposeplateUrlSupplier = null;
+    }
+
+    /** Makes the Search Box and Logo as wide as Most Visited. */
+    private void unifyElementWidths(int width) {
+        int searchBoxWidth = width - mSearchBoxTwoSideMargin;
+        if (mSearchBoxCoordinator != null) {
+            mSearchBoxCoordinator.setLayoutWidth(searchBoxWidth);
+        }
+
+        if (mLogoCoordinator != null) {
+            mLogoCoordinator.setLayoutWidth(width);
+        }
+
+        if (mComposeplateCoordinator != null) {
+            mComposeplateCoordinator.setLayoutWidth(searchBoxWidth);
+        }
+    }
+
+    LogoCoordinator getLogoCoordinatorForTesting() {
+        return mLogoCoordinator;
+    }
+
+    private void onDisplayStyleChanged(UiConfig.DisplayStyle newDisplayStyle) {
+        if (!mIsTablet) return;
+
+        updateDoodleOnTablet();
+        if (mMostVisitedTilesCoordinator != null) {
+            mMostVisitedTilesCoordinator.updateMvtOnTablet();
+        }
+        updateSearchBoxTwoSideMargin();
+    }
+
+    /**
+     * Adjusts the doodle size while the tablet transitions to or from a multi-screen layout,
+     * ensuring the change occurs post-logo initialization.
+     */
+    private void updateDoodleOnTablet() {
+        if (!mIsTablet || mLogoCoordinator == null) return;
+
+        mLogoCoordinator.updateDoodleOnTablet(mShowingNonStandardGoogleLogo);
+    }
+
+    private void updateSearchBoxTwoSideMargin() {
+        mSearchBoxTwoSideMargin =
+                NtpCustomizationUtils.getSearchBoxTwoSideMargin(
+                        mActivity.getResources(), mUiConfig, mIsTablet);
+    }
+
+    /**
+     * Called when the layout changes between edge-to-edge and standard.
+     *
+     * @param systemTopInset The system's top inset, i.e., the height of the Status bar. It is
+     *     always bigger than 0.
+     * @param supportsEdgeToEdgeOnTop Determines if the NTP should consume this top inset, extending
+     *     itself to the Status bar area.
+     */
+    void onToEdgeChange(int systemTopInset, boolean supportsEdgeToEdgeOnTop) {
+        // Exits early if the NTP's top padding doesn't require adjustment.
+        if (NtpCustomizationUtils.shouldSkipTopInsetsChange(
+                mTopInset, systemTopInset, supportsEdgeToEdgeOnTop)) {
+            return;
+        }
+
+        mTopInset = supportsEdgeToEdgeOnTop ? systemTopInset : 0;
+        mCurrentNtpFakeSearchBoxTransitionStartOffset =
+                getNtpSearchBoxTransitionStartOffset(!mSearchProviderHasLogo) + mTopInset;
+
+        int toolbarHeightNoShadow =
+                mActivity.getResources().getDimensionPixelSize(R.dimen.toolbar_height_no_shadow);
+        // Top padding is applied to the NTP layout, ensuring all UI components remain in their
+        // original positions after Status bar is hidden.
+        mNewTabPageLayout.setPaddingRelative(
+                mNewTabPageLayout.getPaddingStart(),
+                toolbarHeightNoShadow + mTopInset,
+                mNewTabPageLayout.getPaddingEnd(),
+                mNewTabPageLayout.getPaddingBottom());
+
+        if (mEnableLogs) {
+            Log.i(TAG, "The top padding to add on the NTP is %d.", mTopInset);
+        }
+    }
+
+    /**
+     * Called when a customized background image is selected or deselected.
+     *
+     * @param applyWhiteBackgroundOnSearchBox Whether to apply a white background color to the fake
+     *     search box.
+     */
+    void onCustomizedBackgroundChanged(boolean applyWhiteBackgroundOnSearchBox) {
+        // If shouldn't apply a white background and the background hasn't been updated before,
+        // returns now.
+        if (mIsWhiteBackgroundOnSearchBoxApplied == null && !applyWhiteBackgroundOnSearchBox) {
+            return;
+        }
+
+        // If composeplate view flags haven't been initialized yet, returns now.
+        if (mIsComposeplateEnabled == null) {
+            return;
+        }
+
+        // If the background has been updated before and it should remain the same, returns now.
+        if (mIsWhiteBackgroundOnSearchBoxApplied != null
+                && mIsWhiteBackgroundOnSearchBoxApplied == applyWhiteBackgroundOnSearchBox) {
+            return;
+        }
+
+        // If the fake search box hasn't been initialized, returns now. It is fine to skip here
+        // because applyWhiteBackgroundWithShadow() will be called immediately after the
+        // mSearchBoxCoordinator is initialized.
+        if (mSearchBoxCoordinator == null) return;
+
+        mIsWhiteBackgroundOnSearchBoxApplied = applyWhiteBackgroundOnSearchBox;
+
+        if (mSearchBoxCoordinator != null) {
+            mSearchBoxCoordinator.applyWhiteBackgroundWithShadow(applyWhiteBackgroundOnSearchBox);
+        }
+
+        if (mComposeplateCoordinator != null) {
+            mComposeplateCoordinator.applyWhiteBackgroundWithShadow(
+                    applyWhiteBackgroundOnSearchBox);
+        }
+
+        setLogoViewBottomMargin();
+        // Update the search box height and bounds vertical inset since the shadow padding changed.
+        setSearchBoxHeightBoundsVerticalInset();
+
+        if (mMostVisitedTilesCoordinator != null) {
+            updateTilesLayoutMargins();
+        }
+    }
+
+    /** Returns the top inset of the NTP. */
+    int getTopInset() {
+        return mTopInset;
+    }
+
+    /** Returns the vertical inset applied to search box bounds. */
+    int getSearchBoxBoundsVerticalInset() {
+        return mSearchBoxBoundsVerticalInset;
+    }
+
+    NewTabPageLayout getNewTabPageLayout() {
+        return mNewTabPageLayout;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
index b3d83a7..cea1761 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
@@ -1,83 +1,20 @@
-// Copyright 2015 The Chromium Authors
+// Copyright 2026 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.ntp;
 
-import static org.chromium.build.NullUtil.assumeNonNull;
-
-import android.app.Activity;
 import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.text.Editable;
 import android.util.AttributeSet;
-import android.view.DragEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.LinearLayout;
 
-import androidx.annotation.VisibleForTesting;
-
-import org.chromium.base.Callback;
-import org.chromium.base.CallbackController;
 import org.chromium.base.Log;
-import org.chromium.base.TraceEvent;
-import org.chromium.build.annotations.Initializer;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.composeplate.ComposeplateCoordinator;
-import org.chromium.chrome.browser.composeplate.ComposeplateMetricsUtils;
-import org.chromium.chrome.browser.composeplate.ComposeplateUtils;
-import org.chromium.chrome.browser.device_lock.DeviceLockActivityLauncherImpl;
-import org.chromium.chrome.browser.feed.FeedSurfaceScrollDelegate;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.incognito.IncognitoUtils;
-import org.chromium.chrome.browser.lens.LensEntryPoint;
-import org.chromium.chrome.browser.lens.LensMetrics;
-import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
-import org.chromium.chrome.browser.logo.LogoBridge.Logo;
-import org.chromium.chrome.browser.logo.LogoCoordinator;
-import org.chromium.chrome.browser.logo.LogoUtils;
-import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
-import org.chromium.chrome.browser.ntp.NewTabPage.OnSearchBoxScrollListener;
-import org.chromium.chrome.browser.ntp.search.SearchBoxCoordinator;
-import org.chromium.chrome.browser.ntp_customization.NtpCustomizationConfigManager;
-import org.chromium.chrome.browser.ntp_customization.NtpCustomizationUtils;
-import org.chromium.chrome.browser.omnibox.SearchEngineUtils;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.setup_list.SetupListModuleUtils;
-import org.chromium.chrome.browser.signin.SigninAndHistorySyncActivityLauncherImpl;
-import org.chromium.chrome.browser.suggestions.tile.MostVisitedTilesCoordinator;
-import org.chromium.chrome.browser.suggestions.tile.TileGroup;
-import org.chromium.chrome.browser.suggestions.tile.TileGroup.Delegate;
-import org.chromium.chrome.browser.tab_ui.InvalidationAwareThumbnailProvider;
-import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
-import org.chromium.chrome.browser.ui.native_page.TouchEnabledDelegate;
-import org.chromium.chrome.browser.ui.signin.signin_promo.NtpSigninPromoCoordinator;
-import org.chromium.chrome.browser.url_constants.UrlConstantResolver;
-import org.chromium.chrome.browser.url_constants.UrlConstantResolverFactory;
-import org.chromium.chrome.browser.util.BrowserUiUtils;
-import org.chromium.chrome.browser.util.BrowserUiUtils.ModuleTypeOnStartAndNtp;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
-import org.chromium.components.browser_ui.widget.displaystyle.DisplayStyleObserver;
-import org.chromium.components.browser_ui.widget.displaystyle.UiConfig;
-import org.chromium.components.omnibox.AutocompleteRequestType;
-import org.chromium.components.omnibox.OmniboxFeatures;
-import org.chromium.components.signin.SigninFeatureMap;
-import org.chromium.components.signin.SigninFeatures;
-import org.chromium.content_public.browser.LoadUrlParams;
-import org.chromium.ui.base.ActivityResultTracker;
-import org.chromium.ui.base.MimeTypeUtils;
-import org.chromium.ui.base.WindowAndroid;
-import org.chromium.ui.modaldialog.ModalDialogManager;
-import org.chromium.ui.text.EmptyTextWatcher;
-import org.chromium.url.GURL;
-
-import java.util.function.Supplier;
 
 /**
  * Layout for the new tab page. This positions the page elements in the correct vertical positions.
@@ -85,103 +22,29 @@
  */
 @NullMarked
 public class NewTabPageLayout extends LinearLayout {
+    /** Delegate to handle layout-related events. */
+    public interface Delegate {
+        /**
+         * Called during the {@link #onMeasure(int, int)} pass.
+         *
+         * @param width The measured width of the layout.
+         */
+        void onMeasure(int width);
+
+        /** Called when the layout is attached to a window. */
+        void onAttachedToWindow();
+
+        /** Called when the layout's window visibility changes to {@link View#VISIBLE}. */
+        void updateActionButtonVisibility();
+    }
+
     private static final String TAG = "NewTabPageLayout";
 
-    private int mSearchBoxTwoSideMargin;
-    private final Context mContext;
-    private LogoCoordinator mLogoCoordinator;
-    private SearchBoxCoordinator mSearchBoxCoordinator;
-    private @Nullable MostVisitedTilesCoordinator mMostVisitedTilesCoordinator;
-
-    private @Nullable OnSearchBoxScrollListener mSearchBoxScrollListener;
-
-    private NewTabPageManager mManager;
-    private WindowAndroid mWindowAndroid;
-    private Activity mActivity;
-    private Profile mProfile;
-    private ActivityResultTracker mActivityResultTracker;
-    private BottomSheetController mBottomSheetController;
-    private ModalDialogManager mModalDialogManager;
-    private SnackbarManager mSnackbarManager;
-    private UiConfig mUiConfig;
-    private @Nullable DisplayStyleObserver mDisplayStyleObserver;
-    private CallbackController mCallbackController = new CallbackController();
-    private SearchEngineUtils.@Nullable SearchEngineIconObserver mSearchEngineIconObserver;
-    private SearchEngineUtils.@Nullable SearchBoxHintTextObserver mSearchBoxHintTextObserver;
-
-    /**
-     * Whether the tiles shown in the layout have finished loading. With {@link #mHasShownView},
-     * it's one of the 2 flags used to track initialisation progress.
-     */
-    private boolean mTilesLoaded;
-
-    /**
-     * Whether the view has been shown at least once.
-     * With {@link #mTilesLoaded}, it's one of the 2 flags used to track initialization progress.
-     */
-    private boolean mHasShownView;
-
-    private boolean mSearchProviderHasLogo = true;
-    private boolean mSearchProviderIsGoogle;
-    private boolean mShowingNonStandardGoogleLogo;
-
-    private boolean mInitialized;
-
-    private float mUrlFocusChangePercent;
-    private boolean mDisableUrlFocusChangeAnimations;
-
-    /** Flag used to request some layout changes after the next layout pass is completed. */
-    private boolean mTileCountChanged;
-
-    private boolean mSnapshotTileGridChanged;
-
-    /**
-     * Vertical inset to add to the top and bottom of the search box bounds. May be 0 if no inset
-     * should be applied. See {@link Rect#inset(int, int)}.
-     */
-    private int mSearchBoxBoundsVerticalInset;
-
-    private FeedSurfaceScrollDelegate mScrollDelegate;
-
-    private boolean mIsTablet;
-    private @Nullable Supplier<Integer> mTabStripHeightSupplier;
-
-    private Callback<Logo> mOnLogoAvailableCallback;
-
-    // mIsComposeplateEnabled is null before checking whether to initialize composeplate view in
-    // NewTabPageLayout#initialize().
-    private @Nullable Boolean mIsComposeplateEnabled;
-    private boolean mIsComposeplatePolicyEnabled;
-    private boolean mIsComposeplateViewInitialized;
-    private Supplier<GURL> mComposeplateUrlSupplier;
-    private @Nullable ComposeplateCoordinator mComposeplateCoordinator;
-    // Previous visibility states for metrics.
-    private @Nullable Boolean mPreviousVoiceSearchButtonVisible;
-    private @Nullable Boolean mPreviousLensButtonVisible;
-    private SearchEngineUtils mSearchEngineUtils;
-    private final int mNtpSearchBoxTransitionStartOffset;
-    private final int mNtpSearchBoxTopMarginWithoutLogo;
-    private final boolean mEnableLogs;
-    private int mCurrentNtpFakeSearchBoxTransitionStartOffset;
-    private int mTopInset;
-    private @Nullable OnLayoutChangeListener mOnLayoutChangeListener;
-    // TODO(crbug.com/451602301): remove @Nullable and all null checks once
-    // ENABLE_SEAMLESS_SIGNIN is removed after the experiment.
-    private @Nullable NtpSigninPromoCoordinator mSigninPromoCoordinator;
-
-    private @Nullable Boolean mIsWhiteBackgroundOnSearchBoxApplied;
+    private @Nullable Delegate mDelegate;
 
     /** Constructor for inflating from XML. */
     public NewTabPageLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mContext = context;
-        Resources resources = getResources();
-        mNtpSearchBoxTopMarginWithoutLogo =
-                resources.getDimensionPixelSize(R.dimen.mvt_container_top_margin);
-        mNtpSearchBoxTransitionStartOffset =
-                resources.getDimensionPixelSize(R.dimen.ntp_search_box_transition_start_offset);
-
-        mEnableLogs = ChromeFeatureList.sNewTabPageCustomizationV2EnableLogs.getValue();
     }
 
     @Override
@@ -200,428 +63,31 @@
         Log.i(TAG, "NewTabPageLayout.onFinishInflate after insertSiteSectionView");
     }
 
-    /**
-     * Initializes the NewTabPageLayout. This must be called immediately after inflation, before
-     * this object is used in any other way.
-     *
-     * @param manager NewTabPageManager used to perform various actions when the user interacts with
-     *     the page.
-     * @param activity The activity that currently owns the new tab page
-     * @param tileGroupDelegate Delegate for {@link TileGroup}.
-     * @param searchProviderHasLogo Whether the search provider has a logo.
-     * @param searchProviderIsGoogle Whether the search provider is Google.
-     * @param scrollDelegate The delegate used to obtain information about scroll state.
-     * @param touchEnabledDelegate The {@link TouchEnabledDelegate} for handling whether touch
-     *     events are allowed.
-     * @param uiConfig UiConfig that provides display information about this view.
-     * @param lifecycleDispatcher Activity lifecycle dispatcher.
-     * @param profile The {@link Profile} associated with the NTP.
-     * @param windowAndroid An instance of a {@link WindowAndroid}.
-     * @param activityResultTracker Tracker of activity results.
-     * @param bottomSheetController Used to interact with the bottom sheet.
-     * @param modalDialogManagerSupplier Supplies the {@link ModalDialogManager}.
-     * @param snackbarManager Manages snackbars shown in the app.
-     * @param isTablet {@code true} if the NTP surface is in tablet mode.
-     * @param tabStripHeightSupplier Supplier of the tab strip height.
-     */
-    @Initializer
-    public void initialize(
-            NewTabPageManager manager,
-            Activity activity,
-            Delegate tileGroupDelegate,
-            boolean searchProviderHasLogo,
-            boolean searchProviderIsGoogle,
-            FeedSurfaceScrollDelegate scrollDelegate,
-            TouchEnabledDelegate touchEnabledDelegate,
-            UiConfig uiConfig,
-            ActivityLifecycleDispatcher lifecycleDispatcher,
-            Profile profile,
-            WindowAndroid windowAndroid,
-            ActivityResultTracker activityResultTracker,
-            BottomSheetController bottomSheetController,
-            ModalDialogManager modalDialogManager,
-            SnackbarManager snackbarManager,
-            boolean isTablet,
-            Supplier<Integer> tabStripHeightSupplier,
-            Supplier<GURL> composeplateUrlSupplier) {
-        TraceEvent.begin(TAG + ".initialize()");
-        mScrollDelegate = scrollDelegate;
-        mManager = manager;
-        mActivity = activity;
-        mProfile = profile;
-        mUiConfig = uiConfig;
-        mWindowAndroid = windowAndroid;
-        mActivityResultTracker = activityResultTracker;
-        mBottomSheetController = bottomSheetController;
-        mModalDialogManager = modalDialogManager;
-        mSnackbarManager = snackbarManager;
-        mIsTablet = isTablet;
-        mTabStripHeightSupplier = tabStripHeightSupplier;
-        mSearchEngineUtils = SearchEngineUtils.getForProfile(mProfile);
-        mComposeplateUrlSupplier = composeplateUrlSupplier;
-
-        if (mIsTablet) {
-            mDisplayStyleObserver = this::onDisplayStyleChanged;
-            mUiConfig.addObserver(mDisplayStyleObserver);
-        } else {
-            // On first run, the NewTabPageLayout is initialized behind the First Run Experience,
-            // meaning the UiConfig will pickup the screen layout then. However
-            // onConfigurationChanged is not called on orientation changes until the FRE is
-            // completed. This means that if a user starts the FRE in one orientation, changes an
-            // orientation and then leaves the FRE the UiConfig will have the wrong orientation.
-            // https://crbug.com/683886.
-            mUiConfig.updateDisplayStyle();
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mDelegate != null) {
+            int width = MeasureSpec.getSize(widthMeasureSpec);
+            mDelegate.onMeasure(width);
         }
 
-        mSearchBoxCoordinator = new SearchBoxCoordinator(getContext(), this, mIsTablet);
-        mSearchBoxCoordinator.initialize(
-                lifecycleDispatcher, mProfile.isOffTheRecord(), mWindowAndroid);
-
-        updateSearchBoxTwoSideMargin();
-        initializeLogoCoordinator();
-        setSearchProviderInfo(searchProviderHasLogo, searchProviderIsGoogle);
-        initializeMostVisitedTilesCoordinator(
-                mProfile, lifecycleDispatcher, tileGroupDelegate, touchEnabledDelegate);
-
-        mSearchEngineIconObserver = (newIcon) -> mSearchBoxCoordinator.setSearchEngineIcon(newIcon);
-        mSearchEngineUtils.addIconObserver(mSearchEngineIconObserver);
-        setSearchBoxTextAppearance();
-
-        initializeSearchBoxTextView();
-        initializeVoiceSearchButton();
-        initializeLensButton();
-
-        initializeComposeplateFlags(mProfile);
-        if (assumeNonNull(mIsComposeplateEnabled)) {
-            initializeComposeplate();
-        }
-
-        // This should be called after both mSearchBoxCoordinator and mComposeplateCoordinator are
-        // initialized.
-        onCustomizedBackgroundChanged(
-                NtpCustomizationUtils.shouldApplyWhiteBackgroundOnSearchBox());
-
-        // This should called after flags of composeplate view are initialized.
-        setSearchBoxHeightBoundsVerticalInset();
-
-        updateActionButtonVisibility();
-        initializeLayoutChangeListener();
-        if (SigninFeatureMap.isEnabled(SigninFeatures.ENABLE_SEAMLESS_SIGNIN)) {
-            initializeSigninPromoCoordinator();
-        }
-
-        // Initialize Searchbox observers
-        mSearchBoxHintTextObserver = this::onSearchBoxHintTextChanged;
-        mSearchEngineUtils.addSearchBoxHintTextObserver(mSearchBoxHintTextObserver);
-
-        manager.addDestructionObserver(NewTabPageLayout.this::onDestroy);
-        mInitialized = true;
-
-        TraceEvent.end(TAG + ".initialize()");
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
-    /** Sets the height of the search box and mSearchBoxBoundsVerticalInset. */
-    private void setSearchBoxHeightBoundsVerticalInset() {
-        int searchBoxHeight =
-                NtpCustomizationUtils.getSearchBoxHeightWithShadows(
-                        getResources(),
-                        assumeNonNull(mIsComposeplateEnabled),
-                        Boolean.TRUE.equals(mIsWhiteBackgroundOnSearchBoxApplied));
-        mSearchBoxCoordinator.setHeight(searchBoxHeight);
-
-        mSearchBoxBoundsVerticalInset =
-                Math.round(
-                        (searchBoxHeight
-                                        - getResources()
-                                                .getDimensionPixelSize(
-                                                        R.dimen.toolbar_height_no_shadow))
-                                / 2f);
-    }
-
-    public void reload() {
-        // TODO(crbug.com/41487877): Add handler in Magic Stack and dispatcher.
-    }
-
-    public void enableSearchBoxEditText(boolean enable) {
-        mSearchBoxCoordinator.enableSearchBoxEditText(enable);
-    }
-
-    /**
-     * @return The {@link FeedSurfaceScrollDelegate} for this class.
-     */
-    FeedSurfaceScrollDelegate getScrollDelegate() {
-        return mScrollDelegate;
-    }
-
-    /** Sets up the hint text and event handlers for the search box text view. */
-    private void initializeSearchBoxTextView() {
-        TraceEvent.begin(TAG + ".initializeSearchBoxTextView()");
-
-        mSearchBoxCoordinator.setSearchBoxClickListener(
-                v -> mManager.focusSearchBox(false, AutocompleteRequestType.SEARCH, null));
-
-        // @TODO(crbug.com/41492572): Add test case for search box OnDragListener.
-        mSearchBoxCoordinator.setSearchBoxDragListener(
-                new OnDragListener() {
-                    @Override
-                    public boolean onDrag(View view, DragEvent dragEvent) {
-                        // Disable search box EditText when browser content is dropped, its
-                        // re-enabled in {@link ChromeTabbedOnDragListener}, since a disabled view
-                        // will stop receiving further drag events. Given the child-first drag event
-                        // dispatch, disabling the TextView at ACTION_DRAG_STARTED is necessary to
-                        // prevent it from registering as a drop target and consuming the
-                        // ACTION_DROP event, thereby ensuring {@link ChromeTabbedOnDragListener}
-                        // receives it.
-                        if (MimeTypeUtils.clipDescriptionHasBrowserContent(
-                                        dragEvent.getClipDescription())
-                                && dragEvent.getAction() == DragEvent.ACTION_DRAG_STARTED) {
-                            enableSearchBoxEditText(false);
-                        }
-                        return false;
-                    }
-                });
-
-        mSearchBoxCoordinator.setSearchBoxTextWatcher(
-                new EmptyTextWatcher() {
-                    @Override
-                    public void afterTextChanged(Editable s) {
-                        if (s.length() == 0) return;
-                        mManager.focusSearchBox(
-                                false, AutocompleteRequestType.SEARCH, s.toString());
-                        mSearchBoxCoordinator.setSearchText("");
-                    }
-                });
-        TraceEvent.end(TAG + ".initializeSearchBoxTextView()");
-    }
-
-    public void onSearchBoxHintTextChanged() {
-        mSearchBoxCoordinator.setSearchBoxHintText(
-                mSearchEngineUtils.getOmniboxHintText(AutocompleteRequestType.SEARCH));
-    }
-
-    private void setSearchBoxTextAppearance() {
-        boolean shouldApplyWhiteBackground =
-                NtpCustomizationUtils.shouldApplyWhiteBackgroundOnSearchBox();
-
-        if (shouldApplyWhiteBackground) {
-            mSearchBoxCoordinator.setSearchBoxTextAppearance(
-                    R.style.TextAppearance_FakeSearchBoxTextMediumDark);
-        } else {
-            mSearchBoxCoordinator.setSearchBoxTextAppearance(
-                    R.style.TextAppearance_FakeSearchBoxTextMedium);
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mDelegate != null) {
+            mDelegate.onAttachedToWindow();
         }
     }
 
-    private void initializeVoiceSearchButton() {
-        TraceEvent.begin(TAG + ".initializeVoiceSearchButton()");
-        OnClickListener voiceSearchButtonClickListener =
-                v -> mManager.focusSearchBox(true, AutocompleteRequestType.SEARCH, null);
-        mSearchBoxCoordinator.addVoiceSearchButtonClickListener(voiceSearchButtonClickListener);
-        TraceEvent.end(TAG + ".initializeVoiceSearchButton()");
-    }
+    @Override
+    protected void onWindowVisibilityChanged(int visibility) {
+        super.onWindowVisibilityChanged(visibility);
 
-    private void initializeLensButton() {
-        TraceEvent.begin(TAG + ".initializeLensButton()");
-        OnClickListener lensButtonClickListener =
-                v -> {
-                    LensMetrics.recordClicked(LensEntryPoint.NEW_TAB_PAGE);
-                    mSearchBoxCoordinator.startLens(LensEntryPoint.NEW_TAB_PAGE);
-                };
-        mSearchBoxCoordinator.addLensButtonClickListener(lensButtonClickListener);
-        TraceEvent.end(TAG + ".initializeLensButton()");
-    }
-
-    private void initializeComposeplateFlags(Profile profile) {
-        mIsComposeplateEnabled = ComposeplateUtils.isComposeplateEnabled(mIsTablet, profile);
-        mIsComposeplatePolicyEnabled =
-                mIsComposeplateEnabled && ComposeplateUtils.isEnabledByPolicy(profile);
-    }
-
-    private void initializeComposeplate() {
-        if (mIsComposeplateViewInitialized) return;
-
-        mIsComposeplateViewInitialized = true;
-
-        boolean shouldApplyWhiteBackgroundOnSearchBox =
-                NtpCustomizationUtils.shouldApplyWhiteBackgroundOnSearchBox();
-
-        ViewStub composeplateViewStub = findViewById(R.id.composeplate_view_v2_stub);
-        ViewGroup composeplateView = (ViewGroup) composeplateViewStub.inflate();
-        if (NtpCustomizationUtils.isNtpThemeCustomizationEnabled()) {
-            // TODO(https://crbug.com/423579377): Moves the layout parameters to
-            //  composeplate_view_layout_v2.xml after the feature NewTabPageCustomizationV2 is
-            //  launched.
-            NewTabPageUtils.applyUpdatedLayoutParamsForComposeplateView(composeplateView);
+        if (visibility == VISIBLE && mDelegate != null) {
+            mDelegate.updateActionButtonVisibility();
         }
-        mComposeplateCoordinator = new ComposeplateCoordinator(composeplateView, mProfile);
-        mComposeplateCoordinator.setIncognitoClickListener(this::onIncognitoButtonClicked);
-        // Don't log click metrics in this listener, since the mComposeplateCoordinator will
-        // log.
-        mComposeplateCoordinator.setComposeplateButtonClickListener(
-                this::onComposeplateButtonClicked);
-
-        if (shouldApplyWhiteBackgroundOnSearchBox) {
-            // It is safe to call mComposeplateCoordinator.applyWhiteBackgroundWithShadow() again
-            // since it is no-op if the white background has been applied.
-            mComposeplateCoordinator.applyWhiteBackgroundWithShadow(/* apply= */ true);
-        }
-    }
-
-    private void onComposeplateButtonClicked(View view) {
-        if (OmniboxFeatures.sOmniboxMultimodalInput.isEnabled()
-                && OmniboxFeatures.sRedirectComposeplateButton.getValue()
-                && !mIsTablet
-                && mIsComposeplatePolicyEnabled) {
-            mManager.focusSearchBox(false, AutocompleteRequestType.AI_MODE, null);
-            return;
-        }
-
-        GURL composeplateUrl = mComposeplateUrlSupplier.get();
-        if (composeplateUrl == null) return;
-
-        mManager.getNativePageHost()
-                .loadUrl(new LoadUrlParams(composeplateUrl), /* incognito= */ false);
-    }
-
-    private void onIncognitoButtonClicked(View view) {
-        if (!IncognitoUtils.isIncognitoModeEnabled(mProfile)) return;
-
-        UrlConstantResolver resolver = UrlConstantResolverFactory.getForProfile(mProfile);
-        mManager.getNativePageHost().loadUrl(new LoadUrlParams(resolver.getNtpUrl()), true);
-    }
-
-    private void initializeLayoutChangeListener() {
-        TraceEvent.begin(TAG + ".initializeLayoutChangeListener()");
-        mOnLayoutChangeListener = this::onLayoutChanged;
-        addOnLayoutChangeListener(mOnLayoutChangeListener);
-        TraceEvent.end(TAG + ".initializeLayoutChangeListener()");
-    }
-
-    private void onLayoutChanged(
-            View view,
-            int left,
-            int top,
-            int right,
-            int bottom,
-            int oldLeft,
-            int oldTop,
-            int oldRight,
-            int oldBottom) {
-        int oldHeight = oldBottom - oldTop;
-        int newHeight = bottom - top;
-
-        if (oldHeight == newHeight && !mTileCountChanged) return;
-        mTileCountChanged = false;
-
-        // Re-apply the url focus change amount after a rotation to ensure the views are
-        // correctly placed with their new layout configurations.
-        onUrlFocusAnimationChanged();
-        updateSearchBoxOnScroll();
-
-        // The positioning of elements may have been changed (since the elements expand
-        // to fill the available vertical space), so adjust the scroll.
-        if (mScrollDelegate.isScrollViewInitialized()) mScrollDelegate.snapScroll();
-    }
-
-    private void initializeLogoCoordinator() {
-        Callback<LoadUrlParams> logoClickedCallback =
-                mCallbackController.makeCancelable(
-                        (urlParams) -> {
-                            mManager.getNativePageHost().loadUrl(urlParams, /* incognito= */ false);
-                            BrowserUiUtils.recordModuleClickHistogram(
-                                    ModuleTypeOnStartAndNtp.DOODLE);
-                        });
-        mOnLogoAvailableCallback =
-                mCallbackController.makeCancelable(
-                        (logo) -> {
-                            mSnapshotTileGridChanged = true;
-                            mShowingNonStandardGoogleLogo = logo != null && mSearchProviderIsGoogle;
-                            NtpCustomizationConfigManager.getInstance()
-                                    .setDefaultSearchEngineLogoBitmap(
-                                            logo == null ? null : logo.image);
-                        });
-
-        mLogoCoordinator =
-                new LogoCoordinator(
-                        mContext,
-                        logoClickedCallback,
-                        /* parentView= */ this,
-                        mOnLogoAvailableCallback,
-                        /* visibilityObserver= */ null,
-                        () -> MultiWindowUtils.getInstance().isInMultiWindowMode(mActivity));
-        mLogoCoordinator.initWithNative(mProfile);
-    }
-
-    private void initializeMostVisitedTilesCoordinator(
-            Profile profile,
-            ActivityLifecycleDispatcher activityLifecycleDispatcher,
-            TileGroup.Delegate tileGroupDelegate,
-            TouchEnabledDelegate touchEnabledDelegate) {
-        View mvTilesContainerLayout = findViewById(R.id.mv_tiles_container);
-        assert mvTilesContainerLayout != null;
-
-        mMostVisitedTilesCoordinator =
-                new MostVisitedTilesCoordinator(
-                        mActivity,
-                        activityLifecycleDispatcher,
-                        mvTilesContainerLayout,
-                        () -> mSnapshotTileGridChanged = true,
-                        () -> {
-                            if (mUrlFocusChangePercent == 1f) mTileCountChanged = true;
-                        });
-
-        mMostVisitedTilesCoordinator.initWithNative(
-                profile, mManager, tileGroupDelegate, touchEnabledDelegate);
-
-        if (ChromeFeatureList.sNewTabPageCustomizationForMvt.isEnabled()) {
-            mMostVisitedTilesCoordinator.updateMvtVisibility();
-        }
-    }
-
-    private void initializeSigninPromoCoordinator() {
-        ViewStub signinPromoViewContainerStub = findViewById(R.id.signin_promo_view_container_stub);
-        mSigninPromoCoordinator =
-                new NtpSigninPromoCoordinator(
-                        mWindowAndroid,
-                        mActivity,
-                        mProfile,
-                        mActivityResultTracker,
-                        SigninAndHistorySyncActivityLauncherImpl.get(),
-                        mBottomSheetController,
-                        mModalDialogManager,
-                        mSnackbarManager,
-                        DeviceLockActivityLauncherImpl.get(),
-                        signinPromoViewContainerStub,
-                        SetupListModuleUtils::isSetupListActive);
-    }
-
-    /** Updates the search box when the parent view's scroll position is changed. */
-    void updateSearchBoxOnScroll() {
-        if (mDisableUrlFocusChangeAnimations) return;
-
-        // When the page changes (tab switching or new page loading), it is possible that events
-        // (e.g. delayed view change notifications) trigger calls to these methods after
-        // the current page changes. We check it again to make sure we don't attempt to update the
-        // wrong page.
-        if (!mManager.isCurrentPage()) return;
-
-        if (mSearchBoxScrollListener != null) {
-            mSearchBoxScrollListener.onNtpScrollChanged(getToolbarTransitionPercentage());
-        }
-    }
-
-    /**
-     * Calculates the percentage (between 0 and 1) of the transition from the search box to the
-     * omnibox at the top of the New Tab Page, which is determined by the amount of scrolling and
-     * the position of the search box.
-     *
-     * @return the transition percentage
-     */
-    float getToolbarTransitionPercentage() {
-        return mSearchBoxCoordinator.getToolbarTransitionPercentage(
-                mScrollDelegate,
-                mTabStripHeightSupplier,
-                mCurrentNtpFakeSearchBoxTransitionStartOffset);
     }
 
     private void initializeSiteSectionView() {
@@ -634,582 +100,8 @@
         if (getVisibility() != View.VISIBLE) setVisibility(View.VISIBLE);
     }
 
-    /**
-     * @return The fake search box view.
-     */
-    public View getSearchBoxView() {
-        return mSearchBoxCoordinator.getView();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        if (mIsTablet && mMostVisitedTilesCoordinator != null) {
-            mMostVisitedTilesCoordinator.calculateTabletMvtWidth(width);
-        }
-
-        unifyElementWidths(width);
-
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    public void onSwitchToForeground() {
-        if (mMostVisitedTilesCoordinator != null) {
-            mMostVisitedTilesCoordinator.onSwitchToForeground();
-        }
-    }
-
-    /**
-     * Should be called every time one of the flags used to track initialization progress changes.
-     * Finalizes initialization once all the preliminary steps are complete.
-     *
-     * @see #mHasShownView
-     * @see #mTilesLoaded
-     */
-    private void onInitializationProgressChanged() {
-        if (!hasLoadCompleted()) return;
-
-        mManager.onLoadingComplete();
-
-        // Load the logo after everything else is finished, since it's lower priority.
-        if (mLogoCoordinator != null) mLogoCoordinator.loadSearchProviderLogoWithAnimation();
-    }
-
-    /**
-     * To be called to notify that the tiles have finished loading. Will do nothing if a load was
-     * previously completed.
-     */
-    public void onTilesLoaded() {
-        if (mTilesLoaded) return;
-        mTilesLoaded = true;
-
-        onInitializationProgressChanged();
-    }
-
-    /**
-     * Changes the layout depending on whether the selected search provider (e.g. Google, Bing) has
-     * a logo.
-     *
-     * @param hasLogo Whether the search provider has a logo.
-     * @param isGoogle Whether the search provider is Google.
-     */
-    void setSearchProviderInfo(boolean hasLogo, boolean isGoogle) {
-        if (hasLogo == mSearchProviderHasLogo
-                && isGoogle == mSearchProviderIsGoogle
-                && mInitialized) {
-            return;
-        }
-        boolean isSearchProviderIsGoogleChanged = mSearchProviderIsGoogle != isGoogle;
-        mSearchProviderHasLogo = hasLogo;
-        mSearchProviderIsGoogle = isGoogle;
-
-        if (!mSearchProviderIsGoogle) {
-            mShowingNonStandardGoogleLogo = false;
-        }
-
-        setSearchProviderTopMargin();
-        setLogoViewBottomMargin();
-
-        updateTilesLayoutMargins();
-
-        // Hide or show the views above the most visited tiles as needed, e.g, spacers. The
-        // visibility of Logo is handled by LogoCoordinator.
-        setSearchBoxTextAppearance();
-
-        // Skips if the flag hasn't been initialized since the initialization of the following
-        // components will be called again in #initialize().
-        if (mIsComposeplateEnabled != null) {
-            // when mSearchProviderIsGoogle is changed, mIsComposeplateEnabled might be changed too,
-            // recalculate its value.
-            if (isSearchProviderIsGoogleChanged) {
-                boolean previousIsComposeplateEnabled = mIsComposeplateEnabled;
-                initializeComposeplateFlags(mProfile);
-                if (!previousIsComposeplateEnabled
-                        && mIsComposeplateEnabled
-                        && mComposeplateCoordinator == null) {
-                    // If the composeplate view is enabled while mComposeplateCoordinator hasn't
-                    // been initialized yet, initialize it now.
-                    initializeComposeplate();
-                }
-
-                if (previousIsComposeplateEnabled != mIsComposeplateEnabled) {
-                    // When the flag value is changed, the height of search box might be changed.
-                    setSearchBoxHeightBoundsVerticalInset();
-                    // Updates the composeplate view's visibility.
-                    updateActionButtonVisibility();
-                }
-            }
-        }
-
-        onUrlFocusAnimationChanged();
-
-        mSnapshotTileGridChanged = true;
-    }
-
-    /** Updates the margins for the most visited tiles layout based on what is shown above it. */
-    private void updateTilesLayoutMargins() {
-        if (mMostVisitedTilesCoordinator == null) return;
-
-        mMostVisitedTilesCoordinator.updateTilesLayoutMargins(
-                shouldShowLogo(),
-                mIsWhiteBackgroundOnSearchBoxApplied == null
-                        ? false
-                        : mIsWhiteBackgroundOnSearchBoxApplied,
-                mIsTablet);
-    }
-
-    /**
-     * Updates whether the NewTabPage should animate on URL focus changes.
-     *
-     * @param disable Whether to disable the animations.
-     */
-    void setUrlFocusAnimationsDisabled(boolean disable) {
-        if (disable == mDisableUrlFocusChangeAnimations) return;
-        mDisableUrlFocusChangeAnimations = disable;
-        if (!disable) onUrlFocusAnimationChanged();
-    }
-
-    /**
-     * @return Whether URL focus animations are currently disabled.
-     */
-    boolean urlFocusAnimationsDisabled() {
-        return mDisableUrlFocusChangeAnimations;
-    }
-
-    /**
-     * Specifies the percentage the URL is focused during an animation.  1.0 specifies that the URL
-     * bar has focus and has completed the focus animation.  0 is when the URL bar is does not have
-     * any focus.
-     *
-     * @param percent The percentage of the URL bar focus animation.
-     */
-    void setUrlFocusChangeAnimationPercent(float percent) {
-        mUrlFocusChangePercent = percent;
-        onUrlFocusAnimationChanged();
-    }
-
-    /**
-     * @return The percentage that the URL bar is focused during an animation.
-     */
-    @VisibleForTesting
-    float getUrlFocusChangeAnimationPercent() {
-        return mUrlFocusChangePercent;
-    }
-
-    void onUrlFocusAnimationChanged() {
-        /*
-         * Avoid Y-translation when animation is disabled, view is moving or on tablet form-factor.
-         * Context for tablets - Unlike phones, this method is not called on tablets during URL
-         * focus post NTP load. However when physical keyboard is present, we try to auto-focus URL
-         * during page load causing this method to be called. Disabling this for all cases on this
-         * form-factor since this translation does not WAI. (see crbug.com/40910640)
-         */
-        if (mDisableUrlFocusChangeAnimations || mIsTablet) return;
-
-        // Translate so that the search box is at the top, but only upwards.
-        int basePosition = mScrollDelegate.getVerticalScrollOffset() + getPaddingTop();
-        int target =
-                Math.max(
-                        basePosition,
-                        getSearchBoxView().getBottom()
-                                - getSearchBoxView().getPaddingBottom()
-                                - mSearchBoxBoundsVerticalInset);
-
-        float translationY = mUrlFocusChangePercent * (basePosition - target);
-        setTranslationYOfFakeboxAndAbove(translationY);
-    }
-
-    /**
-     * Sets the translation_y of the fakebox and all views above it, but not the views below. Used
-     * when the url focus animation is combined with the omnibox suggestions list animation to
-     * reduce the number of visual elements in motion.
-     */
-    private void setTranslationYOfFakeboxAndAbove(float translationY) {
-        for (int i = 0; i < getChildCount(); i++) {
-            View view = getChildAt(i);
-            view.setTranslationY(translationY);
-            if (view.getId() == R.id.search_box) return;
-        }
-    }
-
-    /**
-     * Updates the opacity of the search box when scrolling.
-     *
-     * @param alpha opacity (alpha) value to use.
-     */
-    public void setSearchBoxAlpha(float alpha) {
-        mSearchBoxCoordinator.setAlpha(alpha);
-    }
-
-    /**
-     * Updates the opacity of the search provider logo when scrolling.
-     *
-     * @param alpha opacity (alpha) value to use.
-     */
-    public void setSearchProviderLogoAlpha(float alpha) {
-        if (mLogoCoordinator != null) mLogoCoordinator.setAlpha(alpha);
-    }
-
-    /**
-     * Get the bounds of the search box in relation to the top level {@code parentView}.
-     *
-     * @param bounds The current drawing location of the search box.
-     * @param translation The translation applied to the search box by the parent view hierarchy up
-     *     to the {@code parentView}.
-     * @param parentView The top level parent view used to translate search box bounds.
-     */
-    void getSearchBoxBounds(Rect bounds, Point translation, View parentView) {
-        mSearchBoxCoordinator.getSearchBoxBounds(
-                bounds, translation, parentView, mScrollDelegate, mSearchBoxBoundsVerticalInset);
-    }
-
-    /** Returns the fake search box's transition start offset on NTP. */
-    private int getNtpSearchBoxTransitionStartOffset(boolean showFakeSearchBoxWithoutLogo) {
-        if (mIsTablet && showFakeSearchBoxWithoutLogo) {
-            // On tablets, it is possible to show fake search box if DSE doesn't have logo if DSE
-            // mobile parity v2 is enabled. The mNTPFakeSearchBoxTransitionStartOffset is used to
-            // calculate scrolling percentage in getToolbarTransitionPercentage(). Reset to 0 when
-            // no doodle is shown for 3p DSE to prevent the alpha of fake search box being set to 0
-            // (transparent) by ToolbarTablet#updateNtp().
-            return 0;
-        } else {
-            return mNtpSearchBoxTransitionStartOffset;
-        }
-    }
-
-    private void setSearchProviderTopMargin() {
-        boolean showFakeSearchBoxWithoutLogo = !mSearchProviderHasLogo;
-        mCurrentNtpFakeSearchBoxTransitionStartOffset =
-                getNtpSearchBoxTransitionStartOffset(showFakeSearchBoxWithoutLogo);
-
-        int topMargin = showFakeSearchBoxWithoutLogo ? mNtpSearchBoxTopMarginWithoutLogo : 0;
-        mSearchBoxCoordinator.setTopMargin(topMargin);
-
-        if (mLogoCoordinator != null) {
-            mLogoCoordinator.setTopMargin(getLogoTopMargin());
-        }
-    }
-
-    private void setLogoViewBottomMargin() {
-        if (mLogoCoordinator == null) return;
-
-        boolean shouldShowShadow = Boolean.TRUE.equals(mIsWhiteBackgroundOnSearchBoxApplied);
-        int logoViewBottomMarginPx =
-                NtpCustomizationUtils.getLogoViewBottomMarginPx(getResources(), shouldShowShadow);
-        mLogoCoordinator.setBottomMargin(logoViewBottomMarginPx);
-    }
-
-    private int getLogoTopMargin() {
-        Resources resources = getResources();
-
-        if (mShowingNonStandardGoogleLogo && mSearchProviderHasLogo) {
-            return LogoUtils.getTopMarginForDoodle(resources);
-        }
-
-        return resources.getDimensionPixelSize(R.dimen.ntp_logo_margin_top);
-    }
-
-    /**
-     * Sets the listener for search box scroll changes.
-     *
-     * @param listener The listener to be notified on changes.
-     */
-    void setSearchBoxScrollListener(@Nullable OnSearchBoxScrollListener listener) {
-        mSearchBoxScrollListener = listener;
-        if (mSearchBoxScrollListener != null) updateSearchBoxOnScroll();
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        assert mManager != null;
-
-        if (!mHasShownView) {
-            mHasShownView = true;
-            onInitializationProgressChanged();
-            TraceEvent.instant("NewTabPageSearchAvailable");
-        }
-    }
-
-    /** Update the visibility of the action buttons. */
-    void updateActionButtonVisibility() {
-        boolean shouldShowVoiceSearchButton = mManager.isVoiceSearchEnabled();
-        boolean shouldShowLensButton =
-                mSearchBoxCoordinator.isLensEnabled(LensEntryPoint.NEW_TAB_PAGE);
-        if (mIsComposeplateEnabled == null) return;
-
-        mSearchBoxCoordinator.setVoiceSearchButtonVisibility(shouldShowVoiceSearchButton);
-        mSearchBoxCoordinator.setLensButtonVisibility(shouldShowLensButton);
-        boolean shouldShowComposeplateButton = false;
-        // As long as mComposeplateCoordinator has been initialized, we should update its
-        // visibility.
-        if (mComposeplateCoordinator != null) {
-            shouldShowComposeplateButton =
-                    mIsComposeplateEnabled
-                            && mSearchProviderIsGoogle
-                            && IncognitoUtils.isIncognitoModeEnabled(mProfile);
-            mComposeplateCoordinator.setVisibility(
-                    shouldShowComposeplateButton, mManager.isCurrentPage());
-        }
-        updatePreviousButtonVisibilityAndRecordMetrics(
-                shouldShowVoiceSearchButton, shouldShowLensButton, shouldShowComposeplateButton);
-    }
-
-    /**
-     * Updates the previous visibility state of the voice search and lens buttons and records
-     * metrics if their visibility has changed on the current page.
-     *
-     * @param isVoiceSearchButtonVisible The new visibility state of the voice search button.
-     * @param isLensButtonVisible The new visibility state of the lens button.
-     * @param isComposeplateButtonVisible The new visibility state of the composeplate button.
-     */
-    private void updatePreviousButtonVisibilityAndRecordMetrics(
-            boolean isVoiceSearchButtonVisible,
-            boolean isLensButtonVisible,
-            boolean isComposeplateButtonVisible) {
-        if (!mManager.isCurrentPage()
-                || (mPreviousVoiceSearchButtonVisible != null
-                        && isVoiceSearchButtonVisible == mPreviousVoiceSearchButtonVisible
-                        && mPreviousLensButtonVisible != null
-                        && isLensButtonVisible == mPreviousLensButtonVisible)) {
-            return;
-        }
-
-        if (mPreviousLensButtonVisible == null
-                || isLensButtonVisible != mPreviousLensButtonVisible) {
-            LensMetrics.recordShown(LensEntryPoint.NEW_TAB_PAGE, isLensButtonVisible);
-        }
-
-        ComposeplateMetricsUtils.recordFakeSearchBoxImpression2();
-        ComposeplateMetricsUtils.recordFakeSearchBoxComposeplateButtonImpression2(
-                isComposeplateButtonVisible);
-
-        mPreviousVoiceSearchButtonVisible = isVoiceSearchButtonVisible;
-        mPreviousLensButtonVisible = isLensButtonVisible;
-    }
-
-    @Override
-    protected void onWindowVisibilityChanged(int visibility) {
-        super.onWindowVisibilityChanged(visibility);
-
-        if (visibility == VISIBLE) {
-            updateActionButtonVisibility();
-        }
-    }
-
-    /**
-     * @see InvalidationAwareThumbnailProvider#shouldCaptureThumbnail()
-     */
-    public boolean shouldCaptureThumbnail() {
-        return mSnapshotTileGridChanged;
-    }
-
-    /**
-     * Should be called before a thumbnail of the parent view is captured.
-     *
-     * @see InvalidationAwareThumbnailProvider#captureThumbnail(Canvas)
-     */
-    public void onPreCaptureThumbnail() {
-        if (mLogoCoordinator != null) mLogoCoordinator.endFadeAnimation();
-        mSnapshotTileGridChanged = false;
-    }
-
-    private boolean shouldShowLogo() {
-        return mSearchProviderHasLogo;
-    }
-
-    private boolean hasLoadCompleted() {
-        return mHasShownView && mTilesLoaded;
-    }
-
-    @SuppressWarnings("NullAway")
-    private void onDestroy() {
-        if (mCallbackController != null) {
-            mCallbackController.destroy();
-            mCallbackController = null;
-        }
-
-        if (mLogoCoordinator != null) {
-            mLogoCoordinator.destroy();
-            mLogoCoordinator = null;
-        }
-
-        mSearchBoxCoordinator.destroy();
-        mSearchBoxCoordinator = null;
-
-        if (mMostVisitedTilesCoordinator != null) {
-            mMostVisitedTilesCoordinator.destroyMvtiles();
-            mMostVisitedTilesCoordinator = null;
-        }
-
-        if (mIsTablet) {
-            mUiConfig.removeObserver(mDisplayStyleObserver);
-            mDisplayStyleObserver = null;
-        }
-
-        if (mSearchEngineUtils != null) {
-            if (mSearchBoxHintTextObserver != null) {
-                mSearchEngineUtils.removeSearchBoxHintTextObserver(mSearchBoxHintTextObserver);
-                mSearchBoxHintTextObserver = null;
-            }
-            if (mSearchEngineIconObserver != null) {
-                mSearchEngineUtils.removeIconObserver(mSearchEngineIconObserver);
-                mSearchEngineIconObserver = null;
-            }
-            mSearchEngineUtils = null;
-        }
-
-        removeOnLayoutChangeListener(mOnLayoutChangeListener);
-        mOnLayoutChangeListener = null;
-
-        if (mComposeplateCoordinator != null) {
-            mComposeplateCoordinator.destroy();
-            mComposeplateCoordinator = null;
-        }
-
-        if (mSigninPromoCoordinator != null) {
-            mSigninPromoCoordinator.destroy();
-            mSigninPromoCoordinator = null;
-        }
-
-        mSearchBoxScrollListener = null;
-        mComposeplateUrlSupplier = null;
-    }
-
-    /** Makes the Search Box and Logo as wide as Most Visited. */
-    private void unifyElementWidths(int width) {
-        int searchBoxWidth = width - mSearchBoxTwoSideMargin;
-        if (mSearchBoxCoordinator != null) {
-            mSearchBoxCoordinator.setLayoutWidth(searchBoxWidth);
-        }
-
-        if (mLogoCoordinator != null) {
-            mLogoCoordinator.setLayoutWidth(width);
-        }
-
-        if (mComposeplateCoordinator != null) {
-            mComposeplateCoordinator.setLayoutWidth(searchBoxWidth);
-        }
-    }
-
-    LogoCoordinator getLogoCoordinatorForTesting() {
-        return mLogoCoordinator;
-    }
-
-    private void onDisplayStyleChanged(UiConfig.DisplayStyle newDisplayStyle) {
-        if (!mIsTablet) return;
-
-        updateDoodleOnTablet();
-        if (mMostVisitedTilesCoordinator != null) {
-            mMostVisitedTilesCoordinator.updateMvtOnTablet();
-        }
-        updateSearchBoxTwoSideMargin();
-    }
-
-    /**
-     * Adjusts the doodle size while the tablet transitions to or from a multi-screen layout,
-     * ensuring the change occurs post-logo initialization.
-     */
-    private void updateDoodleOnTablet() {
-        if (!mIsTablet || mLogoCoordinator == null) return;
-
-        mLogoCoordinator.updateDoodleOnTablet(mShowingNonStandardGoogleLogo);
-    }
-
-    private void updateSearchBoxTwoSideMargin() {
-        mSearchBoxTwoSideMargin =
-                NtpCustomizationUtils.getSearchBoxTwoSideMargin(
-                        getResources(), mUiConfig, mIsTablet);
-    }
-
-    /**
-     * Called when the layout changes between edge-to-edge and standard.
-     *
-     * @param systemTopInset The system's top inset, i.e., the height of the Status bar. It is
-     *     always bigger than 0.
-     * @param supportsEdgeToEdgeOnTop Determines if the NTP should consume this top inset, extending
-     *     itself to the Status bar area.
-     */
-    void onToEdgeChange(int systemTopInset, boolean supportsEdgeToEdgeOnTop) {
-        // Exits early if the NTP's top padding doesn't require adjustment.
-        if (NtpCustomizationUtils.shouldSkipTopInsetsChange(
-                mTopInset, systemTopInset, supportsEdgeToEdgeOnTop)) {
-            return;
-        }
-
-        mTopInset = supportsEdgeToEdgeOnTop ? systemTopInset : 0;
-        mCurrentNtpFakeSearchBoxTransitionStartOffset =
-                getNtpSearchBoxTransitionStartOffset(!mSearchProviderHasLogo) + mTopInset;
-
-        // Top padding is applied to the NTP layout, ensuring all UI components remain in their
-        // original positions after Status bar is hidden.
-        setPaddingRelative(
-                getPaddingStart(),
-                getResources().getDimensionPixelSize(R.dimen.toolbar_height_no_shadow) + mTopInset,
-                getPaddingEnd(),
-                getPaddingBottom());
-
-        if (mEnableLogs) {
-            Log.i(TAG, "The top padding to add on the NTP is %d.", mTopInset);
-        }
-    }
-
-    /**
-     * Called when a customized background image is selected or deselected.
-     *
-     * @param applyWhiteBackgroundOnSearchBox Whether to apply a white background color to the fake
-     *     search box.
-     */
-    void onCustomizedBackgroundChanged(boolean applyWhiteBackgroundOnSearchBox) {
-        // If shouldn't apply a white background and the background hasn't been updated before,
-        // returns now.
-        if (mIsWhiteBackgroundOnSearchBoxApplied == null && !applyWhiteBackgroundOnSearchBox) {
-            return;
-        }
-
-        // If composeplate view flags haven't been initialized yet, returns now.
-        if (mIsComposeplateEnabled == null) {
-            return;
-        }
-
-        // If the background has been updated before and it should remain the same, returns now.
-        if (mIsWhiteBackgroundOnSearchBoxApplied != null
-                && mIsWhiteBackgroundOnSearchBoxApplied == applyWhiteBackgroundOnSearchBox) {
-            return;
-        }
-
-        // If the fake search box hasn't been initialized, returns now. It is fine to skip here
-        // because applyWhiteBackgroundWithShadow() will be called immediately after the
-        // mSearchBoxCoordinator is initialized.
-        if (mSearchBoxCoordinator == null) return;
-
-        mIsWhiteBackgroundOnSearchBoxApplied = applyWhiteBackgroundOnSearchBox;
-
-        if (mSearchBoxCoordinator != null) {
-            mSearchBoxCoordinator.applyWhiteBackgroundWithShadow(applyWhiteBackgroundOnSearchBox);
-        }
-
-        if (mComposeplateCoordinator != null) {
-            mComposeplateCoordinator.applyWhiteBackgroundWithShadow(
-                    applyWhiteBackgroundOnSearchBox);
-        }
-
-        setLogoViewBottomMargin();
-        // Update the search box height and bounds vertical inset since the shadow padding changed.
-        setSearchBoxHeightBoundsVerticalInset();
-
-        if (mMostVisitedTilesCoordinator != null) {
-            updateTilesLayoutMargins();
-        }
-    }
-
-    /** Returns the top inset of the NTP. */
-    int getTopInset() {
-        return mTopInset;
-    }
-
-    /** Returns the vertical inset applied to search box bounds. */
-    int getSearchBoxBoundsVerticalInset() {
-        return mSearchBoxBoundsVerticalInset;
+    /** Sets the delegate instance. */
+    void setDelegate(Delegate delegate) {
+        mDelegate = delegate;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/SnapScrollHelperImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/SnapScrollHelperImpl.java
index 493d5d46..a92c8f8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/SnapScrollHelperImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/SnapScrollHelperImpl.java
@@ -25,7 +25,7 @@
     private static final long SNAP_SCROLL_DELAY_MS = 30;
 
     private final NewTabPageManager mManager;
-    private final NewTabPageLayout mNewTabPageLayout;
+    private final NewTabPageCoordinator mNewTabPageCoordinator;
     private final Runnable mSnapScrollRunnable;
     private final Runnable mUpdateSearchBoxOnScrollRunnable;
     private final int mToolbarHeight;
@@ -38,19 +38,23 @@
 
     /**
      * @param manager The {@link NewTabPageManager} to get information about user interactions on
-     *                the {@link NewTabPage}.
-     * @param newTabPageLayout The {@link NewTabPageLayout} associated with the {@link NewTabPage}.
+     *     the {@link NewTabPage}.
+     * @param newTabPageCoordinator The {@link NewTabPageCoordinator} associated with the {@link
+     *     NewTabPage}.
      */
-    public SnapScrollHelperImpl(NewTabPageManager manager, NewTabPageLayout newTabPageLayout) {
+    public SnapScrollHelperImpl(
+            NewTabPageManager manager, NewTabPageCoordinator newTabPageCoordinator) {
         mManager = manager;
-        mNewTabPageLayout = newTabPageLayout;
+        mNewTabPageCoordinator = newTabPageCoordinator;
         mSnapScrollRunnable = new SnapScrollRunnable();
-        mUpdateSearchBoxOnScrollRunnable = mNewTabPageLayout::updateSearchBoxOnScroll;
+        mUpdateSearchBoxOnScrollRunnable = mNewTabPageCoordinator::updateSearchBoxOnScroll;
 
-        Resources res = newTabPageLayout.getResources();
+        Resources res = newTabPageCoordinator.getNewTabPageLayout().getResources();
         if (ChromeFeatureList.sAndroidProgressBarVisualUpdate.isEnabled()) {
-            mToolbarHeight = res.getDimensionPixelSize(R.dimen.toolbar_height_no_shadow)
-                    + res.getDimensionPixelSize(R.dimen.toolbar_progress_bar_increased_height);
+            mToolbarHeight =
+                    res.getDimensionPixelSize(R.dimen.toolbar_height_no_shadow)
+                            + res.getDimensionPixelSize(
+                                    R.dimen.toolbar_progress_bar_increased_height);
         } else {
             mToolbarHeight =
                     res.getDimensionPixelSize(R.dimen.toolbar_height_no_shadow)
@@ -97,7 +101,7 @@
     /** Update scroll offset and perform snap scroll if necessary. */
     @Override
     public void handleScroll() {
-        int scrollY = mNewTabPageLayout.getScrollDelegate().getVerticalScrollOffset();
+        int scrollY = mNewTabPageCoordinator.getScrollDelegate().getVerticalScrollOffset();
         if (mLastScrollY == scrollY) return;
 
         mLastScrollY = scrollY;
@@ -106,7 +110,7 @@
             mView.removeCallbacks(mSnapScrollRunnable);
             mView.postDelayed(mSnapScrollRunnable, SNAP_SCROLL_DELAY_MS);
         }
-        mNewTabPageLayout.updateSearchBoxOnScroll();
+        mNewTabPageCoordinator.updateSearchBoxOnScroll();
     }
 
     /**
@@ -136,7 +140,7 @@
             scrollPosition = calculateSnapPositionForRegion(scrollPosition, 0, mToolbarHeight);
 
             // Snap scroll to prevent resting in the middle of the omnibox transition.
-            View fakeBox = mNewTabPageLayout.getSearchBoxView();
+            View fakeBox = mNewTabPageCoordinator.getSearchBoxView();
             int fakeBoxUpperBound = fakeBox.getTop() + fakeBox.getPaddingTop();
             scrollPosition =
                     calculateSnapPositionForRegion(
@@ -187,7 +191,7 @@
             assert mPendingSnapScroll;
             mPendingSnapScroll = false;
 
-            mNewTabPageLayout.getScrollDelegate().snapScroll();
+            mNewTabPageCoordinator.getScrollDelegate().snapScroll();
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesCoordinator.java
index 4116447..82858c0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesCoordinator.java
@@ -88,7 +88,7 @@
                 new MostVisitedTilesMediator(
                         activity,
                         mUiConfig,
-                        tilesLayout,
+                        mvTilesContainerLayout,
                         mRenderer,
                         propertyModel,
                         isTablet,
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 fb71b629..50c7a3d 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
@@ -12,6 +12,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.MarginLayoutParams;
 
@@ -50,6 +51,7 @@
     private final Context mContext;
     private final Resources mResources;
     private final UiConfig mUiConfig;
+    private final View mMvTilesContainerLayout;
     private final MostVisitedTilesLayout mMvTilesLayout;
     private final PropertyModel mModel;
     private final boolean mIsTablet;
@@ -74,7 +76,7 @@
     public MostVisitedTilesMediator(
             Context context,
             UiConfig uiConfig,
-            MostVisitedTilesLayout mvTilesLayout,
+            View mvTilesContainerLayout,
             TileRenderer renderer,
             PropertyModel propertyModel,
             boolean isTablet,
@@ -88,7 +90,8 @@
         mIsTablet = isTablet;
         mSnapshotTileGridChangedRunnable = snapshotTileGridChangedRunnable;
         mTileCountChangedRunnable = tileCountChangedRunnable;
-        mMvTilesLayout = mvTilesLayout;
+        mMvTilesContainerLayout = mvTilesContainerLayout;
+        mMvTilesLayout = mvTilesContainerLayout.findViewById(R.id.mv_tiles_layout);
 
         mTileViewLandscapePadding =
                 mResources.getDimensionPixelSize(R.dimen.tile_view_padding_landscape);
@@ -266,7 +269,7 @@
         }
 
         MarginLayoutParams marginLayoutParams =
-                (MarginLayoutParams) mMvTilesLayout.getLayoutParams();
+                (MarginLayoutParams) mMvTilesContainerLayout.getLayoutParams();
         marginLayoutParams.width =
                 mMvtContentFits
                         ? ViewGroup.LayoutParams.WRAP_CONTENT
@@ -292,7 +295,10 @@
     void updateTilesLayoutMargins(
             boolean shouldShowLogo, boolean isWhiteBackgroundOnSearchBoxApplied, boolean isTablet) {
         NewTabPageUtils.updateTilesLayoutTopMargin(
-                mMvTilesLayout, shouldShowLogo, isWhiteBackgroundOnSearchBoxApplied, isTablet);
+                mMvTilesContainerLayout,
+                shouldShowLogo,
+                isWhiteBackgroundOnSearchBoxApplied,
+                isTablet);
     }
 
     public void onSwitchToForeground() {
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 bc02833..111d5a7 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
@@ -1807,16 +1807,18 @@
                 || (getNewTabPageForCurrentTab() != null);
     }
 
-    private void back(int metaState) {
+    private void back(int metaState, int buttonState) {
         setUrlBarFocus(false, OmniboxFocusReason.UNFOCUS);
         boolean hasControl = (metaState & KeyEvent.META_CTRL_ON) != 0;
         boolean hasShift = (metaState & KeyEvent.META_SHIFT_ON) != 0;
-        if (hasControl && hasShift) {
+        boolean isMiddleClick = (buttonState & MotionEvent.BUTTON_TERTIARY) != 0;
+
+        if ((hasControl && hasShift) || (isMiddleClick && hasShift)) {
             // Holding ALT is allowed as well (reference desktop behavior).
             final boolean isSuccess =
                     mToolbarTabController.backInNewTab(/* foregroundNewTab= */ true);
             if (isSuccess) RecordUserAction.record("MobileToolbarBackInNewForegroundTab");
-        } else if (hasControl) {
+        } else if (hasControl || isMiddleClick) {
             final boolean isSuccess =
                     mToolbarTabController.backInNewTab(/* foregroundNewTab= */ false);
             if (isSuccess) RecordUserAction.record("MobileToolbarBackInNewBackgroundTab");
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
index 0fe2017..b22bfda8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
@@ -460,25 +460,27 @@
                 new Runnable() {
                     @Override
                     public void run() {
-                        NewTabPageLayout ntpLayout = mNtp.getNewTabPageLayout();
+                        NewTabPageCoordinator ntpCoordinator = mNtp.getNewTabPageCoordinator();
                         View logoView = mNtp.getLayout().findViewById(R.id.search_provider_logo);
                         Assert.assertEquals(View.VISIBLE, logoView.getVisibility());
 
-                        ntpLayout.setSearchProviderInfo(/* hasLogo= */ false, /* isGoogle= */ true);
+                        ntpCoordinator.setSearchProviderInfo(
+                                /* hasLogo= */ false, /* isGoogle= */ true);
                         // Mock to notify the template URL service observer.
                         when(mTemplateUrlService.doesDefaultSearchEngineHaveLogo())
                                 .thenReturn(false);
                         when(mTemplateUrlService.isDefaultSearchEngineGoogle()).thenReturn(true);
-                        ntpLayout
+                        ntpCoordinator
                                 .getLogoCoordinatorForTesting()
                                 .onTemplateURLServiceChangedForTesting();
                         Assert.assertEquals(View.GONE, logoView.getVisibility());
 
-                        ntpLayout.setSearchProviderInfo(/* hasLogo= */ true, /* isGoogle= */ true);
+                        ntpCoordinator.setSearchProviderInfo(
+                                /* hasLogo= */ true, /* isGoogle= */ true);
                         // Mock to notify the template URL service observer.
                         when(mTemplateUrlService.doesDefaultSearchEngineHaveLogo())
                                 .thenReturn(true);
-                        ntpLayout
+                        ntpCoordinator
                                 .getLogoCoordinatorForTesting()
                                 .onTemplateURLServiceChangedForTesting();
                         Assert.assertEquals(View.VISIBLE, logoView.getVisibility());
@@ -802,8 +804,8 @@
     @Feature({"NewTabPage"})
     public void testRecordHistogramLogoClick_Ntp() {
         LogoBridgeJni.setInstanceForTesting(mLogoBridgeJniMock);
-        NewTabPageLayout ntpLayout = mNtp.getNewTabPageLayout();
-        LogoCoordinator logoCoordinator = ntpLayout.getLogoCoordinatorForTesting();
+        NewTabPageCoordinator ntpCoordinator = mNtp.getNewTabPageCoordinator();
+        LogoCoordinator logoCoordinator = ntpCoordinator.getLogoCoordinatorForTesting();
         logoCoordinator.setLogoBridgeForTesting(mLogoBridge);
         logoCoordinator.setOnLogoClickUrlForTesting(TEST_URL);
         HistogramWatcher histogramWatcher =
@@ -996,7 +998,7 @@
                 new Callable<>() {
                     @Override
                     public Boolean call() {
-                        return mNtp.getNewTabPageLayout().urlFocusAnimationsDisabled();
+                        return mNtp.getNewTabPageCoordinator().urlFocusAnimationsDisabled();
                     }
                 });
     }
@@ -1021,7 +1023,7 @@
         CriteriaHelper.pollUiThread(
                 () -> {
                     Criteria.checkThat(
-                            ntp.getNewTabPageLayout().getUrlFocusChangeAnimationPercent(),
+                            ntp.getNewTabPageCoordinator().getUrlFocusChangeAnimationPercent(),
                             is(percent));
                 });
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/NewTabPageUtilUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/NewTabPageUtilUnitTest.java
index d328f1f..2b060bf 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/NewTabPageUtilUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/NewTabPageUtilUnitTest.java
@@ -34,7 +34,9 @@
 import org.chromium.components.browser_ui.widget.displaystyle.UiConfig.DisplayStyle;
 import org.chromium.components.browser_ui.widget.displaystyle.VerticalDisplayStyle;
 
-/** Unit tests for helper functions in {@link NewTabPage} and {@link NewTabPageLayout} classes. */
+/**
+ * Unit tests for helper functions in {@link NewTabPage} and {@link NewTabPageCoordinator} classes.
+ */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class NewTabPageUtilUnitTest {
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 f6c77b6..612f745 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
@@ -74,6 +74,7 @@
 public class MostVisitedMediatorUnitTest {
     @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Mock UiConfig mUiConfig;
+    @Mock ViewGroup mMvTilesContainerLayout;
     @Mock MostVisitedTilesLayout mMvTilesLayout;
     @Mock Tile mTile;
     @Mock SuggestionsTileView mTileView;
@@ -557,7 +558,7 @@
         ViewGroup.MarginLayoutParams marginLayoutParams =
                 new ViewGroup.MarginLayoutParams(
                         ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-        when(mMvTilesLayout.getLayoutParams()).thenReturn(marginLayoutParams);
+        when(mMvTilesContainerLayout.getLayoutParams()).thenReturn(marginLayoutParams);
         when(mMvTilesLayout.contentFitsOnTablet(totalWidth)).thenReturn(true);
 
         // Test case of regular tablets.
@@ -591,6 +592,7 @@
 
     private void createMediator(boolean isTablet) {
         mMvTilesLayout = Mockito.mock(MostVisitedTilesLayout.class);
+        when(mMvTilesContainerLayout.findViewById(R.id.mv_tiles_layout)).thenReturn(mMvTilesLayout);
 
         when(mMvTilesLayout.getResources()).thenReturn(mResources);
         when(mMvTilesLayout.getChildCount()).thenReturn(1);
@@ -602,7 +604,7 @@
                 new MostVisitedTilesMediator(
                         mContext,
                         mUiConfig,
-                        mMvTilesLayout,
+                        mMvTilesContainerLayout,
                         mTileRenderer,
                         mModel,
                         isTablet,
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt
index cd47db4f..5b83c7d 100644
--- a/chrome/android/profiles/arm.newest.txt
+++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-147.0.7725.0_pre1595991_rc-r1-merged.afdo.bz2
+chromeos-chrome-arm-148.0.7729.0_pre1597507_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index bdff405..8b7ae1d 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -2987,33 +2987,6 @@
   <message name="IDS_NEW_PORTAL_DETECTION_NOTIFICATION_BUTTON" desc="The text to display on the button when the user needs to sign in to the captive portal">
     Sign in
   </message>
-  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIRED" desc="Title for the system notification that current wired network is behind captive portal">
-    Connect to network
-  </message>
-  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_TITLE_WIFI" desc="Title for the system notification that current Wi-Fi network is behind captive portal">
-    Connect to Wi-Fi network
-  </message>
-  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIRED" desc="Body of the system notification that current wired network is behind captive portal">
-    The network you are using (<ph name="NETWORK_ID">$1<ex>Public Network</ex></ph>) may require you to visit its login page.
-  </message>
-  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_WIFI" desc="Body of the system notification that current Wi-Fi network is behind captive portal">
-    The Wi-Fi network you are using (<ph name="NETWORK_ID">$1<ex>Public Network</ex></ph>) may require you to visit its login page.
-  </message>
-  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_ASK_WIFI" desc="Body of the system notification that asks a user whether to use an extension to authenticate to the network">
-    The Wi-Fi network you are using (<ph name="NETWORK_ID">$1<ex>Public Wifi</ex></ph>) may require authentication.
-  </message>
-  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_MESSAGE_FAILED_WIFI" desc="Body of the system notification that authentication failed and current network is still behind captive portal">
-    Authentication failed. Click to visit the login page for the Wi-Fi network you are using (<ph name="NETWORK_ID">$1<ex>Public Wifi</ex></ph>).
-  </message>
-  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION" desc="Button caption to connect using the authenticating extension">
-    Connect using <ph name="ExtensionName">$1<ex>WiFi Company Authenticator</ex></ph>
-  </message>
-  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_EXTENSION_RETRY" desc="Button caption to connect using the authenticating extension">
-    Retry using <ph name="ExtensionName">$1<ex>WiFi Company Authenticator</ex></ph>
-  </message>
-  <message name="IDS_PORTAL_DETECTION_NOTIFICATION_BUTTON_PORTAL" desc="Button caption to open the captive portal login page.">
-    Visit captive portal login page
-  </message>
 
   <!-- 3G data Notifications -->
   <message name="IDS_MOBILE_DATA_NOTIFICATION_TITLE" desc="Title of notification telling that mobile data is enabled.">
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 93ff0e8..6ef44799 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -8884,6 +8884,9 @@
       <message name="IDS_NTP_ACTION_CHIP_DISABLE_TEXT" desc="Text for menu option to disable action chip feature.">
         Disable suggestions
       </message>
+      <message name="IDS_NTP_ACTION_CHIPS_UNDO_DISABLEMENT_TOAST_MESSAGE" desc="Text for the toast that appears when the user disables action chips from the NTP. Clicking on it re-enables the action chips.">
+        You won't see suggestions on this page again
+      </message>
       <message name="IDS_COMPOSE_REMOVE_TOOL_CHIP_A11Y_LABEL" desc="A11y text for removing a tool in the composebox.">
         Remove <ph name="TOOL_NAME">$1<ex>Deep Search</ex></ph>
       </message>
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_ACTION_CHIPS_UNDO_DISABLEMENT_TOAST_MESSAGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_ACTION_CHIPS_UNDO_DISABLEMENT_TOAST_MESSAGE.png.sha1
new file mode 100644
index 0000000..86f88ee
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_NTP_ACTION_CHIPS_UNDO_DISABLEMENT_TOAST_MESSAGE.png.sha1
@@ -0,0 +1 @@
+abecad12f2fe7b2a294234e0067248f73bfc4333
\ No newline at end of file
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index b56b8a98..ef50431 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -22,6 +22,10 @@
     "add_chrome_refresh.icon",
     "add_circle.icon",
     "add_photo_alternate.icon",
+    "arrow_back.icon",
+    "arrow_downward.icon",
+    "arrow_forward.icon",
+    "arrow_upward.icon",
     "astrophotography_mode.icon",
     "attach_file.icon",
     "auto_tab_groups.icon",
diff --git a/chrome/app/vector_icons/arrow_back.icon b/chrome/app/vector_icons/arrow_back.icon
new file mode 100644
index 0000000..b4cae2c
--- /dev/null
+++ b/chrome/app/vector_icons/arrow_back.icon
@@ -0,0 +1,16 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+FILL_RULE_NONZERO,
+R_MOVE_TO, 6.88f, 10.75f,
+R_LINE_TO, 4.19f, 4.19f,
+LINE_TO, 10, 16,
+R_LINE_TO, -6, -6,
+R_LINE_TO, 6, -6,
+R_LINE_TO, 1.06f, 1.06f,
+LINE_TO, 6.88f, 9.25f,
+H_LINE_TO, 16,
+R_V_LINE_TO, 1.5f,
+CLOSE
diff --git a/chrome/app/vector_icons/arrow_downward.icon b/chrome/app/vector_icons/arrow_downward.icon
new file mode 100644
index 0000000..4d09d08
--- /dev/null
+++ b/chrome/app/vector_icons/arrow_downward.icon
@@ -0,0 +1,16 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+FILL_RULE_NONZERO,
+MOVE_TO, 9.25f, 4,
+R_V_LINE_TO, 9.13f,
+LINE_TO, 5.06f, 8.94f,
+LINE_TO, 4, 10,
+R_LINE_TO, 6, 6,
+R_LINE_TO, 6, -6,
+R_LINE_TO, -1.06f, -1.06f,
+R_LINE_TO, -4.19f, 4.19f,
+V_LINE_TO, 4,
+CLOSE
diff --git a/chrome/app/vector_icons/arrow_forward.icon b/chrome/app/vector_icons/arrow_forward.icon
new file mode 100644
index 0000000..311382bd
--- /dev/null
+++ b/chrome/app/vector_icons/arrow_forward.icon
@@ -0,0 +1,16 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+FILL_RULE_NONZERO,
+MOVE_TO, 13.13f, 10.75f,
+H_LINE_TO, 4,
+R_V_LINE_TO, -1.5f,
+R_H_LINE_TO, 9.13f,
+LINE_TO, 8.94f, 5.06f,
+LINE_TO, 10, 4,
+R_LINE_TO, 6, 6,
+R_LINE_TO, -6, 6,
+R_LINE_TO, -1.06f, -1.06f,
+CLOSE
diff --git a/chrome/app/vector_icons/arrow_upward.icon b/chrome/app/vector_icons/arrow_upward.icon
new file mode 100644
index 0000000..8806fe8
--- /dev/null
+++ b/chrome/app/vector_icons/arrow_upward.icon
@@ -0,0 +1,16 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+FILL_RULE_NONZERO,
+MOVE_TO, 9.25f, 16,
+V_LINE_TO, 6.88f,
+R_LINE_TO, -4.19f, 4.19f,
+LINE_TO, 4, 10,
+R_LINE_TO, 6, -6,
+R_LINE_TO, 6, 6,
+R_LINE_TO, -1.06f, 1.06f,
+R_LINE_TO, -4.19f, -4.19f,
+V_LINE_TO, 16,
+CLOSE
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index d313f89f..3e56fb02 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -323,15 +323,6 @@
     "contextual_cueing/contextual_cueing_service.cc",
     "contextual_cueing/contextual_cueing_service.h",
     "contextual_cueing/contextual_cueing_service_factory.cc",
-    "data_sharing/data_sharing_navigation_throttle.cc",
-    "data_sharing/data_sharing_navigation_throttle.h",
-    "data_sharing/data_sharing_navigation_utils.cc",
-    "data_sharing/data_sharing_navigation_utils.h",
-    "data_sharing/data_sharing_service_factory.cc",
-    "data_sharing/migration/migratable_sync_service_coordinator_factory.cc",
-    "data_sharing/migration/migratable_sync_service_coordinator_factory.h",
-    "data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory.cc",
-    "data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory.h",
     "defaults.cc",
     "defaults.h",
     "download/background_download_service_factory.cc",
@@ -703,16 +694,6 @@
     "page_content_annotations/page_content_extraction_service_factory.cc",
     "page_content_annotations/page_content_screenshot_service_factory.cc",
     "page_content_annotations/page_content_screenshot_service_factory.h",
-    "page_info/about_this_site_service_factory.cc",
-    "page_info/about_this_site_service_factory.h",
-    "page_info/about_this_site_tab_helper.cc",
-    "page_info/about_this_site_tab_helper.h",
-    "page_info/merchant_trust_service_delegate.cc",
-    "page_info/merchant_trust_service_delegate.h",
-    "page_info/merchant_trust_service_factory.cc",
-    "page_info/merchant_trust_service_factory.h",
-    "page_info/privacy_policy_insights_service_factory.cc",
-    "page_info/privacy_policy_insights_service_factory.h",
     "page_load_metrics/observers/bookmark_bar_page_load_metrics_observer.cc",
     "page_load_metrics/observers/bookmark_bar_page_load_metrics_observer.h",
     "page_load_metrics/observers/captcha_metrics_observer.cc",
@@ -1674,6 +1655,7 @@
     "//chrome/browser/custom_handlers",
     "//chrome/browser/data_saver",
     "//chrome/browser/data_sharing",
+    "//chrome/browser/data_sharing:impl",
     "//chrome/browser/desktop_to_mobile_promos:utils",
     "//chrome/browser/devtools",
     "//chrome/browser/diagnostics",
@@ -2848,11 +2830,6 @@
       "commerce/price_tracking/android/price_tracking_notification_bridge.cc",
       "commerce/price_tracking/android/price_tracking_notification_bridge.h",
       "composeplate/android/composeplate_utils.cc",
-      "data_sharing/android/data_sharing_service_factory_android.cc",
-      "data_sharing/android/data_sharing_ui_delegate_android.cc",
-      "data_sharing/android/data_sharing_ui_delegate_android.h",
-      "data_sharing/data_sharing_service_factory_bridge.cc",
-      "data_sharing/data_sharing_service_factory_bridge.h",
       "device_reauth/android/device_authenticator_android.cc",
       "device_reauth/android/device_authenticator_android.h",
       "device_reauth/android/device_authenticator_bridge.h",
@@ -3002,7 +2979,6 @@
       "page_content_annotations/android/page_content_extraction_service_factory_android.cc",
       "page_content_annotations/android/page_content_extraction_tab_model_observer_android.cc",
       "page_content_annotations/android/page_content_extraction_tab_model_observer_android.h",
-      "page_info/about_this_site_controller_android.cc",
       "page_load_metrics/observers/android_page_load_metrics_observer.cc",
       "page_load_metrics/observers/android_page_load_metrics_observer.h",
       "password_manager/android/account_chooser_dialog_android.cc",
@@ -3534,6 +3510,12 @@
       # - c/b/android/android_theme_resources.h
       # - c/b/android/resource_mapper.h
       "//chrome/browser/lookalikes:impl",
+
+      # TODO(crbug.com/353332589): Remove this circular dependency when the following
+      # headers get componentized:
+      # - c/b/android/android_theme_resources.h
+      # - c/b/android/resource_mapper.h
+      "//chrome/browser/page_info:impl",
     ]
 
     if (safe_browsing_mode == 2) {
@@ -3596,12 +3578,6 @@
       "component_updater/soda_language_pack_component_installer.h",
       "component_updater/zxcvbn_data_component_installer.cc",
       "component_updater/zxcvbn_data_component_installer.h",
-      "data_sharing/desktop/data_sharing_conversion_utils.cc",
-      "data_sharing/desktop/data_sharing_conversion_utils.h",
-      "data_sharing/desktop/data_sharing_sdk_delegate_desktop.cc",
-      "data_sharing/desktop/data_sharing_sdk_delegate_desktop.h",
-      "data_sharing/desktop/data_sharing_ui_delegate_desktop.cc",
-      "data_sharing/desktop/data_sharing_ui_delegate_desktop.h",
       "download/default_download_dir_policy_handler.cc",
       "download/default_download_dir_policy_handler.h",
       "download/download_auto_open_policy_handler.cc",
@@ -3831,8 +3807,6 @@
       "notifications/profile_notification.h",
       "notifications/screen_capture_notification_blocker.cc",
       "notifications/screen_capture_notification_blocker.h",
-      "page_info/web_view_side_panel_throttle.cc",
-      "page_info/web_view_side_panel_throttle.h",
       "page_load_metrics/observers/initial_webui_page_load_metrics_observer.cc",
       "page_load_metrics/observers/initial_webui_page_load_metrics_observer.h",
       "page_load_metrics/observers/non_tab_webui_page_load_metrics_observer.cc",
@@ -4126,6 +4100,7 @@
       "//chrome/browser/importer:impl",
       "//chrome/browser/indigo",
       "//chrome/browser/indigo:impl",
+      "//chrome/browser/indigo/onboarding",
       "//chrome/browser/lens/region_search",
       "//chrome/browser/media/router:mojo_impl",
       "//chrome/browser/media/router:providers_wired_display_impl",
@@ -4473,7 +4448,6 @@
       # componentized:
       # - c/b/history_embeddings/history_embeddings_utils.h
       # - c/b/defaults.h"
-      # - c/b/page_info/merchant_trust_service_factory.h
       # - c/b/translate/translate_service.h
       "//chrome/browser/ui/views/location_bar:impl",
 
@@ -4508,6 +4482,9 @@
       # TODO(crbug.com/353332589): Remove this circular dependency when
       # c/b/net/system_network_context_manager.h gets componentized.
       "//chrome/browser/digital_credentials:impl",
+
+      # Circular dependency on file_select_helper.h.
+      "//chrome/browser/indigo/onboarding",
     ]
 
     if (is_win || is_chromeos) {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 27b39c8..dbce114 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2040,6 +2040,9 @@
 const FeatureEntry::FeatureParam kOmniboxAimMultiContextCompactFusebox[] = {
     {"multi_context", "true"},
     {"compact_fusebox", "true"}};
+const FeatureEntry::FeatureParam kOmniboxAimOriginalComposeplate[] = {
+    {"multi_context", "true"},
+    {"redirect_composeplate_button", "false"}};
 const FeatureEntry::FeatureParam kOmniboxAimModelPickerAndCanvas[] = {
     {"multi_context", "true"},
     {"show_model_picker", "true"}};
@@ -2054,6 +2057,7 @@
     {"MC Button with Hint", kOmniboxAimMultiContextDedicatedModeButtonWithHint,
      "3395755"},
     {"MC Compact", kOmniboxAimMultiContextCompactFusebox, "3395755"},
+    {"Original Composeplate", kOmniboxAimOriginalComposeplate, "3395755"},
     {"Model Picker & Canvas", kOmniboxAimModelPickerAndCanvas, "3395755"},
 };
 
@@ -7919,10 +7923,6 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS)
-    {"disable-quick-answers-v2-translation",
-     flag_descriptions::kDisableQuickAnswersV2TranslationName,
-     flag_descriptions::kDisableQuickAnswersV2TranslationDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(chromeos::features::kDisableQuickAnswersV2Translation)},
     {"quick-answers-rich-card", flag_descriptions::kQuickAnswersRichCardName,
      flag_descriptions::kQuickAnswersRichCardDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kQuickAnswersRichCard)},
@@ -12299,13 +12299,6 @@
      kOsDesktop,
      FEATURE_VALUE_TYPE(contextual_tasks::kContextualTasksContextLibrary)},
 
-    {"contextual-tasks-expand-button",
-     contextual_tasks::flag_descriptions::kContextualTasksExpandButtonName,
-     contextual_tasks::flag_descriptions::
-         kContextualTasksExpandButtonDescription,
-     kOsDesktop,
-     FEATURE_VALUE_TYPE(contextual_tasks::kContextualTasksExpandButton)},
-
 #if !BUILDFLAG(IS_ANDROID)
     {"create-new-tab-group-app-menu-top-level",
      flag_descriptions::kCreateNewTabGroupAppMenuTopLevelName,
diff --git a/chrome/browser/actor/action_tracker_for_metrics.cc b/chrome/browser/actor/action_tracker_for_metrics.cc
index 1b2057e..61e2c67 100644
--- a/chrome/browser/actor/action_tracker_for_metrics.cc
+++ b/chrome/browser/actor/action_tracker_for_metrics.cc
@@ -32,6 +32,9 @@
     }
   }
   base::UmaHistogramCounts100("Actor.Task.SubsequentWaits", total_count);
+
+  base::UmaHistogramCounts100("Autofill.Actor.AutofillAttentionDialogsPerTask",
+                              autofill_attention_dialog_count_);
 }
 
 void ActionTrackerForMetrics::WillMoveToState(ActorTask::State state) {
@@ -76,4 +79,8 @@
   last_tool_name_in_current_sequence_.clear();
 }
 
+void ActionTrackerForMetrics::OnAutofillAttentionDialogPresented() {
+  autofill_attention_dialog_count_++;
+}
+
 }  // namespace actor
diff --git a/chrome/browser/actor/action_tracker_for_metrics.h b/chrome/browser/actor/action_tracker_for_metrics.h
index 8dd11043..057775e 100644
--- a/chrome/browser/actor/action_tracker_for_metrics.h
+++ b/chrome/browser/actor/action_tracker_for_metrics.h
@@ -30,6 +30,8 @@
 
   void OnFinishedAct(const mojom::ActionResult& result);
 
+  void OnAutofillAttentionDialogPresented();
+
  private:
   // Caches the last tool name found in the current sequence. This is only
   // committed to the persistent state if the sequence succeeded.
@@ -44,6 +46,9 @@
   // A map from the name of a tool request to the number of times a wait action
   // was executed immediately after it within this task's lifetime.
   std::map<std::string, int> subsequent_waits_per_tool_name_;
+
+  // The number of autofill attention dialogs presented during the task.
+  int autofill_attention_dialog_count_ = 0;
 };
 
 }  // namespace actor
diff --git a/chrome/browser/actor/actor_task.h b/chrome/browser/actor/actor_task.h
index 9905f535..fc74ca30 100644
--- a/chrome/browser/actor/actor_task.h
+++ b/chrome/browser/actor/actor_task.h
@@ -222,6 +222,10 @@
 
   Profile* GetProfile() const;
 
+  ActionTrackerForMetrics& action_tracker_for_metrics() const {
+    return *action_tracker_for_metrics_;
+  }
+
   ActorKeyedService& actor_keyed_service() const { return service_.get(); }
 
   // These observations will be added to the final ActionsResult returned by the
diff --git a/chrome/browser/actor/execution_engine.cc b/chrome/browser/actor/execution_engine.cc
index 8073d7ab..6e91861 100644
--- a/chrome/browser/actor/execution_engine.cc
+++ b/chrome/browser/actor/execution_engine.cc
@@ -27,6 +27,7 @@
 #include "base/types/id_type.h"
 #include "base/types/optional_ref.h"
 #include "base/types/pass_key.h"
+#include "chrome/browser/actor/action_tracker_for_metrics.h"
 #include "chrome/browser/actor/actor_features.h"
 #include "chrome/browser/actor/actor_keyed_service.h"
 #include "chrome/browser/actor/actor_metrics.h"
@@ -1179,6 +1180,7 @@
   task_->delegate()->RequestToShowAutofillSuggestionsDialog(
       task_->id(), std::move(requests), std::move(event_handler),
       std::move(callback));
+  task_->action_tracker_for_metrics().OnAutofillAttentionDialogPresented();
 }
 
 void ExecutionEngine::InterruptFromTool() {
diff --git a/chrome/browser/actor/tools/action_tracker_for_metrics_browsertest.cc b/chrome/browser/actor/tools/action_tracker_for_metrics_browsertest.cc
index 266ee62..cabf622 100644
--- a/chrome/browser/actor/tools/action_tracker_for_metrics_browsertest.cc
+++ b/chrome/browser/actor/tools/action_tracker_for_metrics_browsertest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/actor/action_tracker_for_metrics.h"
+
 #include <memory>
 #include <optional>
 #include <utility>
@@ -359,5 +361,24 @@
                                       /*expected_bucket_count=*/1);
 }
 
+IN_PROC_BROWSER_TEST_F(ActionTrackerForMetricsTest,
+                       AutofillAttentionDialog_Recorded) {
+  base::HistogramTester histogram_tester;
+
+  actor_task()
+      .action_tracker_for_metrics()
+      .OnAutofillAttentionDialogPresented();
+  actor_task()
+      .action_tracker_for_metrics()
+      .OnAutofillAttentionDialogPresented();
+
+  StopAllTasks();
+
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.Actor.AutofillAttentionDialogsPerTask",
+      /*sample=*/2,
+      /*expected_bucket_count=*/1);
+}
+
 }  // namespace
 }  // namespace actor
diff --git a/chrome/browser/ash/app_list/BUILD.gn b/chrome/browser/ash/app_list/BUILD.gn
index 0479e2f..f56034b 100644
--- a/chrome/browser/ash/app_list/BUILD.gn
+++ b/chrome/browser/ash/app_list/BUILD.gn
@@ -86,12 +86,12 @@
     "//chrome/browser/search_engines",
     "//chrome/browser/ui:browser_navigator_params_headers",
     "//chrome/browser/ui/ash/app_icon_color_cache",
-    "//chrome/browser/ui/webui/ash/settings/app_management",
     "//chrome/browser/web_applications",
     "//chrome/common",
     "//chromeos/ash/components/browser_context_helper",
     "//chromeos/ash/components/file_manager:constants",
     "//chromeos/ash/experiences/arc:arc_app_constants",
+    "//chromeos/ash/experiences/settings_ui",
     "//chromeos/ash/services/assistant/public/cpp",
     "//chromeos/constants",
     "//components/app_constants",
diff --git a/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc b/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc
index 7164c67b..17b2c0d 100644
--- a/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc
+++ b/chrome/browser/ash/app_list/app_list_client_impl_browsertest.cc
@@ -86,7 +86,6 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/testing_profile.h"
diff --git a/chrome/browser/ash/app_list/app_list_controller_delegate.cc b/chrome/browser/ash/app_list/app_list_controller_delegate.cc
index a07bd5c..15a60f8 100644
--- a/chrome/browser/ash/app_list/app_list_controller_delegate.cc
+++ b/chrome/browser/ash/app_list/app_list_controller_delegate.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/check_deref.h"
 #include "base/metrics/histogram_macros.h"
 #include "build/build_config.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
@@ -15,9 +16,11 @@
 #include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h"
 #include "chrome/common/extensions/extension_constants.h"
+#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
+#include "chromeos/ash/experiences/settings_ui/settings_app_manager.h"
 #include "components/services/app_service/public/cpp/app_types.h"
+#include "components/user_manager/user.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
@@ -60,16 +63,24 @@
                       .GetAppType(app_id);
   DCHECK_NE(app_type, apps::AppType::kUnknown);
 
+  std::string sub_page;
+  std::optional<ash::SettingsAppManager::EntryPoint> entry_point;
   if (app_type == apps::AppType::kWeb ||
       app_type == apps::AppType::kSystemWeb) {
-    chrome::ShowAppManagementPage(profile, app_id,
-                                  ash::settings::AppManagementEntryPoint::
-                                      kAppListContextMenuAppInfoWebApp);
+    sub_page = ash::SettingsAppManager::CreateAppManagementPagePath(app_id);
+    entry_point =
+        ash::SettingsAppManager::EntryPoint::kAppListContextMenuAppInfoWebApp;
   } else {
-    chrome::ShowAppManagementPage(profile, app_id,
-                                  ash::settings::AppManagementEntryPoint::
-                                      kAppListContextMenuAppInfoChromeApp);
+    sub_page = ash::SettingsAppManager::CreateAppManagementPagePath(app_id);
+    entry_point = ash::SettingsAppManager::EntryPoint::
+        kAppListContextMenuAppInfoChromeApp;
   }
+
+  const user_manager::User* user =
+      ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile);
+  ash::SettingsAppManager::Get()->Open(
+      CHECK_DEREF(user), ash::SettingsAppManager::OpenParams{
+                             .sub_page = sub_page, .entry_point = entry_point});
 }
 
 void AppListControllerDelegate::UninstallApp(Profile* profile,
diff --git a/chrome/browser/ash/app_list/app_service/BUILD.gn b/chrome/browser/ash/app_list/app_service/BUILD.gn
index 93eb0e9..5a46229c 100644
--- a/chrome/browser/ash/app_list/app_service/BUILD.gn
+++ b/chrome/browser/ash/app_list/app_service/BUILD.gn
@@ -49,7 +49,8 @@
     "//chrome/browser/ash/plugin_vm",
     "//chrome/browser/ash/remote_apps",
     "//chrome/browser/profiles:profile",
-    "//chrome/browser/ui/webui/ash/settings/app_management",
+    "//chromeos/ash/components/browser_context_helper",
+    "//chromeos/ash/experiences/settings_ui",
     "//chromeos/constants",
     "//components/app_constants",
     "//components/services/app_service",
diff --git a/chrome/browser/ash/app_list/app_service/app_service_app_model_builder_unittest.cc b/chrome/browser/ash/app_list/app_service/app_service_app_model_builder_unittest.cc
index ec1f2e08..181abc7 100644
--- a/chrome/browser/ash/app_list/app_service/app_service_app_model_builder_unittest.cc
+++ b/chrome/browser/ash/app_list/app_service/app_service_app_model_builder_unittest.cc
@@ -52,7 +52,6 @@
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension_constants.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
diff --git a/chrome/browser/ash/app_list/app_service/app_service_context_menu.cc b/chrome/browser/ash/app_list/app_service/app_service_context_menu.cc
index 875dec00..cd0a604 100644
--- a/chrome/browser/ash/app_list/app_service/app_service_context_menu.cc
+++ b/chrome/browser/ash/app_list/app_service/app_service_context_menu.cc
@@ -6,6 +6,7 @@
 
 #include "ash/public/cpp/app_menu_constants.h"
 #include "ash/public/cpp/new_window_delegate.h"
+#include "base/check_deref.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
@@ -28,9 +29,11 @@
 #include "chrome/browser/extensions/menu_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h"
+#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
+#include "chromeos/ash/experiences/settings_ui/settings_app_manager.h"
 #include "components/app_constants/constants.h"
 #include "components/services/app_service/public/cpp/types_util.h"
+#include "components/user_manager/user.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/context_menu_params.h"
@@ -381,9 +384,15 @@
 
 void AppServiceContextMenu::ShowAppInfo() {
   if (app_type_ == apps::AppType::kArc) {
-    chrome::ShowAppManagementPage(
-        profile(), app_id(),
-        ash::settings::AppManagementEntryPoint::kAppListContextMenuAppInfoArc);
+    const user_manager::User* user =
+        ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile());
+    ash::SettingsAppManager::Get()->Open(
+        CHECK_DEREF(user),
+        ash::SettingsAppManager::OpenParams{
+            .sub_page =
+                ash::SettingsAppManager::CreateAppManagementPagePath(app_id()),
+            .entry_point = ash::SettingsAppManager::EntryPoint::
+                kAppListContextMenuAppInfoArc});
     return;
   }
 
diff --git a/chrome/browser/ash/app_list/arc/arc_package_syncable_service.cc b/chrome/browser/ash/app_list/arc/arc_package_syncable_service.cc
index f49bd51..a72e58c 100644
--- a/chrome/browser/ash/app_list/arc/arc_package_syncable_service.cc
+++ b/chrome/browser/ash/app_list/arc/arc_package_syncable_service.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/ash/app_list/arc/arc_package_syncable_service_factory.h"
 #include "chrome/browser/ash/arc/session/arc_session_manager.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/experiences/arc/arc_util.h"
 #include "chromeos/ash/experiences/arc/session/connection_holder.h"
 #include "components/prefs/scoped_user_pref_update.h"
diff --git a/chrome/browser/ash/app_list/search/help_app_zero_state_provider.cc b/chrome/browser/ash/app_list/search/help_app_zero_state_provider.cc
index c79c0f05..1f6565a 100644
--- a/chrome/browser/ash/app_list/search/help_app_zero_state_provider.cc
+++ b/chrome/browser/ash/app_list/search/help_app_zero_state_provider.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/ash/release_notes/release_notes_storage.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "components/prefs/pref_service.h"
 #include "components/services/app_service/public/cpp/app_launch_util.h"
diff --git a/chrome/browser/ash/app_list/search/test/app_list_search_test_helper.h b/chrome/browser/ash/app_list/search/test/app_list_search_test_helper.h
index c44f94034..ef9fa86 100644
--- a/chrome/browser/ash/app_list/search/test/app_list_search_test_helper.h
+++ b/chrome/browser/ash/app_list/search/test/app_list_search_test_helper.h
@@ -30,7 +30,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/ash/app_mode/metrics/periodic_metrics_service_unittest.cc b/chrome/browser/ash/app_mode/metrics/periodic_metrics_service_unittest.cc
index 067f573..853d3f1 100644
--- a/chrome/browser/ash/app_mode/metrics/periodic_metrics_service_unittest.cc
+++ b/chrome/browser/ash/app_mode/metrics/periodic_metrics_service_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/ash/components/network/network_handler_test_helper.h"
 #include "chromeos/ash/components/sync_wifi/network_test_helper.h"
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge.cc b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge.cc
index 3572f794..9f0250a 100644
--- a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge.cc
+++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/ash/arc/input_method_manager/arc_input_method_manager_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/extensions/api/accessibility_private.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/experiences/arc/arc_browser_context_keyed_service_factory_base.h"
 #include "chromeos/ash/experiences/arc/message_center/arc_notification_surface.h"
 #include "chromeos/ash/experiences/arc/session/arc_bridge_service.h"
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
index d86c22bc..fc5f13a 100644
--- a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
+++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
@@ -18,7 +18,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/common/extensions/api/accessibility_private.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/views/chrome_views_test_base.h"
 #include "chromeos/ash/experiences/arc/arc_util.h"
diff --git a/chrome/browser/ash/arc/enterprise/cert_store/cert_store_service_browsertest.cc b/chrome/browser/ash/arc/enterprise/cert_store/cert_store_service_browsertest.cc
index 648e8d0..589de29 100644
--- a/chrome/browser/ash/arc/enterprise/cert_store/cert_store_service_browsertest.cc
+++ b/chrome/browser/ash/arc/enterprise/cert_store/cert_store_service_browsertest.cc
@@ -38,7 +38,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/net/x509_certificate_model_nss.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/components/network/network_cert_loader.h"
diff --git a/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc b/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc
index 6a966c2..d2acb17 100644
--- a/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc
+++ b/chrome/browser/ash/arc/wallpaper/arc_wallpaper_service_unittest.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h"
 #include "chrome/browser/ui/ash/wallpaper/test_wallpaper_controller.h"
 #include "chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/components/cryptohome/system_salt_getter.h"
diff --git a/chrome/browser/ash/attestation/platform_verification_flow_unittest.cc b/chrome/browser/ash/attestation/platform_verification_flow_unittest.cc
index 5c3433ee..985a0eb6 100644
--- a/chrome/browser/ash/attestation/platform_verification_flow_unittest.cc
+++ b/chrome/browser/ash/attestation/platform_verification_flow_unittest.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/ash/attestation/platform_verification_flow.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/attestation/fake_certificate.h"
 #include "chromeos/ash/components/attestation/mock_attestation_flow.h"
 #include "chromeos/ash/components/dbus/attestation/attestation.pb.h"
diff --git a/chrome/browser/ash/attestation/tpm_challenge_key.cc b/chrome/browser/ash/attestation/tpm_challenge_key.cc
index ede98f2..5db868e 100644
--- a/chrome/browser/ash/attestation/tpm_challenge_key.cc
+++ b/chrome/browser/ash/attestation/tpm_challenge_key.cc
@@ -13,7 +13,6 @@
 #include "base/sequence_checker.h"
 #include "chrome/browser/ash/attestation/tpm_challenge_key_result.h"
 #include "chrome/browser/ash/attestation/tpm_challenge_key_subtle.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/dbus/attestation/attestation_ca.pb.h"
 #include "chromeos/ash/components/dbus/constants/attestation_constants.h"
 
diff --git a/chrome/browser/ash/attestation/tpm_challenge_key_subtle.cc b/chrome/browser/ash/attestation/tpm_challenge_key_subtle.cc
index 57b4051..581af2bd 100644
--- a/chrome/browser/ash/attestation/tpm_challenge_key_subtle.cc
+++ b/chrome/browser/ash/attestation/tpm_challenge_key_subtle.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/extensions/chrome_extension_function_details.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/attestation/attestation_flow_adaptive.h"
 #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
 #include "chromeos/ash/components/dbus/attestation/attestation_client.h"
diff --git a/chrome/browser/ash/attestation/tpm_challenge_key_subtle_unittest.cc b/chrome/browser/ash/attestation/tpm_challenge_key_subtle_unittest.cc
index b47ced5..edf56ec 100644
--- a/chrome/browser/ash/attestation/tpm_challenge_key_subtle_unittest.cc
+++ b/chrome/browser/ash/attestation/tpm_challenge_key_subtle_unittest.cc
@@ -24,7 +24,6 @@
 #include "chrome/browser/ash/platform_keys/key_permissions/user_private_token_kpm_service_factory.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/common/chrome_constants.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
diff --git a/chrome/browser/ash/camera_mic/BUILD.gn b/chrome/browser/ash/camera_mic/BUILD.gn
index 25fe6f67..0ec69492 100644
--- a/chrome/browser/ash/camera_mic/BUILD.gn
+++ b/chrome/browser/ash/camera_mic/BUILD.gn
@@ -33,7 +33,6 @@
     "//chrome/browser/ash/login/session",
     "//chrome/browser/ash/plugin_vm",
     "//chrome/browser/notifications",
-    "//chrome/browser/ui/webui/ash/settings/app_management",
     "//chromeos/ash/components/browser_context_helper",
     "//chromeos/ash/experiences/settings_ui",
     "//components/vector_icons",
diff --git a/chrome/browser/ash/camera_mic/vm_camera_mic_manager.cc b/chrome/browser/ash/camera_mic/vm_camera_mic_manager.cc
index 6d437af..41d697d 100644
--- a/chrome/browser/ash/camera_mic/vm_camera_mic_manager.cc
+++ b/chrome/browser/ash/camera_mic/vm_camera_mic_manager.cc
@@ -33,7 +33,6 @@
 #include "chrome/browser/notifications/notification_display_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
 #include "chromeos/ash/experiences/settings_ui/settings_app_manager.h"
@@ -370,27 +369,35 @@
 
   // Opens the settings page.
   void OpenSettings() const {
+    std::string sub_page;
+    std::optional<ash::SettingsAppManager::EntryPoint> entry_point;
     switch (vm_type_) {
-      case VmType::kCrostiniVm: {
-        auto* user = ash::BrowserContextHelper::Get()->GetUserByBrowserContext(
-            profile_.get());
-        ash::SettingsAppManager::Get()->Open(
-            CHECK_DEREF(user),
-            {.sub_page =
-                 chromeos::settings::mojom::kCrostiniDetailsSubpagePath});
+      case VmType::kCrostiniVm:
+        sub_page = chromeos::settings::mojom::kCrostiniDetailsSubpagePath;
         break;
-      }
       case VmType::kPluginVm:
-        chrome::ShowAppManagementPage(
-            profile_, plugin_vm::kPluginVmShelfAppId,
-            settings::AppManagementEntryPoint::kNotificationPluginVm);
+        sub_page = ash::SettingsAppManager::CreateAppManagementPagePath(
+            plugin_vm::kPluginVmShelfAppId);
+        entry_point =
+            ash::SettingsAppManager::EntryPoint::kNotificationPluginVm;
         break;
       case VmType::kBorealis:
-        chrome::ShowAppManagementPage(
-            profile_, borealis::kClientAppId,
-            settings::AppManagementEntryPoint::kAppManagementMainViewBorealis);
+        sub_page = ash::SettingsAppManager::CreateAppManagementPagePath(
+            borealis::kClientAppId);
+        entry_point =
+            ash::SettingsAppManager::EntryPoint::kAppManagementMainViewBorealis;
         break;
+      default:
+        return;
     }
+
+    const user_manager::User* user =
+        ash::BrowserContextHelper::Get()->GetUserByBrowserContext(
+            profile_.get());
+    ash::SettingsAppManager::Get()->Open(
+        CHECK_DEREF(user),
+        ash::SettingsAppManager::OpenParams{.sub_page = sub_page,
+                                            .entry_point = entry_point});
   }
 
   const raw_ptr<Profile, LeakedDanglingUntriaged> profile_;
diff --git a/chrome/browser/ash/cert_provisioning/cert_provisioning_scheduler.cc b/chrome/browser/ash/cert_provisioning/cert_provisioning_scheduler.cc
index 80c373f..875c7c3 100644
--- a/chrome/browser/ash/cert_provisioning/cert_provisioning_scheduler.cc
+++ b/chrome/browser/ash/cert_provisioning/cert_provisioning_scheduler.cc
@@ -29,7 +29,6 @@
 #include "chrome/browser/ash/platform_keys/platform_keys_service_factory.h"
 #include "chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
 #include "chromeos/ash/components/platform_keys/platform_keys.h"
diff --git a/chrome/browser/ash/child_accounts/child_status_reporting_service.cc b/chrome/browser/ash/child_accounts/child_status_reporting_service.cc
index 6800162..578804c9 100644
--- a/chrome/browser/ash/child_accounts/child_status_reporting_service.cc
+++ b/chrome/browser/ash/child_accounts/child_status_reporting_service.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash/child_accounts/child_status_reporting_service.h"
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/task/single_thread_task_runner.h"
@@ -13,7 +14,6 @@
 #include "chrome/browser/ash/policy/status_collector/child_status_collector.h"
 #include "chrome/browser/ash/policy/uploading/status_uploader.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/system/statistics_provider.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
@@ -52,7 +52,7 @@
   pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
   pref_change_registrar_->Init(pref_service);
   pref_change_registrar_->Add(
-      prefs::kUsageTimeLimit,
+      ash::prefs::kUsageTimeLimit,
       base::BindRepeating(
           &ChildStatusReportingService::OnTimeLimitsPolicyChanged,
           base::Unretained(this)));
@@ -66,7 +66,7 @@
 void ChildStatusReportingService::CreateStatusUploaderIfNeeded(
     policy::CloudPolicyClient* client) {
   const base::DictValue& time_limit =
-      pref_change_registrar_->prefs()->GetDict(prefs::kUsageTimeLimit);
+      pref_change_registrar_->prefs()->GetDict(ash::prefs::kUsageTimeLimit);
   const base::TimeDelta new_day_reset_time =
       usage_time_limit::GetTimeUsageLimitResetTime(time_limit);
 
diff --git a/chrome/browser/ash/child_accounts/child_user_service.cc b/chrome/browser/ash/child_accounts/child_user_service.cc
index 3be239a..81b975c 100644
--- a/chrome/browser/ash/child_accounts/child_user_service.cc
+++ b/chrome/browser/ash/child_accounts/child_user_service.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash/child_accounts/child_user_service.h"
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/check.h"
 #include "base/functional/bind.h"
 #include "base/metrics/histogram_functions.h"
@@ -16,7 +17,6 @@
 #include "chrome/browser/ash/child_accounts/time_limits/app_types.h"
 #include "chrome/browser/ash/child_accounts/usage_time_limit_processor.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "components/app_constants/constants.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_context.h"
@@ -83,7 +83,7 @@
 
   pref_change_registrar_.Init(profile_->GetPrefs());
   pref_change_registrar_.Add(
-      prefs::kUsageTimeLimit,
+      ash::prefs::kUsageTimeLimit,
       base::BindRepeating(&ChildUserService::ReportTimeLimitPolicy,
                           base::Unretained(this)));
 }
@@ -132,7 +132,7 @@
 
 void ChildUserService ::ReportTimeLimitPolicy() const {
   const base::DictValue& time_limit_prefs =
-      profile_->GetPrefs()->GetDict(prefs::kUsageTimeLimit);
+      profile_->GetPrefs()->GetDict(ash::prefs::kUsageTimeLimit);
 
   std::set<usage_time_limit::PolicyType> enabled_policies =
       usage_time_limit::GetEnabledTimeLimitPolicies(time_limit_prefs);
@@ -173,7 +173,7 @@
     app_time_controller_->RecordMetricsOnShutdown();
     app_time_controller_.reset();
   }
-  pref_change_registrar_.Remove(prefs::kUsageTimeLimit);
+  pref_change_registrar_.Remove(ash::prefs::kUsageTimeLimit);
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics.cc b/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics.cc
index 6d9fbec..e694e9e9 100644
--- a/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics.cc
+++ b/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics.cc
@@ -4,13 +4,13 @@
 
 #include "chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics.h"
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/check.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/unguessable_token.h"
 #include "chrome/browser/ash/child_accounts/time_limits/app_time_limit_utils.h"
 #include "chrome/browser/ash/child_accounts/time_limits/app_types.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
@@ -33,7 +33,7 @@
 void FamilyUserChromeActivityMetrics::RegisterProfilePrefs(
     PrefRegistrySimple* registry) {
   registry->RegisterTimeDeltaPref(
-      prefs::kFamilyUserMetricsChromeBrowserEngagementDuration,
+      ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration,
       base::TimeDelta());
 }
 
@@ -53,14 +53,14 @@
 
 void FamilyUserChromeActivityMetrics::OnNewDay() {
   base::TimeDelta unreported_duration = pref_service_->GetTimeDelta(
-      prefs::kFamilyUserMetricsChromeBrowserEngagementDuration);
+      ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration);
   if (unreported_duration <= base::TimeDelta())
     return;
   base::UmaHistogramCustomTimes(kChromeBrowserEngagementDurationHistogramName,
                                 unreported_duration, kMinChromeDuration,
                                 kMaxChromeDuration, kChromeDurationBuckets);
   pref_service_->ClearPref(
-      prefs::kFamilyUserMetricsChromeBrowserEngagementDuration);
+      ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration);
 }
 
 void FamilyUserChromeActivityMetrics::SetActiveSessionStartForTesting(
@@ -119,9 +119,9 @@
   } else {
     if (base::Time() < active_duration_start_ && active_duration_start_ < now) {
       base::TimeDelta unreported_duration = pref_service_->GetTimeDelta(
-          prefs::kFamilyUserMetricsChromeBrowserEngagementDuration);
+          ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration);
       pref_service_->SetTimeDelta(
-          prefs::kFamilyUserMetricsChromeBrowserEngagementDuration,
+          ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration,
           unreported_duration + now - active_duration_start_);
     }
 
diff --git a/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc b/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc
index c93cf3c7..127e8299 100644
--- a/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc
+++ b/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <vector>
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
@@ -22,7 +23,6 @@
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/browser/ui/browser_finder.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/test_browser_window_aura.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
@@ -175,7 +175,7 @@
 
   EXPECT_EQ(kHalfHour,
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
+                ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
 
   // Test multiple browsers.
   std::unique_ptr<aura::Window> window =
@@ -197,7 +197,7 @@
                         apps::InstanceState::kDestroyed);
   EXPECT_EQ(base::Hours(1),
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
+                ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
 
   // Test date change.
   task_environment()->FastForwardBy(base::Days(1));
@@ -205,7 +205,7 @@
 
   EXPECT_EQ(base::TimeDelta(),
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
+                ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
   histogram_tester.ExpectTimeBucketCount(
       FamilyUserChromeActivityMetrics::
           kChromeBrowserEngagementDurationHistogramName,
@@ -229,7 +229,7 @@
       0);
   EXPECT_EQ(base::TimeDelta(),
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
+                ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
 }
 
 // Tests destroying FamilyUserChromeActivityMetrics. OnAppInactive() will be
@@ -252,7 +252,7 @@
       0);
   EXPECT_EQ(kHalfHour,
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
+                ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
 
   // Test restart.
   InitiateFamilyUserChromeActivityMetrics();
@@ -271,7 +271,7 @@
       0);
   EXPECT_EQ(base::Hours(1),
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
+                ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
 }
 
 TEST_F(FamilyUserChromeActivityMetricsTest, ScreenStateChange) {
@@ -290,7 +290,7 @@
                         kInactiveInstanceState);
   EXPECT_EQ(kOneMinute,
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
+                ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
 
   // Test the screen off for 1 day.
   SetScreenOff(true);
@@ -300,7 +300,7 @@
 
   EXPECT_EQ(base::TimeDelta(),
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
+                ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
   histogram_tester.ExpectTimeBucketCount(
       FamilyUserChromeActivityMetrics::
           kChromeBrowserEngagementDurationHistogramName,
@@ -336,14 +336,14 @@
 
   EXPECT_EQ(base::Minutes(2),
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
+                ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
 
   task_environment()->FastForwardBy(base::Days(1));
   OnNewDay();
 
   EXPECT_EQ(base::TimeDelta(),
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
+                ash::prefs::kFamilyUserMetricsChromeBrowserEngagementDuration));
   histogram_tester.ExpectTimeBucketCount(
       FamilyUserChromeActivityMetrics::
           kChromeBrowserEngagementDurationHistogramName,
diff --git a/chrome/browser/ash/child_accounts/family_user_metrics_service.cc b/chrome/browser/ash/child_accounts/family_user_metrics_service.cc
index 55e35a6..4f9003a 100644
--- a/chrome/browser/ash/child_accounts/family_user_metrics_service.cc
+++ b/chrome/browser/ash/child_accounts/family_user_metrics_service.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash/child_accounts/family_user_metrics_service.h"
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/check.h"
 #include "base/time/time.h"
 #include "chrome/browser/ash/child_accounts/family_user_app_metrics.h"
@@ -12,7 +13,6 @@
 #include "chrome/browser/ash/child_accounts/family_user_parental_control_metrics.h"
 #include "chrome/browser/ash/child_accounts/family_user_session_metrics.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_context.h"
@@ -33,7 +33,7 @@
 // static
 void FamilyUserMetricsService::RegisterProfilePrefs(
     PrefRegistrySimple* registry) {
-  registry->RegisterIntegerPref(prefs::kFamilyUserMetricsDayId, 0);
+  registry->RegisterIntegerPref(ash::prefs::kFamilyUserMetricsDayId, 0);
 }
 
 // static
@@ -86,14 +86,15 @@
 }
 
 void FamilyUserMetricsService::CheckForNewDay() {
-  int day_id = pref_service_->GetInteger(prefs::kFamilyUserMetricsDayId);
+  int day_id = pref_service_->GetInteger(ash::prefs::kFamilyUserMetricsDayId);
   base::Time now = base::Time::Now();
   // The OnNewDay() event can fire sooner or later than 24 hours due to clock or
   // time zone changes.
   if (day_id < GetDayId(now)) {
     for (Observer& observer : observers_)
       observer.OnNewDay();
-    pref_service_->SetInteger(prefs::kFamilyUserMetricsDayId, GetDayId(now));
+    pref_service_->SetInteger(ash::prefs::kFamilyUserMetricsDayId,
+                              GetDayId(now));
   }
 }
 
diff --git a/chrome/browser/ash/child_accounts/family_user_metrics_service_unittest.cc b/chrome/browser/ash/child_accounts/family_user_metrics_service_unittest.cc
index cbd6733..388efa16 100644
--- a/chrome/browser/ash/child_accounts/family_user_metrics_service_unittest.cc
+++ b/chrome/browser/ash/child_accounts/family_user_metrics_service_unittest.cc
@@ -6,8 +6,8 @@
 
 #include <memory>
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/time/time.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
 #include "chromeos/dbus/power_manager/idle.pb.h"
@@ -74,7 +74,7 @@
   }
 
   int GetDayIdPref() {
-    return GetPrefService()->GetInteger(prefs::kFamilyUserMetricsDayId);
+    return GetPrefService()->GetInteger(ash::prefs::kFamilyUserMetricsDayId);
   }
 
   content::BrowserTaskEnvironment task_environment_{
diff --git a/chrome/browser/ash/child_accounts/family_user_parental_control_metrics_unittest.cc b/chrome/browser/ash/child_accounts/family_user_parental_control_metrics_unittest.cc
index 28b9a2f..ec32509 100644
--- a/chrome/browser/ash/child_accounts/family_user_parental_control_metrics_unittest.cc
+++ b/chrome/browser/ash/child_accounts/family_user_parental_control_metrics_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <string>
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/containers/flat_map.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
@@ -27,7 +28,6 @@
 #include "chrome/browser/ash/child_accounts/time_limits/app_types.h"
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/experiences/arc/test/fake_app_instance.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -119,7 +119,7 @@
       /*quota*/ kOneHour,
       /*last_updated=*/base::Time::Now());
 
-  GetPrefs()->SetDict(prefs::kUsageTimeLimit, policy_content.Clone());
+  GetPrefs()->SetDict(ash::prefs::kUsageTimeLimit, policy_content.Clone());
 
   histogram_tester_.ExpectBucketCount(
       ChildUserService::GetTimeLimitPolicyTypesHistogramNameForTest(),
@@ -167,7 +167,7 @@
       /*action=*/usage_time_limit::TimeLimitOverride::Action::kLock,
       /*created_at=*/base::Time::Now() - kOneDay,
       /*duration=*/base::Hours(2));
-  GetPrefs()->SetDict(prefs::kUsageTimeLimit, policy_content.Clone());
+  GetPrefs()->SetDict(ash::prefs::kUsageTimeLimit, policy_content.Clone());
 
   // The override time limit policy would not get reported since the difference
   // between reported and created time are greater than 1 day.
@@ -188,7 +188,7 @@
       /*action=*/usage_time_limit::TimeLimitOverride::Action::kLock,
       /*created_at=*/base::Time::Now() - base::Hours(23),
       /*duration=*/base::Hours(2));
-  GetPrefs()->SetDict(prefs::kUsageTimeLimit, policy_content.Clone());
+  GetPrefs()->SetDict(ash::prefs::kUsageTimeLimit, policy_content.Clone());
 
   // The override time limit policy would get reported since the created
   // time and reported time are within 1 day.
@@ -245,7 +245,7 @@
                                            base::Hours(1), base::Time::Now()));
 
     builder.SetResetTime(6, 0);
-    GetPrefs()->SetDict(prefs::kPerAppTimeLimitsPolicy,
+    GetPrefs()->SetDict(ash::prefs::kPerAppTimeLimitsPolicy,
                         builder.value().Clone());
   }
 
diff --git a/chrome/browser/ash/child_accounts/family_user_session_metrics.cc b/chrome/browser/ash/child_accounts/family_user_session_metrics.cc
index 0ce6b2a..a24e0c9c 100644
--- a/chrome/browser/ash/child_accounts/family_user_session_metrics.cc
+++ b/chrome/browser/ash/child_accounts/family_user_session_metrics.cc
@@ -7,10 +7,10 @@
 #include <string>
 #include <utility>
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/user_metrics.h"
-#include "chrome/common/pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
@@ -90,7 +90,8 @@
 void FamilyUserSessionMetrics::RegisterProfilePrefs(
     PrefRegistrySimple* registry) {
   registry->RegisterTimeDeltaPref(
-      prefs::kFamilyUserMetricsSessionEngagementDuration, base::TimeDelta());
+      ash::prefs::kFamilyUserMetricsSessionEngagementDuration,
+      base::TimeDelta());
 }
 
 FamilyUserSessionMetrics::FamilyUserSessionMetrics(PrefService* pref_service)
@@ -112,13 +113,14 @@
 
 void FamilyUserSessionMetrics::OnNewDay() {
   base::TimeDelta unreported_duration = pref_service_->GetTimeDelta(
-      prefs::kFamilyUserMetricsSessionEngagementDuration);
+      ash::prefs::kFamilyUserMetricsSessionEngagementDuration);
   if (unreported_duration <= base::TimeDelta())
     return;
   base::UmaHistogramCustomTimes(kSessionEngagementDurationHistogramName,
                                 unreported_duration, kMinSessionDuration,
                                 kMaxSessionDuration, kSessionDurationBuckets);
-  pref_service_->ClearPref(prefs::kFamilyUserMetricsSessionEngagementDuration);
+  pref_service_->ClearPref(
+      ash::prefs::kFamilyUserMetricsSessionEngagementDuration);
 }
 
 void FamilyUserSessionMetrics::SetActiveSessionStartForTesting(
@@ -144,9 +146,9 @@
         /*end=*/now);
     if (now > active_session_start_) {
       base::TimeDelta unreported_duration = pref_service_->GetTimeDelta(
-          prefs::kFamilyUserMetricsSessionEngagementDuration);
+          ash::prefs::kFamilyUserMetricsSessionEngagementDuration);
       pref_service_->SetTimeDelta(
-          prefs::kFamilyUserMetricsSessionEngagementDuration,
+          ash::prefs::kFamilyUserMetricsSessionEngagementDuration,
           unreported_duration + now - active_session_start_);
     }
 
diff --git a/chrome/browser/ash/child_accounts/family_user_session_metrics_unittest.cc b/chrome/browser/ash/child_accounts/family_user_session_metrics_unittest.cc
index 3e5c91e7..facf215 100644
--- a/chrome/browser/ash/child_accounts/family_user_session_metrics_unittest.cc
+++ b/chrome/browser/ash/child_accounts/family_user_session_metrics_unittest.cc
@@ -6,12 +6,12 @@
 
 #include <memory>
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/logging.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/metrics/user_action_tester.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
 #include "chromeos/dbus/power_manager/idle.pb.h"
 #include "chromeos/dbus/power_manager/suspend.pb.h"
@@ -161,8 +161,9 @@
   histogram_tester.ExpectUniqueTimeSample(
       FamilyUserSessionMetrics::kSessionEngagementDurationHistogramName,
       kTenMinutes, 1);
-  EXPECT_EQ(kOneHour, pref_service()->GetTimeDelta(
-                          prefs::kFamilyUserMetricsSessionEngagementDuration));
+  EXPECT_EQ(kOneHour,
+            pref_service()->GetTimeDelta(
+                ash::prefs::kFamilyUserMetricsSessionEngagementDuration));
 }
 
 TEST_F(FamilyUserSessionMetricsTest, ScreenStateChange) {
@@ -198,7 +199,7 @@
       kOneHour, 1);
   EXPECT_EQ(base::TimeDelta(),
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsSessionEngagementDuration));
+                ash::prefs::kFamilyUserMetricsSessionEngagementDuration));
 
   // Test screen on on 4 Jan 2020 0:10:00.
   SetScreenOff(false);
@@ -258,7 +259,7 @@
       FamilyUserSessionMetrics::kSessionEngagementDurationHistogramName, 2);
   EXPECT_EQ(base::TimeDelta(),
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsSessionEngagementDuration));
+                ash::prefs::kFamilyUserMetricsSessionEngagementDuration));
 }
 
 TEST_F(FamilyUserSessionMetricsTest, SuspendStateChange) {
@@ -302,7 +303,7 @@
       FamilyUserSessionMetrics::kSessionEngagementDurationHistogramName, 0);
   EXPECT_EQ(base::Minutes(20),
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsSessionEngagementDuration));
+                ash::prefs::kFamilyUserMetricsSessionEngagementDuration));
 }
 
 TEST_F(FamilyUserSessionMetricsTest, ClockBackward) {
@@ -340,7 +341,7 @@
       FamilyUserSessionMetrics::kSessionEngagementDurationHistogramName, 0);
   EXPECT_EQ(base::TimeDelta(),
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsSessionEngagementDuration));
+                ash::prefs::kFamilyUserMetricsSessionEngagementDuration));
 }
 
 // Tests destroying FamilyUserSessionMetrics without invoking
@@ -370,10 +371,10 @@
 
   // Duration metric result:
   histogram_tester.ExpectTotalCount(
-      prefs::kFamilyUserMetricsSessionEngagementDuration, 0);
+      ash::prefs::kFamilyUserMetricsSessionEngagementDuration, 0);
   EXPECT_EQ(kTenMinutes,
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsSessionEngagementDuration));
+                ash::prefs::kFamilyUserMetricsSessionEngagementDuration));
 
   // Test restart.
   InitiateFamilyUserSessionMetrics();
@@ -396,10 +397,10 @@
 
   // Duration metric result:
   histogram_tester.ExpectTotalCount(
-      prefs::kFamilyUserMetricsSessionEngagementDuration, 0);
+      ash::prefs::kFamilyUserMetricsSessionEngagementDuration, 0);
   EXPECT_EQ(base::Minutes(20),
             pref_service()->GetTimeDelta(
-                prefs::kFamilyUserMetricsSessionEngagementDuration));
+                ash::prefs::kFamilyUserMetricsSessionEngagementDuration));
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/child_accounts/parent_access_code/config_source.cc b/chrome/browser/ash/child_accounts/parent_access_code/config_source.cc
index c5aa4e5..4386dcc 100644
--- a/chrome/browser/ash/child_accounts/parent_access_code/config_source.cc
+++ b/chrome/browser/ash/child_accounts/parent_access_code/config_source.cc
@@ -7,11 +7,11 @@
 #include <optional>
 #include <utility>
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/check.h"
 #include "base/check_deref.h"
 #include "base/logging.h"
 #include "base/values.h"
-#include "chrome/common/pref_names.h"
 #include "components/account_id/account_id.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/user.h"
@@ -41,7 +41,7 @@
     }
 
     const base::Value* dictionary = known_user.FindPath(
-        user->GetAccountId(), prefs::kKnownUserParentAccessCodeConfig);
+        user->GetAccountId(), ash::prefs::kKnownUserParentAccessCodeConfig);
     if (dictionary) {
       LoadConfigForUser(user->GetAccountId(), dictionary->GetDict());
     }
@@ -60,18 +60,18 @@
 #endif  // DCHECK_IS_ON()
 
   user_manager::KnownUser known_user(&local_state_.get());
-  known_user.SetPath(account_id, ::prefs::kKnownUserParentAccessCodeConfig,
+  known_user.SetPath(account_id, ash::prefs::kKnownUserParentAccessCodeConfig,
                      base::Value(std::move(config)));
 
-  const base::Value* dictionary =
-      known_user.FindPath(account_id, prefs::kKnownUserParentAccessCodeConfig);
+  const base::Value* dictionary = known_user.FindPath(
+      account_id, ash::prefs::kKnownUserParentAccessCodeConfig);
   CHECK(dictionary);
   LoadConfigForUser(account_id, dictionary->GetDict());
 }
 
 void ConfigSource::RemoveConfigForUser(const AccountId& account_id) {
   user_manager::KnownUser(&local_state_.get())
-      .RemovePref(account_id, ::prefs::kKnownUserParentAccessCodeConfig);
+      .RemovePref(account_id, ash::prefs::kKnownUserParentAccessCodeConfig);
 }
 
 void ConfigSource::LoadConfigForUser(const AccountId& account_id,
diff --git a/chrome/browser/ash/child_accounts/parent_access_code/parent_access_service.cc b/chrome/browser/ash/child_accounts/parent_access_code/parent_access_service.cc
index b39c1937..ecc23b9 100644
--- a/chrome/browser/ash/child_accounts/parent_access_code/parent_access_service.cc
+++ b/chrome/browser/ash/child_accounts/parent_access_code/parent_access_service.cc
@@ -7,6 +7,7 @@
 #include <string>
 #include <utility>
 
+#include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/child_accounts/parent_access_controller.h"
 #include "base/check.h"
 #include "base/check_deref.h"
@@ -15,7 +16,6 @@
 #include "base/no_destructor.h"
 #include "base/timer/timer.h"
 #include "base/values.h"
-#include "chrome/common/pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
@@ -57,7 +57,7 @@
 
 // static
 void ParentAccessService::RegisterProfilePrefs(PrefRegistrySimple* registry) {
-  registry->RegisterDictionaryPref(prefs::kParentAccessCodeConfig);
+  registry->RegisterDictionaryPref(ash::prefs::kParentAccessCodeConfig);
 }
 
 // static
diff --git a/chrome/browser/ash/child_accounts/screen_time_controller.cc b/chrome/browser/ash/child_accounts/screen_time_controller.cc
index af1a50d..84605ae 100644
--- a/chrome/browser/ash/child_accounts/screen_time_controller.cc
+++ b/chrome/browser/ash/child_accounts/screen_time_controller.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 
+#include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/child_accounts/parent_access_controller.h"
 #include "ash/public/cpp/login_screen.h"
 #include "base/functional/bind.h"
@@ -22,7 +23,6 @@
 #include "chrome/browser/ash/login/lock/screen_locker.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
@@ -39,7 +39,7 @@
 // Otherwise, requesting lock does not work.
 constexpr base::TimeDelta kCheckLoginDelay = base::Seconds(15);
 
-// Dictionary keys for prefs::kScreenTimeLastState.
+// Dictionary keys for ash::prefs::kScreenTimeLastState.
 constexpr char kScreenStateLocked[] = "locked";
 constexpr char kScreenStateCurrentPolicyType[] = "active_policy";
 constexpr char kScreenStateTimeUsageLimitEnabled[] = "time_usage_limit_enabled";
@@ -68,9 +68,9 @@
 
 // static
 void ScreenTimeController::RegisterProfilePrefs(PrefRegistrySimple* registry) {
-  registry->RegisterDictionaryPref(prefs::kScreenTimeLastState);
-  registry->RegisterDictionaryPref(prefs::kTimeLimitLocalOverride);
-  registry->RegisterDictionaryPref(prefs::kUsageTimeLimit);
+  registry->RegisterDictionaryPref(ash::prefs::kScreenTimeLastState);
+  registry->RegisterDictionaryPref(ash::prefs::kTimeLimitLocalOverride);
+  registry->RegisterDictionaryPref(ash::prefs::kUsageTimeLimit);
 }
 
 ScreenTimeController::ScreenTimeController(content::BrowserContext* context)
@@ -79,7 +79,7 @@
       clock_(base::DefaultClock::GetInstance()),
       next_state_timer_(std::make_unique<base::OneShotTimer>()),
       usage_time_limit_warning_timer_(std::make_unique<base::OneShotTimer>()),
-      last_policy_(pref_service_->GetDict(prefs::kUsageTimeLimit).Clone()),
+      last_policy_(pref_service_->GetDict(ash::prefs::kUsageTimeLimit).Clone()),
       time_limit_notifier_(context) {
   session_manager::SessionManager::Get()->AddObserver(this);
   UsageTimeStateNotifier::GetInstance()->AddObserver(this);
@@ -88,7 +88,7 @@
   SystemClockClient::Get()->AddObserver(this);
   pref_change_registrar_.Init(pref_service_);
   pref_change_registrar_.Add(
-      prefs::kUsageTimeLimit,
+      ash::prefs::kUsageTimeLimit,
       base::BindRepeating(&ScreenTimeController::OnPolicyChanged,
                           base::Unretained(this)));
 
@@ -150,9 +150,9 @@
       system::TimezoneSettings::GetInstance()->GetTimezone();
   std::optional<usage_time_limit::State> last_state = GetLastStateFromPref();
   const base::DictValue& time_limit =
-      pref_service_->GetDict(prefs::kUsageTimeLimit);
+      pref_service_->GetDict(ash::prefs::kUsageTimeLimit);
   const base::DictValue& local_override =
-      pref_service_->GetDict(prefs::kTimeLimitLocalOverride);
+      pref_service_->GetDict(ash::prefs::kTimeLimitLocalOverride);
 
   // TODO(agawronska): Usage timestamp should be passed instead of second |now|.
   usage_time_limit::State state = usage_time_limit::GetState(
@@ -246,7 +246,7 @@
       std::nullopt);
   // Replace previous local override stored in pref, because PAC can only be
   // entered if previous override is not active anymore.
-  pref_service_->SetDict(prefs::kTimeLimitLocalOverride,
+  pref_service_->SetDict(ash::prefs::kTimeLimitLocalOverride,
                          local_override.ToDictionary());
   pref_service_->CommitPendingWrite();
 
@@ -350,14 +350,15 @@
   state_dict.Set(kScreenStateNextUnlockTime,
                  state.next_unlock_time.InSecondsFSinceUnixEpoch());
 
-  pref_service_->SetDict(prefs::kScreenTimeLastState, std::move(state_dict));
+  pref_service_->SetDict(ash::prefs::kScreenTimeLastState,
+                         std::move(state_dict));
   pref_service_->CommitPendingWrite();
 }
 
 std::optional<usage_time_limit::State>
 ScreenTimeController::GetLastStateFromPref() {
   const base::DictValue& last_state =
-      pref_service_->GetDict(prefs::kScreenTimeLastState);
+      pref_service_->GetDict(ash::prefs::kScreenTimeLastState);
   usage_time_limit::State result;
   if (last_state.empty())
     return std::nullopt;
@@ -439,9 +440,9 @@
   const icu::TimeZone& time_zone =
       system::TimezoneSettings::GetInstance()->GetTimezone();
   const base::DictValue& time_limit =
-      pref_service_->GetDict(prefs::kUsageTimeLimit);
+      pref_service_->GetDict(ash::prefs::kUsageTimeLimit);
   const base::DictValue& local_override =
-      pref_service_->GetDict(prefs::kTimeLimitLocalOverride);
+      pref_service_->GetDict(ash::prefs::kTimeLimitLocalOverride);
 
   std::optional<base::TimeDelta> remaining_usage =
       usage_time_limit::GetRemainingTimeUsage(time_limit, &local_override, now,
diff --git a/chrome/browser/ash/child_accounts/screen_time_controller.h b/chrome/browser/ash/child_accounts/screen_time_controller.h
index 400a4f28..6ebefff 100644
--- a/chrome/browser/ash/child_accounts/screen_time_controller.h
+++ b/chrome/browser/ash/child_accounts/screen_time_controller.h
@@ -9,6 +9,7 @@
 #include <optional>
 #include <string>
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/observer_list.h"
@@ -118,11 +119,11 @@
   // Schedule a call for UsageTimeLimitWarning.
   void ScheduleUsageTimeLimitWarning(const usage_time_limit::State& state);
 
-  // Save the |state| to |prefs::kScreenTimeLastState|.
+  // Save the |state| to |ash::prefs::kScreenTimeLastState|.
   void SaveCurrentStateToPref(const usage_time_limit::State& state);
 
-  // Get the last calculated |state| from |prefs::kScreenTimeLastState|, if it
-  // exists.
+  // Get the last calculated |state| from |ash::prefs::kScreenTimeLastState|, if
+  // it exists.
   std::optional<usage_time_limit::State> GetLastStateFromPref();
 
   // Called when the usage time limit is |kUsageTimeLimitWarningTime| or less to
diff --git a/chrome/browser/ash/child_accounts/screen_time_controller_browsertest.cc b/chrome/browser/ash/child_accounts/screen_time_controller_browsertest.cc
index e170f5e..e0c089c 100644
--- a/chrome/browser/ash/child_accounts/screen_time_controller_browsertest.cc
+++ b/chrome/browser/ash/child_accounts/screen_time_controller_browsertest.cc
@@ -24,7 +24,6 @@
 #include "chrome/browser/ash/policy/core/user_policy_test_helper.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "components/prefs/pref_service.h"
 #include "components/session_manager/core/session_manager.h"
@@ -122,7 +121,7 @@
 
   void MockChildScreenTime(base::TimeDelta used_time) {
     child_profile_->GetPrefs()->SetInteger(
-        ::prefs::kChildScreenTimeMilliseconds, used_time.InMilliseconds());
+        ash::prefs::kChildScreenTimeMilliseconds, used_time.InMilliseconds());
   }
 
   bool IsLocked() {
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_activity_registry.cc b/chrome/browser/ash/child_accounts/time_limits/app_activity_registry.cc
index d349a11..b414579 100644
--- a/chrome/browser/ash/child_accounts/time_limits/app_activity_registry.cc
+++ b/chrome/browser/ash/child_accounts/time_limits/app_activity_registry.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/logging.h"
 #include "base/time/default_tick_clock.h"
 #include "base/unguessable_token.h"
@@ -15,7 +16,6 @@
 #include "chrome/browser/ash/child_accounts/time_limits/app_time_notification_delegate.h"
 #include "chrome/browser/ash/child_accounts/time_limits/app_time_policy_helpers.h"
 #include "chrome/browser/ash/child_accounts/time_limits/persisted_app_info.h"
-#include "chrome/common/pref_names.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
@@ -133,10 +133,11 @@
 
 // static
 void AppActivityRegistry::RegisterProfilePrefs(PrefRegistrySimple* registry) {
-  registry->RegisterListPref(prefs::kPerAppTimeLimitsAppActivities);
-  registry->RegisterInt64Pref(prefs::kPerAppTimeLimitsLastSuccessfulReportTime,
-                              0);
-  registry->RegisterInt64Pref(prefs::kPerAppTimeLimitsLatestLimitUpdateTime, 0);
+  registry->RegisterListPref(ash::prefs::kPerAppTimeLimitsAppActivities);
+  registry->RegisterInt64Pref(
+      ash::prefs::kPerAppTimeLimitsLastSuccessfulReportTime, 0);
+  registry->RegisterInt64Pref(
+      ash::prefs::kPerAppTimeLimitsLatestLimitUpdateTime, 0);
 }
 
 AppActivityRegistry::AppActivityRegistry(
@@ -445,7 +446,7 @@
   }
 
   const base::ListValue& list =
-      pref_service_->GetList(prefs::kPerAppTimeLimitsAppActivities);
+      pref_service_->GetList(ash::prefs::kPerAppTimeLimitsAppActivities);
 
   const std::vector<PersistedAppInfo> applications_info =
       PersistedAppInfo::PersistedAppInfosFromList(
@@ -497,7 +498,7 @@
 
   // Update last successful report time.
   pref_service_->SetInt64(
-      prefs::kPerAppTimeLimitsLastSuccessfulReportTime,
+      ash::prefs::kPerAppTimeLimitsLastSuccessfulReportTime,
       timestamp.ToDeltaSinceWindowsEpoch().InMicroseconds());
 }
 
@@ -529,7 +530,7 @@
 
   // Update the latest app limit update.
   pref_service_->SetInt64(
-      prefs::kPerAppTimeLimitsLatestLimitUpdateTime,
+      ash::prefs::kPerAppTimeLimitsLatestLimitUpdateTime,
       latest_app_limit_update_.ToDeltaSinceWindowsEpoch().InMicroseconds());
 
   return policy_updated;
@@ -671,7 +672,7 @@
 void AppActivityRegistry::SaveAppActivity() {
   {
     ScopedListPrefUpdate update(pref_service_,
-                                prefs::kPerAppTimeLimitsAppActivities);
+                                ash::prefs::kPerAppTimeLimitsAppActivities);
     base::ListValue& list = update.Get();
 
     const base::Time now = base::Time::Now();
@@ -745,7 +746,7 @@
 
 void AppActivityRegistry::CleanRegistry(base::Time timestamp) {
   ScopedListPrefUpdate update(pref_service_,
-                              prefs::kPerAppTimeLimitsAppActivities);
+                              ash::prefs::kPerAppTimeLimitsAppActivities);
 
   base::ListValue& list = update.Get();
 
@@ -1122,8 +1123,8 @@
 void AppActivityRegistry::InitializeRegistryFromPref() {
   DCHECK(pref_service_);
 
-  int64_t last_limits_updates =
-      pref_service_->GetInt64(prefs::kPerAppTimeLimitsLatestLimitUpdateTime);
+  int64_t last_limits_updates = pref_service_->GetInt64(
+      ash::prefs::kPerAppTimeLimitsLatestLimitUpdateTime);
 
   latest_app_limit_update_ = base::Time::FromDeltaSinceWindowsEpoch(
       base::Microseconds(last_limits_updates));
@@ -1133,7 +1134,7 @@
 
 void AppActivityRegistry::InitializeAppActivities() {
   const base::ListValue& list =
-      pref_service_->GetList(prefs::kPerAppTimeLimitsAppActivities);
+      pref_service_->GetList(ash::prefs::kPerAppTimeLimitsAppActivities);
 
   const std::vector<PersistedAppInfo> applications_info =
       PersistedAppInfo::PersistedAppInfosFromList(
@@ -1183,8 +1184,8 @@
 }
 
 bool AppActivityRegistry::ShouldCleanUpStoredPref() {
-  int64_t last_time =
-      pref_service_->GetInt64(prefs::kPerAppTimeLimitsLastSuccessfulReportTime);
+  int64_t last_time = pref_service_->GetInt64(
+      ash::prefs::kPerAppTimeLimitsLastSuccessfulReportTime);
 
   if (last_time == 0) {
     return false;
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_activity_registry_unittest.cc b/chrome/browser/ash/child_accounts/time_limits/app_activity_registry_unittest.cc
index 4f2c134..ea44c6c 100644
--- a/chrome/browser/ash/child_accounts/time_limits/app_activity_registry_unittest.cc
+++ b/chrome/browser/ash/child_accounts/time_limits/app_activity_registry_unittest.cc
@@ -9,6 +9,7 @@
 #include <optional>
 #include <vector>
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "base/unguessable_token.h"
@@ -19,7 +20,6 @@
 #include "chrome/browser/ash/child_accounts/time_limits/app_time_notification_delegate.h"
 #include "chrome/browser/ash/child_accounts/time_limits/app_types.h"
 #include "chrome/browser/ash/child_accounts/time_limits/persisted_app_info.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/views/chrome_views_test_base.h"
 #include "components/prefs/pref_service.h"
@@ -636,7 +636,7 @@
 
   // Now let's test that the app activity are stored appropriately.
   const base::ListValue& list =
-      prefs()->GetList(prefs::kPerAppTimeLimitsAppActivities);
+      prefs()->GetList(ash::prefs::kPerAppTimeLimitsAppActivities);
 
   const std::vector<PersistedAppInfo> app_infos =
       PersistedAppInfo::PersistedAppInfosFromList(
@@ -678,7 +678,7 @@
 
   // Now let's test that the app activity are stored appropriately.
   const base::ListValue& list =
-      prefs()->GetList(prefs::kPerAppTimeLimitsAppActivities);
+      prefs()->GetList(ash::prefs::kPerAppTimeLimitsAppActivities);
 
   const std::vector<PersistedAppInfo> app_infos =
       PersistedAppInfo::PersistedAppInfosFromList(
@@ -696,7 +696,7 @@
   registry().OnSuccessfullyReported(base::Time::Now());
 
   const base::ListValue& new_list =
-      prefs()->GetList(prefs::kPerAppTimeLimitsAppActivities);
+      prefs()->GetList(ash::prefs::kPerAppTimeLimitsAppActivities);
 
   const std::vector<PersistedAppInfo> final_app_infos =
       PersistedAppInfo::PersistedAppInfosFromList(
@@ -716,7 +716,7 @@
   CreateAppActivityForApp(kApp1, base::Hours(1));
   CreateAppActivityForApp(kApp2, base::Hours(1));
 
-  prefs()->SetInt64(prefs::kPerAppTimeLimitsLastSuccessfulReportTime,
+  prefs()->SetInt64(ash::prefs::kPerAppTimeLimitsLastSuccessfulReportTime,
                     start_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
 
   task_environment()->AdvanceClock(base::Days(30));
@@ -727,7 +727,7 @@
 
   // Now let's test that the app activity are stored appropriately.
   const base::ListValue& list =
-      prefs()->GetList(prefs::kPerAppTimeLimitsAppActivities);
+      prefs()->GetList(ash::prefs::kPerAppTimeLimitsAppActivities);
 
   const std::vector<PersistedAppInfo> app_infos =
       PersistedAppInfo::PersistedAppInfosFromList(
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_time_controller.cc b/chrome/browser/ash/child_accounts/time_limits/app_time_controller.cc
index 079c557..47b3473c 100644
--- a/chrome/browser/ash/child_accounts/time_limits/app_time_controller.cc
+++ b/chrome/browser/ash/child_accounts/time_limits/app_time_controller.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <string>
 
+#include "ash/constants/ash_pref_names.h"
 #include "ash/constants/notifier_catalogs.h"
 #include "ash/public/cpp/notification_utils.h"
 #include "base/functional/bind.h"
@@ -33,7 +34,6 @@
 #include "chrome/browser/notifications/notification_handler.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/experiences/arc/app/arc_app_constants.h"
 #include "chromeos/ui/vector_icons/vector_icons.h"
@@ -208,9 +208,10 @@
 
 // static
 void AppTimeController::RegisterProfilePrefs(PrefRegistrySimple* registry) {
-  registry->RegisterInt64Pref(prefs::kPerAppTimeLimitsLastResetTime, 0);
-  registry->RegisterDictionaryPref(prefs::kPerAppTimeLimitsPolicy);
-  registry->RegisterDictionaryPref(prefs::kPerAppTimeLimitsAllowlistPolicy);
+  registry->RegisterInt64Pref(ash::prefs::kPerAppTimeLimitsLastResetTime, 0);
+  registry->RegisterDictionaryPref(ash::prefs::kPerAppTimeLimitsPolicy);
+  registry->RegisterDictionaryPref(
+      ash::prefs::kPerAppTimeLimitsAllowlistPolicy);
 }
 
 AppTimeController::AppTimeController(
@@ -247,8 +248,9 @@
 void AppTimeController::Init() {
   PrefService* pref_service = profile_->GetPrefs();
   RegisterProfilePrefObservers(pref_service);
-  TimeLimitsAllowlistPolicyUpdated(prefs::kPerAppTimeLimitsAllowlistPolicy);
-  TimeLimitsPolicyUpdated(prefs::kPerAppTimeLimitsPolicy);
+  TimeLimitsAllowlistPolicyUpdated(
+      ash::prefs::kPerAppTimeLimitsAllowlistPolicy);
+  TimeLimitsPolicyUpdated(ash::prefs::kPerAppTimeLimitsPolicy);
 
   // Restore the last reset time. If reset time has have been crossed, triggers
   // AppActivityRegistry to clear up the running active times of applications.
@@ -319,20 +321,20 @@
   // Using base::Unretained(this) is safe here because when |pref_registrar_|
   // gets destroyed, it will remove the observers from PrefService.
   pref_registrar_->Add(
-      prefs::kPerAppTimeLimitsPolicy,
+      ash::prefs::kPerAppTimeLimitsPolicy,
       base::BindRepeating(&AppTimeController::TimeLimitsPolicyUpdated,
                           base::Unretained(this)));
   pref_registrar_->Add(
-      prefs::kPerAppTimeLimitsAllowlistPolicy,
+      ash::prefs::kPerAppTimeLimitsAllowlistPolicy,
       base::BindRepeating(&AppTimeController::TimeLimitsAllowlistPolicyUpdated,
                           base::Unretained(this)));
 }
 
 void AppTimeController::TimeLimitsPolicyUpdated(const std::string& pref_name) {
-  DCHECK_EQ(pref_name, prefs::kPerAppTimeLimitsPolicy);
+  DCHECK_EQ(pref_name, ash::prefs::kPerAppTimeLimitsPolicy);
 
   const base::DictValue& policy =
-      pref_registrar_->prefs()->GetDict(prefs::kPerAppTimeLimitsPolicy);
+      pref_registrar_->prefs()->GetDict(ash::prefs::kPerAppTimeLimitsPolicy);
 
   std::map<AppId, AppLimit> app_limits = policy::AppLimitsFromDict(policy);
 
@@ -369,10 +371,10 @@
 
 void AppTimeController::TimeLimitsAllowlistPolicyUpdated(
     const std::string& pref_name) {
-  DCHECK_EQ(pref_name, prefs::kPerAppTimeLimitsAllowlistPolicy);
+  DCHECK_EQ(pref_name, ash::prefs::kPerAppTimeLimitsAllowlistPolicy);
 
   const base::DictValue& policy = pref_registrar_->prefs()->GetDict(
-      prefs::kPerAppTimeLimitsAllowlistPolicy);
+      ash::prefs::kPerAppTimeLimitsAllowlistPolicy);
 
   // Figure out a way to avoid cloning
   AppTimeLimitsAllowlistPolicyWrapper wrapper(&policy);
@@ -420,14 +422,14 @@
   }
 
   const base::DictValue& allowlist_policy = pref_registrar_->prefs()->GetDict(
-      prefs::kPerAppTimeLimitsAllowlistPolicy);
+      ash::prefs::kPerAppTimeLimitsAllowlistPolicy);
   AppTimeLimitsAllowlistPolicyWrapper wrapper(&allowlist_policy);
   if (std::ranges::contains(wrapper.GetAllowlistAppList(), app_id)) {
     app_registry_->SetAppAllowlisted(app_id);
   }
 
   const base::DictValue& policy =
-      pref_registrar_->prefs()->GetDict(prefs::kPerAppTimeLimitsPolicy);
+      pref_registrar_->prefs()->GetDict(ash::prefs::kPerAppTimeLimitsPolicy);
 
   // Update the application's time limit.
   const std::map<AppId, AppLimit> limits = policy::AppLimitsFromDict(policy);
@@ -488,7 +490,7 @@
 void AppTimeController::RestoreLastResetTime() {
   PrefService* pref_service = profile_->GetPrefs();
   int64_t reset_time =
-      pref_service->GetInt64(prefs::kPerAppTimeLimitsLastResetTime);
+      pref_service->GetInt64(ash::prefs::kPerAppTimeLimitsLastResetTime);
 
   if (reset_time == 0) {
     SetLastResetTime(base::Time::Now());
@@ -525,7 +527,7 @@
   PrefService* service = profile_->GetPrefs();
   DCHECK(service);
   service->SetInt64(
-      prefs::kPerAppTimeLimitsLastResetTime,
+      ash::prefs::kPerAppTimeLimitsLastResetTime,
       last_limits_reset_time_.ToDeltaSinceWindowsEpoch().InMicroseconds());
   service->CommitPendingWrite();
 }
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_time_controller_unittest.cc b/chrome/browser/ash/child_accounts/time_limits/app_time_controller_unittest.cc
index c899267..d25c8de 100644
--- a/chrome/browser/ash/child_accounts/time_limits/app_time_controller_unittest.cc
+++ b/chrome/browser/ash/child_accounts/time_limits/app_time_controller_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <optional>
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/strings/strcat.h"
@@ -25,7 +26,6 @@
 #include "chrome/browser/ash/child_accounts/time_limits/app_time_limits_policy_builder.h"
 #include "chrome/browser/ash/child_accounts/time_limits/app_types.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/components/dbus/system_clock/system_clock_client.h"
 #include "chromeos/ash/components/settings/timezone_settings.h"
@@ -453,7 +453,7 @@
     builder.AddAppLimit(kApp2, AppLimit(AppRestriction::kTimeLimit,
                                         kOneHour / 2, base::Time::Now()));
     builder.SetResetTime(6, 0);
-    profile().GetPrefs()->SetDict(prefs::kPerAppTimeLimitsPolicy,
+    profile().GetPrefs()->SetDict(ash::prefs::kPerAppTimeLimitsPolicy,
                                   builder.value().Clone());
   }
 
@@ -502,7 +502,7 @@
   // |last_reset_time|.
   last_reset_time = last_reset_time - kDay;
   profile().GetPrefs()->SetInt64(
-      prefs::kPerAppTimeLimitsLastResetTime,
+      ash::prefs::kPerAppTimeLimitsLastResetTime,
       last_reset_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
 
   InstantiateController();
@@ -535,7 +535,7 @@
     builder.AddAppLimit(absent_app, app_limit);
     builder.AddAppLimit(kApp2, blocked_app);
     builder.SetResetTime(6, 0);
-    profile().GetPrefs()->SetDict(prefs::kPerAppTimeLimitsPolicy,
+    profile().GetPrefs()->SetDict(ash::prefs::kPerAppTimeLimitsPolicy,
                                   builder.value().Clone());
   }
 
diff --git a/chrome/browser/ash/child_accounts/usage_time_limit_processor.h b/chrome/browser/ash/child_accounts/usage_time_limit_processor.h
index 2f2126a..1cc558cd 100644
--- a/chrome/browser/ash/child_accounts/usage_time_limit_processor.h
+++ b/chrome/browser/ash/child_accounts/usage_time_limit_processor.h
@@ -14,6 +14,7 @@
 #include <set>
 #include <unordered_map>
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chromeos/ash/components/settings/timezone_settings.h"
@@ -198,8 +199,8 @@
                                         const base::DictValue& new_policy);
 
 // Returns the active time limit polices in `time_limit_prefs`.
-// `time_limit_prefs` is the value of prefs::kUsageTimeLimit which stores the
-// usage time limit preference of a user.
+// `time_limit_prefs` is the value of ash::prefs::kUsageTimeLimit which stores
+// the usage time limit preference of a user.
 std::set<PolicyType> GetEnabledTimeLimitPolicies(
     const base::DictValue& time_limit_prefs);
 
diff --git a/chrome/browser/ash/dbus/vm/BUILD.gn b/chrome/browser/ash/dbus/vm/BUILD.gn
index 6a5d81a..0949e7ca 100644
--- a/chrome/browser/ash/dbus/vm/BUILD.gn
+++ b/chrome/browser/ash/dbus/vm/BUILD.gn
@@ -48,7 +48,6 @@
     "//chrome/browser/chromeos/policy/dlp",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui/views/select_file_dialog_extension",
-    "//chrome/browser/ui/webui/ash/settings/app_management",
     "//chrome/common",
     "//chrome/common:constants",
     "//chromeos/ash/components/browser_context_helper",
diff --git a/chrome/browser/ash/dbus/vm/plugin_vm_service_provider.cc b/chrome/browser/ash/dbus/vm/plugin_vm_service_provider.cc
index 4b81282d..b91ab8a9 100644
--- a/chrome/browser/ash/dbus/vm/plugin_vm_service_provider.cc
+++ b/chrome/browser/ash/dbus/vm/plugin_vm_service_provider.cc
@@ -16,7 +16,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h"
+#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
 #include "chromeos/ash/components/dbus/plugin_vm_service/plugin_vm_service.pb.h"
 #include "chromeos/ash/experiences/settings_ui/settings_app_manager.h"
 #include "components/prefs/pref_service.h"
@@ -116,20 +116,15 @@
     return;
   }
 
+  std::string sub_page;
+  std::optional<ash::SettingsAppManager::EntryPoint> entry_point;
+
   if (request.subpage_path() == kShowSettingsPageDetails) {
-    Profile* primary_profile = ProfileManager::GetPrimaryUserProfile();
-    chrome::ShowAppManagementPage(
-        primary_profile, plugin_vm::kPluginVmShelfAppId,
-        settings::AppManagementEntryPoint::kDBusServicePluginVm);
+    sub_page = ash::SettingsAppManager::CreateAppManagementPagePath(
+        plugin_vm::kPluginVmShelfAppId);
+    entry_point = ash::SettingsAppManager::EntryPoint::kDBusServicePluginVm;
   } else if (request.subpage_path() == kShowSettingsPageSharedPaths) {
-    if (auto* session =
-            session_manager::SessionManager::Get()->GetPrimarySession()) {
-      ash::SettingsAppManager::Get()->Open(
-          CHECK_DEREF(user_manager::UserManager::Get()->FindUser(
-              session->account_id())),
-          {.sub_page =
-               chromeos::settings::mojom::kPluginVmSharedPathsSubpagePath});
-    }
+    sub_page = chromeos::settings::mojom::kPluginVmSharedPathsSubpagePath;
   } else {
     constexpr char error_message[] = "Invalid subpage_path";
     LOG(ERROR) << error_message;
@@ -139,6 +134,16 @@
     return;
   }
 
+  if (auto* session =
+          session_manager::SessionManager::Get()->GetPrimarySession()) {
+    const user_manager::User* user =
+        user_manager::UserManager::Get()->FindUser(session->account_id());
+    ash::SettingsAppManager::Get()->Open(
+        CHECK_DEREF(user),
+        ash::SettingsAppManager::OpenParams{.sub_page = sub_page,
+                                            .entry_point = entry_point});
+  }
+
   std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
 }
 
diff --git a/chrome/browser/ash/events/event_rewriter_delegate_impl.cc b/chrome/browser/ash/events/event_rewriter_delegate_impl.cc
index 43e964b..8c451052 100644
--- a/chrome/browser/ash/events/event_rewriter_delegate_impl.cc
+++ b/chrome/browser/ash/events/event_rewriter_delegate_impl.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/extensions/extension_commands_global_registry.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/login/login_display_host.h"
-#include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
 #include "ui/aura/client/aura_constants.h"
diff --git a/chrome/browser/ash/events/event_rewriter_unittest.cc b/chrome/browser/ash/events/event_rewriter_unittest.cc
index 9c05af6..7f1280e 100644
--- a/chrome/browser/ash/events/event_rewriter_unittest.cc
+++ b/chrome/browser/ash/events/event_rewriter_unittest.cc
@@ -38,7 +38,6 @@
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/notifications/deprecation_notification_controller.h"
 #include "chrome/browser/ash/preferences/preferences.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/chrome_ash_test_base.h"
 #include "components/prefs/pref_member.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
diff --git a/chrome/browser/ash/extensions/gfx_utils.cc b/chrome/browser/ash/extensions/gfx_utils.cc
index 8ed86443..dc120330 100644
--- a/chrome/browser/ash/extensions/gfx_utils.cc
+++ b/chrome/browser/ash/extensions/gfx_utils.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/common/extensions/extension_constants.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/chromeos_app_icon_resources.h"
 #include "components/prefs/pref_service.h"
 #include "extensions/browser/extension_registry.h"
diff --git a/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc b/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc
index 8da11ec..11f45e1 100644
--- a/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc
+++ b/chrome/browser/ash/file_manager/app_service_file_tasks_unittest.cc
@@ -30,7 +30,6 @@
 #include "chrome/browser/ash/policy/dlp/dlp_files_controller_ash.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_factory.h"
 #include "chrome/browser/chromeos/policy/dlp/test/mock_dlp_rules_manager.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/components/file_manager/app_id.h"
 #include "components/prefs/scoped_user_pref_update.h"
diff --git a/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc b/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc
index 7a15eb9..051fdf81 100644
--- a/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc
+++ b/chrome/browser/ash/floating_workspace/floating_workspace_service_unittest.cc
@@ -43,7 +43,6 @@
 #include "chrome/browser/ui/ash/session/session_controller_client_impl.h"
 #include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.h"
 #include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_ui.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
diff --git a/chrome/browser/ash/hats/hats_dialog.cc b/chrome/browser/ash/hats/hats_dialog.cc
index 0cba83b..ef1cead 100644
--- a/chrome/browser/ash/hats/hats_dialog.cc
+++ b/chrome/browser/ash/hats/hats_dialog.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/profiles/profile_destroyer.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/dialogs/browser_dialogs.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/version/version_loader.h"
diff --git a/chrome/browser/ash/input_method/assistive_suggester.cc b/chrome/browser/ash/input_method/assistive_suggester.cc
index 2a1feb1..bcd6624 100644
--- a/chrome/browser/ash/input_method/assistive_suggester.cc
+++ b/chrome/browser/ash/input_method/assistive_suggester.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/ash/input_method/assistive_prefs.h"
 #include "chrome/browser/ash/input_method/assistive_suggester_switch.h"
 #include "chrome/browser/ash/input_method/suggestion_handler_interface.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/services/ime/public/cpp/assistive_suggestions.h"
 #include "components/exo/wm_helper.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/input_method/autocorrect_manager.cc b/chrome/browser/ash/input_method/autocorrect_manager.cc
index 351288c4..17b277b 100644
--- a/chrome/browser/ash/input_method/autocorrect_manager.cc
+++ b/chrome/browser/ash/input_method/autocorrect_manager.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/components/kiosk/kiosk_utils.h"
 #include "components/strings/grit/components_strings.h"
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_observer.cc b/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
index bffaaf3ae..350e4c7 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
+++ b/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
@@ -35,7 +35,6 @@
 #include "chrome/browser/ash/lobster/lobster_event_sink.h"
 #include "chrome/browser/ui/ash/input_method/input_method_menu_manager.h"
 #include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/experiences/settings_ui/settings_app_manager.h"
 #include "chromeos/ash/services/ime/public/cpp/autocorrect.h"
 #include "chromeos/ash/services/ime/public/mojom/input_method.mojom.h"
diff --git a/chrome/browser/ash/login/existing_user_controller.cc b/chrome/browser/ash/login/existing_user_controller.cc
index 1811ebc2..9978acf 100644
--- a/chrome/browser/ash/login/existing_user_controller.cc
+++ b/chrome/browser/ash/login/existing_user_controller.cc
@@ -84,7 +84,6 @@
 #include "chrome/browser/ui/webui/ash/login/tpm_error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/update_required_screen_handler.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
diff --git a/chrome/browser/ash/login/lock/views_screen_locker.cc b/chrome/browser/ash/login/lock/views_screen_locker.cc
index 8bc93520..d3a2330 100644
--- a/chrome/browser/ash/login/lock/views_screen_locker.cc
+++ b/chrome/browser/ash/login/lock/views_screen_locker.cc
@@ -34,7 +34,6 @@
 #include "chrome/browser/global_features.h"
 #include "chrome/browser/ui/ash/session/session_controller_client_impl.h"
 #include "chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/user.h"
diff --git a/chrome/browser/ash/login/lock_screen_utils.cc b/chrome/browser/ash/login/lock_screen_utils.cc
index 8bfe14b6..076f0f4 100644
--- a/chrome/browser/ash/login/lock_screen_utils.cc
+++ b/chrome/browser/ash/login/lock_screen_utils.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/input_method/ime_controller_client_impl.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/settings/cros_settings.h"
 #include "components/account_id/account_id.h"
 #include "components/prefs/scoped_user_pref_update.h"
diff --git a/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc b/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc
index b776740..3deaaf96 100644
--- a/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc
+++ b/chrome/browser/ash/login/login_ui_keyboard_browsertest.cc
@@ -32,7 +32,6 @@
 #include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/ash/login/user_adding_screen.h"
 #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/language_preferences/language_preferences.h"
 #include "chromeos/ash/components/login/auth/public/user_context.h"
 #include "chromeos/ash/components/policy/device_policy/device_policy_builder.h"
diff --git a/chrome/browser/ash/login/oobe_browsertest.cc b/chrome/browser/ash/login/oobe_browsertest.cc
index f4e8daaa..52d7643 100644
--- a/chrome/browser/ash/login/oobe_browsertest.cc
+++ b/chrome/browser/ash/login/oobe_browsertest.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/ui/webui/ash/login/welcome_screen_handler.h"
 #include "chrome/browser/ui/webui/signin/signin_utils.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chromeos/ash/components/dbus/cryptohome/key.pb.h"
diff --git a/chrome/browser/ash/login/oobe_localization_browsertest.cc b/chrome/browser/ash/login/oobe_localization_browsertest.cc
index 1db45afc..a7b17687 100644
--- a/chrome/browser/ash/login/oobe_localization_browsertest.cc
+++ b/chrome/browser/ash/login/oobe_localization_browsertest.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/browser/ui/webui/ash/login/welcome_screen_handler.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "chromeos/ash/components/system/statistics_provider.h"
diff --git a/chrome/browser/ash/login/screens/consumer_update_screen.cc b/chrome/browser/ash/login/screens/consumer_update_screen.cc
index 372f387..ff9d1a2 100644
--- a/chrome/browser/ash/login/screens/consumer_update_screen.cc
+++ b/chrome/browser/ash/login/screens/consumer_update_screen.cc
@@ -26,7 +26,6 @@
 #include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/ui/webui/ash/login/consumer_update_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/mojom/screens_oobe.mojom.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
diff --git a/chrome/browser/ash/login/screens/marketing_opt_in_screen.cc b/chrome/browser/ash/login/screens/marketing_opt_in_screen.cc
index 2f2e685f..44d96435 100644
--- a/chrome/browser/ash/login/screens/marketing_opt_in_screen.cc
+++ b/chrome/browser/ash/login/screens/marketing_opt_in_screen.cc
@@ -33,7 +33,6 @@
 #include "chrome/browser/ui/webui/ash/login/gesture_navigation_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/marketing_opt_in_screen_handler.h"
 #include "chrome/common/chrome_features.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc b/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc
index 08f7dcee..bc55767 100644
--- a/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc
@@ -36,7 +36,6 @@
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/marketing_opt_in_screen_handler.h"
 #include "chrome/common/chrome_features.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/fake_gaia_mixin.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/login/screens/update_screen.cc b/chrome/browser/ash/login/screens/update_screen.cc
index 513d6854..b7fe9bd4 100644
--- a/chrome/browser/ash/login/screens/update_screen.cc
+++ b/chrome/browser/ash/login/screens/update_screen.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/system/timezone_util.h"
 #include "chrome/browser/ui/webui/ash/login/update_screen_handler.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/network/network_state.h"
diff --git a/chrome/browser/ash/login/smart_lock/smart_lock_notification_controller_unittest.cc b/chrome/browser/ash/login/smart_lock/smart_lock_notification_controller_unittest.cc
index afaf1a4b..191a5fe 100644
--- a/chrome/browser/ash/login/smart_lock/smart_lock_notification_controller_unittest.cc
+++ b/chrome/browser/ash/login/smart_lock/smart_lock_notification_controller_unittest.cc
@@ -7,7 +7,6 @@
 #include "ash/constants/ash_features.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chromeos/ash/components/proximity_auth/proximity_auth_pref_names.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
diff --git a/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc b/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc
index 9ff7e660c..b98c4e4 100644
--- a/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc
+++ b/chrome/browser/ash/login/users/avatar/user_image_sync_observer.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/ash/login/users/default_user_image/default_user_images.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/prefs/pref_service_syncable_util.h"
-#include "chrome/common/pref_names.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/sync_preferences/pref_service_syncable.h"
diff --git a/chrome/browser/ash/login/users/multi_user_sign_in_policy_controller_unittest.cc b/chrome/browser/ash/login/users/multi_user_sign_in_policy_controller_unittest.cc
index 584c2097..bcda6af 100644
--- a/chrome/browser/ash/login/users/multi_user_sign_in_policy_controller_unittest.cc
+++ b/chrome/browser/ash/login/users/multi_user_sign_in_policy_controller_unittest.cc
@@ -25,7 +25,6 @@
 #include "chrome/browser/policy/networking/policy_cert_service.h"
 #include "chrome/browser/policy/networking/policy_cert_service_factory.h"
 #include "chrome/browser/prefs/browser_prefs.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
diff --git a/chrome/browser/ash/login/users/user_manager_unittest.cc b/chrome/browser/ash/login/users/user_manager_unittest.cc
index bed4bd0..5559c3a5 100644
--- a/chrome/browser/ash/login/users/user_manager_unittest.cc
+++ b/chrome/browser/ash/login/users/user_manager_unittest.cc
@@ -31,7 +31,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_test_util.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/fake_profile_manager.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
diff --git a/chrome/browser/ash/note_taking/note_taking_helper_unittest.cc b/chrome/browser/ash/note_taking/note_taking_helper_unittest.cc
index 263a8fd..e36c36e5 100644
--- a/chrome/browser/ash/note_taking/note_taking_helper_unittest.cc
+++ b/chrome/browser/ash/note_taking/note_taking_helper_unittest.cc
@@ -42,7 +42,6 @@
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
diff --git a/chrome/browser/ash/platform_keys/key_permissions/key_permissions_service_impl.cc b/chrome/browser/ash/platform_keys/key_permissions/key_permissions_service_impl.cc
index 9618b00..2883c32 100644
--- a/chrome/browser/ash/platform_keys/key_permissions/key_permissions_service_impl.cc
+++ b/chrome/browser/ash/platform_keys/key_permissions/key_permissions_service_impl.cc
@@ -20,7 +20,6 @@
 #include "base/values.h"
 #include "chrome/browser/ash/platform_keys/key_permissions/key_permissions_manager_impl.h"
 #include "chrome/browser/ash/platform_keys/platform_keys_service.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/platform_keys/platform_keys.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_namespace.h"
diff --git a/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash_unittest.cc b/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash_unittest.cc
index 853e0665..cf185e5 100644
--- a/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash_unittest.cc
+++ b/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash_unittest.cc
@@ -31,7 +31,6 @@
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/common/chrome_constants.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
diff --git a/chrome/browser/ash/policy/handlers/app_launch_automation_policy_handler.cc b/chrome/browser/ash/policy/handlers/app_launch_automation_policy_handler.cc
index 268c2cb..168506c3 100644
--- a/chrome/browser/ash/policy/handlers/app_launch_automation_policy_handler.cc
+++ b/chrome/browser/ash/policy/handlers/app_launch_automation_policy_handler.cc
@@ -10,7 +10,6 @@
 #include "ash/shell.h"
 #include "base/functional/callback_helpers.h"
 #include "base/values.h"
-#include "chrome/common/pref_names.h"
 #include "components/desks_storage/core/admin_template_service.h"
 #include "components/desks_storage/core/desk_model.h"
 #include "components/desks_storage/core/desk_template_conversion.h"
diff --git a/chrome/browser/ash/policy/handlers/configuration_policy_handler_ash_unittest.cc b/chrome/browser/ash/policy/handlers/configuration_policy_handler_ash_unittest.cc
index 98f8b4a..a47a5d2 100644
--- a/chrome/browser/ash/policy/handlers/configuration_policy_handler_ash_unittest.cc
+++ b/chrome/browser/ash/policy/handlers/configuration_policy_handler_ash_unittest.cc
@@ -12,7 +12,6 @@
 #include "base/json/json_reader.h"
 #include "base/values.h"
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_prefs.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/experiences/arc/arc_prefs.h"
 #include "chromeos/dbus/power/power_policy_controller.h"
 #include "components/policy/core/browser/configuration_policy_handler.h"
diff --git a/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_unittest.cc b/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_unittest.cc
index 7b688fd..9c62570 100644
--- a/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_unittest.cc
+++ b/chrome/browser/ash/policy/handlers/minimum_version_policy_handler_unittest.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
 #include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
 #include "chrome/browser/browser_process_platform_part.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/ash/components/dbus/shill/shill_service_client.h"
 #include "chromeos/ash/components/dbus/update_engine/fake_update_engine_client.h"
diff --git a/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc b/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc
index 81ff1a8..0b219a89 100644
--- a/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc
+++ b/chrome/browser/ash/policy/login/device_login_screen_policy_browsertest.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/reset_screen_handler.h"
-#include "chrome/common/pref_names.h"
 #include "components/policy/core/common/policy_types.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/prefs/pref_change_registrar.h"
diff --git a/chrome/browser/ash/policy/reporting/arc_app_install_encrypted_event_reporter_unittest.cc b/chrome/browser/ash/policy/reporting/arc_app_install_encrypted_event_reporter_unittest.cc
index 64aa8637..092ee039 100644
--- a/chrome/browser/ash/policy/reporting/arc_app_install_encrypted_event_reporter_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/arc_app_install_encrypted_event_reporter_unittest.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/ash/policy/reporting/install_event_log_util.h"
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/ash/components/network/network_handler_test_helper.h"
diff --git a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_collector.cc b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_collector.cc
index 87977a0e..5e4e78f5 100644
--- a/chrome/browser/ash/policy/reporting/arc_app_install_event_log_collector.cc
+++ b/chrome/browser/ash/policy/reporting/arc_app_install_event_log_collector.cc
@@ -6,7 +6,6 @@
 
 #include "base/command_line.h"
 #include "base/time/time.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/dbus/dbus_thread_manager.h"
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_state.h"
diff --git a/chrome/browser/ash/policy/status_collector/activity_storage.cc b/chrome/browser/ash/policy/status_collector/activity_storage.cc
index cfbe444..68dd01e 100644
--- a/chrome/browser/ash/policy/status_collector/activity_storage.cc
+++ b/chrome/browser/ash/policy/status_collector/activity_storage.cc
@@ -15,7 +15,6 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
-#include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 
diff --git a/chrome/browser/ash/policy/status_collector/child_activity_storage.cc b/chrome/browser/ash/policy/status_collector/child_activity_storage.cc
index f1ed39e..8278ae1 100644
--- a/chrome/browser/ash/policy/status_collector/child_activity_storage.cc
+++ b/chrome/browser/ash/policy/status_collector/child_activity_storage.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
 #include "chrome/common/pref_names.h"
@@ -71,12 +72,12 @@
   DCHECK(activity_day_start <= today_start);
 
   base::TimeDelta previous_activity = base::Milliseconds(
-      pref_service_->GetInteger(prefs::kChildScreenTimeMilliseconds));
+      pref_service_->GetInteger(ash::prefs::kChildScreenTimeMilliseconds));
 
   // If this activity window belongs to the current day, the screen time pref
   // should be updated.
   if (activity_day_start == today_start) {
-    pref_service_->SetInteger(prefs::kChildScreenTimeMilliseconds,
+    pref_service_->SetInteger(ash::prefs::kChildScreenTimeMilliseconds,
                               (previous_activity + activity).InMilliseconds());
     pref_service_->SetTime(prefs::kLastChildScreenTimeSaved, now);
     pref_service_->CommitPendingWrite();
diff --git a/chrome/browser/ash/policy/status_collector/child_status_collector.cc b/chrome/browser/ash/policy/status_collector/child_status_collector.cc
index a5bca7f..ead1466 100644
--- a/chrome/browser/ash/policy/status_collector/child_status_collector.cc
+++ b/chrome/browser/ash/policy/status_collector/child_status_collector.cc
@@ -15,6 +15,7 @@
 #include <sstream>
 #include <utility>
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/base64.h"
 #include "base/check.h"
 #include "base/feature_list.h"
@@ -177,7 +178,7 @@
 base::TimeDelta ChildStatusCollector::GetActiveChildScreenTime() {
   UpdateChildUsageTime();
   return base::Milliseconds(
-      pref_service_->GetInteger(prefs::kChildScreenTimeMilliseconds));
+      pref_service_->GetInteger(ash::prefs::kChildScreenTimeMilliseconds));
 }
 
 // static
@@ -237,7 +238,7 @@
   // Reset screen time if it has not been reset today.
   if (reset_time > pref_service_->GetTime(prefs::kLastChildScreenTimeReset)) {
     pref_service_->SetTime(prefs::kLastChildScreenTimeReset, now);
-    pref_service_->SetInteger(prefs::kChildScreenTimeMilliseconds, 0);
+    pref_service_->SetInteger(ash::prefs::kChildScreenTimeMilliseconds, 0);
     pref_service_->CommitPendingWrite();
   }
 
@@ -304,7 +305,7 @@
     base::UmaHistogramMemoryKB(kReportSizeHistogramName, size_in_bytes / 1024);
 
     int64_t last_successful_report_time_int = pref_service_->GetInt64(
-        prefs::kPerAppTimeLimitsLastSuccessfulReportTime);
+        ash::prefs::kPerAppTimeLimitsLastSuccessfulReportTime);
     if (last_successful_report_time_int > 0) {
       base::Time last_successful_report_time =
           base::Time::FromDeltaSinceWindowsEpoch(
diff --git a/chrome/browser/ash/policy/status_collector/child_status_collector_unittest.cc b/chrome/browser/ash/policy/status_collector/child_status_collector_unittest.cc
index 24ba5659..450d68f 100644
--- a/chrome/browser/ash/policy/status_collector/child_status_collector_unittest.cc
+++ b/chrome/browser/ash/policy/status_collector/child_status_collector_unittest.cc
@@ -12,6 +12,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/compiler_specific.h"
 #include "base/environment.h"
 #include "base/functional/bind.h"
@@ -367,8 +368,9 @@
         base::OnceClosure(),
         base::BindOnce(
             [](int64_t duration, PrefService* profile_pref_service_) {
-              EXPECT_EQ(duration, profile_pref_service_->GetInteger(
-                                      prefs::kChildScreenTimeMilliseconds));
+              EXPECT_EQ(duration,
+                        profile_pref_service_->GetInteger(
+                            ash::prefs::kChildScreenTimeMilliseconds));
             },
             duration, pref_service()));
   }
@@ -755,7 +757,7 @@
   {
     ash::app_time::AppTimeLimitsPolicyBuilder builder;
     builder.SetAppActivityReportingEnabled(/* enabled */ false);
-    testing_profile()->GetPrefs()->SetDict(prefs::kPerAppTimeLimitsPolicy,
+    testing_profile()->GetPrefs()->SetDict(ash::prefs::kPerAppTimeLimitsPolicy,
                                            builder.value().Clone());
   }
 
diff --git a/chrome/browser/ash/policy/status_collector/status_collector.cc b/chrome/browser/ash/policy/status_collector/status_collector.cc
index 81f24fd..665753cc 100644
--- a/chrome/browser/ash/policy/status_collector/status_collector.cc
+++ b/chrome/browser/ash/policy/status_collector/status_collector.cc
@@ -11,6 +11,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/constants/ash_pref_names.h"
 #include "base/check.h"
 #include "base/logging.h"
 #include "base/time/clock.h"
@@ -87,7 +88,7 @@
   registry->RegisterDictionaryPref(prefs::kUserActivityTimes);
   registry->RegisterTimePref(prefs::kLastChildScreenTimeReset, base::Time());
   registry->RegisterTimePref(prefs::kLastChildScreenTimeSaved, base::Time());
-  registry->RegisterIntegerPref(prefs::kChildScreenTimeMilliseconds, 0);
+  registry->RegisterIntegerPref(ash::prefs::kChildScreenTimeMilliseconds, 0);
 
   AppInfoGenerator::RegisterProfilePrefs(registry);
 }
diff --git a/chrome/browser/ash/preferences/preferences.cc b/chrome/browser/ash/preferences/preferences.cc
index 41804171..d9fa8f2 100644
--- a/chrome/browser/ash/preferences/preferences.cc
+++ b/chrome/browser/ash/preferences/preferences.cc
@@ -767,7 +767,7 @@
   pref_change_registrar_.Add(ash::prefs::kUserTimezone, callback);
   pref_change_registrar_.Add(ash::prefs::kResolveTimezoneByGeolocationMethod,
                              callback);
-  pref_change_registrar_.Add(::prefs::kParentAccessCodeConfig, callback);
+  pref_change_registrar_.Add(ash::prefs::kParentAccessCodeConfig, callback);
   for (auto* copy_pref : kCopyToKnownUserPrefs) {
     pref_change_registrar_.Add(copy_pref, callback);
   }
@@ -1330,12 +1330,12 @@
     }
   }
 
-  if (pref_name == ::prefs::kParentAccessCodeConfig ||
+  if (pref_name == ash::prefs::kParentAccessCodeConfig ||
       reason != REASON_PREF_CHANGED) {
-    if (prefs_->IsManagedPreference(::prefs::kParentAccessCodeConfig) &&
+    if (prefs_->IsManagedPreference(ash::prefs::kParentAccessCodeConfig) &&
         user_->IsChild()) {
       const base::DictValue& value =
-          prefs_->GetDict(::prefs::kParentAccessCodeConfig);
+          prefs_->GetDict(ash::prefs::kParentAccessCodeConfig);
       parent_access::ParentAccessService::Get().UpdateConfigForUser(
           user_->GetAccountId(), value.Clone());
     } else {
diff --git a/chrome/browser/ash/printing/cups_print_job_notification.cc b/chrome/browser/ash/printing/cups_print_job_notification.cc
index 819172e..94d9a69 100644
--- a/chrome/browser/ash/printing/cups_print_job_notification.cc
+++ b/chrome/browser/ash/printing/cups_print_job_notification.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/system_web_apps/system_web_app_utils.h"
 #include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/prefs/pref_service.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/ash/printing/enterprise/print_servers_provider_unittest.cc b/chrome/browser/ash/printing/enterprise/print_servers_provider_unittest.cc
index 4e412cb9..0b38f94 100644
--- a/chrome/browser/ash/printing/enterprise/print_servers_provider_unittest.cc
+++ b/chrome/browser/ash/printing/enterprise/print_servers_provider_unittest.cc
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "chrome/browser/ash/printing/print_server.h"
-#include "chrome/common/pref_names.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/ash/printing/print_servers_manager.cc b/chrome/browser/ash/printing/print_servers_manager.cc
index 91288f9a..e3929d5 100644
--- a/chrome/browser/ash/printing/print_servers_manager.cc
+++ b/chrome/browser/ash/printing/print_servers_manager.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/ash/printing/synced_printers_manager_factory.h"
 #include "chrome/browser/ash/printing/usb_printer_notification_controller.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/printing/cups_printer_status.h"
 #include "chromeos/printing/printing_constants.h"
 #include "chromeos/printing/uri.h"
diff --git a/chrome/browser/ash/printing/print_servers_manager_unittest.cc b/chrome/browser/ash/printing/print_servers_manager_unittest.cc
index be27abc..97c7a082 100644
--- a/chrome/browser/ash/printing/print_servers_manager_unittest.cc
+++ b/chrome/browser/ash/printing/print_servers_manager_unittest.cc
@@ -17,7 +17,6 @@
 #include "base/test/task_environment.h"
 #include "chrome/browser/ash/printing/enterprise/print_servers_provider.h"
 #include "chrome/browser/ash/printing/server_printers_provider.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "components/prefs/pref_service.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
diff --git a/chrome/browser/ash/printing/server_printers_provider.cc b/chrome/browser/ash/printing/server_printers_provider.cc
index dec0c842..2d973ac0 100644
--- a/chrome/browser/ash/printing/server_printers_provider.cc
+++ b/chrome/browser/ash/printing/server_printers_provider.cc
@@ -15,7 +15,6 @@
 #include "base/task/sequenced_task_runner.h"
 #include "chrome/browser/ash/printing/server_printers_fetcher.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "components/device_event_log/device_event_log.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/ash/printing/synced_printers_manager.cc b/chrome/browser/ash/printing/synced_printers_manager.cc
index 65eec4f..8a5a2bad 100644
--- a/chrome/browser/ash/printing/synced_printers_manager.cc
+++ b/chrome/browser/ash/printing/synced_printers_manager.cc
@@ -14,7 +14,6 @@
 #include "base/values.h"
 #include "chrome/browser/ash/printing/printers_sync_bridge.h"
 #include "chrome/browser/ash/printing/specifics_translation.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/printing/printer_configuration.h"
 #include "chromeos/printing/printer_translator.h"
diff --git a/chrome/browser/ash/printing/synced_printers_manager_unittest.cc b/chrome/browser/ash/printing/synced_printers_manager_unittest.cc
index 5a02eed..858019ac 100644
--- a/chrome/browser/ash/printing/synced_printers_manager_unittest.cc
+++ b/chrome/browser/ash/printing/synced_printers_manager_unittest.cc
@@ -18,7 +18,6 @@
 #include "base/time/time.h"
 #include "chrome/browser/ash/printing/printers_sync_bridge.h"
 #include "chrome/browser/ash/printing/synced_printers_manager_factory.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/sync/model/data_type_store.h"
 #include "components/sync/test/data_type_store_test_util.h"
diff --git a/chrome/browser/ash/system/timezone_resolver_manager.cc b/chrome/browser/ash/system/timezone_resolver_manager.cc
index 47c32f7d..020ffe6e 100644
--- a/chrome/browser/ash/system/timezone_resolver_manager.cc
+++ b/chrome/browser/ash/system/timezone_resolver_manager.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/geolocation/system_location_provider.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
diff --git a/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.cc b/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.cc
index f8ab6b2f..1f0ca2a1 100644
--- a/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.cc
+++ b/chrome/browser/ash/system_web_apps/apps/help_app/help_app_ui_delegate.cc
@@ -30,7 +30,6 @@
 #include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h"
 #include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
 #include "chromeos/ash/experiences/settings_ui/settings_app_manager.h"
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButton.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButton.java
index 0205e00d..03f7b2be 100644
--- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButton.java
+++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarButton.java
@@ -72,7 +72,9 @@
      */
     public void setClickCallback(@Nullable ClickWithMetaStateCallback callback) {
         setOnClickListener(
-                callback != null ? (v) -> callback.onClickWithMeta(mLastEventMetaState) : null);
+                callback != null
+                        ? (v) -> callback.onClickWithMeta(mLastEventMetaState, /* buttonState= */ 0)
+                        : null);
     }
 
     /**
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java
index 33cfdafb..307d153d 100644
--- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java
+++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java
@@ -289,7 +289,7 @@
     // Private methods.
 
     // TODO(crbug.com/394614779): Open in popup window instead of bookmark manager.
-    private void onAllBookmarksButtonClick(int metaState) {
+    private void onAllBookmarksButtonClick(int metaState, int buttonState) {
         // Open the manager iff the active profile and model are unchanged to prevent accidentally
         // opening the manager for the wrong profile/model. We will only record the click event if
         // this guard passes, so the data shows only actions that resulted in a change.
@@ -1047,7 +1047,7 @@
                 new PropertyModel.Builder(BookmarkBarButtonProperties.ALL_KEYS)
                         .with(
                                 BookmarkBarButtonProperties.CLICK_CALLBACK,
-                                (metaState) -> clickCallback.accept(item, metaState))
+                                (metaState, buttonState) -> clickCallback.accept(item, metaState))
                         .with(BookmarkBarButtonProperties.KEY_LISTENER, keyListener)
                         .with(
                                 BookmarkBarButtonProperties.ICON_TINT_LIST_ID,
diff --git a/chrome/browser/chromeos/extensions/wm/wm_desks_private_events_unittest.cc b/chrome/browser/chromeos/extensions/wm/wm_desks_private_events_unittest.cc
index 7b8220b..36921c69 100644
--- a/chrome/browser/chromeos/extensions/wm/wm_desks_private_events_unittest.cc
+++ b/chrome/browser/chromeos/extensions/wm/wm_desks_private_events_unittest.cc
@@ -8,7 +8,6 @@
 #include "base/uuid.h"
 #include "chrome/browser/extensions/extension_service_test_with_install.h"
 #include "chrome/common/extensions/api/wm_desks_private.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/test_browser_window.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/crx_file/id_util.h"
diff --git a/chrome/browser/chromeos/network/network_portal_signin_window.cc b/chrome/browser/chromeos/network/network_portal_signin_window.cc
index 16ea581..da1f3e8 100644
--- a/chrome/browser/chromeos/network/network_portal_signin_window.cc
+++ b/chrome/browser/chromeos/network/network_portal_signin_window.cc
@@ -11,7 +11,6 @@
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/dialogs/browser_dialogs.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
 #include "components/device_event_log/device_event_log.h"
diff --git a/chrome/browser/contextual_search/contextual_search_web_contents_helper.cc b/chrome/browser/contextual_search/contextual_search_web_contents_helper.cc
index e6ebd4e..0549c6c6 100644
--- a/chrome/browser/contextual_search/contextual_search_web_contents_helper.cc
+++ b/chrome/browser/contextual_search/contextual_search_web_contents_helper.cc
@@ -19,4 +19,9 @@
   return std::move(input_state_model_);
 }
 
+std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+ContextualSearchWebContentsHelper::TakeSessionHandle() {
+  return std::move(session_handle_);
+}
+
 WEB_CONTENTS_USER_DATA_KEY_IMPL(ContextualSearchWebContentsHelper);
diff --git a/chrome/browser/contextual_search/contextual_search_web_contents_helper.h b/chrome/browser/contextual_search/contextual_search_web_contents_helper.h
index 3c0bad4f..9c78958 100644
--- a/chrome/browser/contextual_search/contextual_search_web_contents_helper.h
+++ b/chrome/browser/contextual_search/contextual_search_web_contents_helper.h
@@ -47,9 +47,15 @@
     return session_handle_.get();
   }
 
-  // Returns the input state model. May return nullptr.
+  // Returns the input state model. May return nullptr. This transfers
+  // ownership to the caller.
   std::unique_ptr<contextual_search::InputStateModel> TakeInputStateModel();
 
+  // Returns the session handle. May return nullptr. This transfers ownership
+  // to the caller.
+  std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+  TakeSessionHandle();
+
   // Returns the task ID associated with the current contextual search session.
   // std::nullopt if the web_contents isn't showing a contextual task.
   const std::optional<base::Uuid>& task_id() const { return task_id_; }
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc
index f1bc1d333..ac18d82b 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc
+++ b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc
@@ -160,6 +160,7 @@
     mojo::PendingReceiver<searchbox::mojom::PageHandler>
         pending_searchbox_handler,
     GetSessionHandleCallback get_session_callback,
+    ClearSessionHandleCallback clear_session_callback,
     TakeInputStateModelCallback take_input_model_callback)
     : ComposeboxHandler(
           std::move(pending_handler),
@@ -171,7 +172,8 @@
               std::make_unique<ContextualTasksOmniboxClient>(profile,
                                                              web_contents,
                                                              this)),
-          std::move(get_session_callback)),
+          std::move(get_session_callback),
+          std::move(clear_session_callback)),
       take_input_model_callback_(std::move(take_input_model_callback)),
       web_ui_interface_(web_ui_interface),
       contextual_tasks_service_(
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h
index a9ee937..1c51522 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h
+++ b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h
@@ -62,6 +62,7 @@
       mojo::PendingReceiver<searchbox::mojom::PageHandler>
           pending_searchbox_handler,
       GetSessionHandleCallback get_session_callback,
+      ClearSessionHandleCallback clear_session_callback,
       TakeInputStateModelCallback take_input_model_callback);
   ~ContextualTasksComposeboxHandler() override;
 
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc
index b4bc4f62..e0a973b 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc
+++ b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc
@@ -290,6 +290,8 @@
         base::BindRepeating(
             &ContextualTasksUI::GetOrCreateContextualSessionHandle,
             base::Unretained(mock_ui_.get())),
+        base::BindRepeating(&ContextualTasksUI::ClearContextualSessionHandle,
+                            base::Unretained(mock_ui_.get())),
         base::BindRepeating(&ContextualTasksUI::TakeInputStateModel,
                             base::Unretained(mock_ui_.get())));
     handler_->SetMockContextualTasksService(mock_contextual_tasks_service_ptr_);
@@ -2742,6 +2744,7 @@
           []() -> contextual_search::ContextualSearchSessionHandle* {
             return nullptr;
           }),
+      base::DoNothing(),
       base::BindRepeating(&ContextualTasksUI::TakeInputStateModel,
                           base::Unretained(mock_ui_.get())));
   auto file_info = searchbox::mojom::SelectedFileInfo::New();
@@ -2812,6 +2815,8 @@
       base::BindRepeating(
           &ContextualTasksUI::GetOrCreateContextualSessionHandle,
           base::Unretained(mock_ui_.get())),
+      base::BindRepeating(&ContextualTasksUI::ClearContextualSessionHandle,
+                          base::Unretained(mock_ui_.get())),
       std::move(mock_callback));
 
   // 2. Act: Trigger the handler to fetch the model via the callback.
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc b/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc
index 33b49472..a4c5286 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc
+++ b/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc
@@ -53,6 +53,7 @@
       mojo::PendingReceiver<searchbox::mojom::PageHandler>
           pending_searchbox_handler,
       GetSessionHandleCallback get_session_callback,
+      ClearSessionHandleCallback clear_session_callback,
       TakeInputStateModelCallback get_inputstatemodel_callback)
       : ContextualTasksComposeboxHandler(
             ui_controller,
@@ -62,6 +63,7 @@
             std::move(pending_page),
             std::move(pending_searchbox_handler),
             std::move(get_session_callback),
+            std::move(clear_session_callback),
             std::move(get_inputstatemodel_callback)) {}
   ~MockContextualTasksComposeboxHandler() override = default;
 
@@ -682,6 +684,7 @@
           base::BindRepeating(
               &ContextualTasksUI::GetOrCreateContextualSessionHandle,
               base::Unretained(ui)),
+          base::DoNothing(),
           base::BindRepeating(&ContextualTasksUI::TakeInputStateModel,
                               base::Unretained(ui)));
   MockContextualTasksComposeboxHandler* mock_handler =
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_ui.cc b/chrome/browser/contextual_tasks/contextual_tasks_ui.cc
index a45e5e9..2ba71264 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_ui.cc
+++ b/chrome/browser/contextual_tasks/contextual_tasks_ui.cc
@@ -677,6 +677,8 @@
       base::BindRepeating(
           &ContextualTasksUI::GetOrCreateContextualSessionHandle,
           base::Unretained(this)),
+      base::BindRepeating(&ContextualTasksUI::ClearContextualSessionHandle,
+                          base::Unretained(this)),
       base::BindRepeating(&ContextualTasksUI::TakeInputStateModel,
                           base::Unretained(this)));
   composebox_handler_->SetPage(std::move(pending_searchbox_page));
@@ -733,6 +735,11 @@
   return helper->session_handle();
 }
 
+// Empty implementation, does not need to be cleared in contextual tasks. Only
+// needs to be cleared when transferring ownership to a new web contents / UI
+// controller which never happens for contextual tasks.
+void ContextualTasksUI::ClearContextualSessionHandle() {}
+
 std::unique_ptr<contextual_search::InputStateModel>
 ContextualTasksUI::TakeInputStateModel() {
   if (!task_id_.has_value()) {
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_ui.h b/chrome/browser/contextual_tasks/contextual_tasks_ui.h
index 62b42a6..bfaa38e 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_ui.h
+++ b/chrome/browser/contextual_tasks/contextual_tasks_ui.h
@@ -155,6 +155,9 @@
   void PostMessageToWebview(const lens::ClientToAimMessage& message) override;
   contextual_search::ContextualSearchSessionHandle*
   GetOrCreateContextualSessionHandle() override;
+
+  void ClearContextualSessionHandle();
+
   std::unique_ptr<contextual_search::InputStateModel> TakeInputStateModel()
       override;
   mojo::Remote<contextual_tasks::mojom::Page>& GetPageRemote() override;
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc b/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc
index a05b0a1..7012796 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc
+++ b/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc
@@ -213,31 +213,8 @@
     auto* helper = ContextualSearchWebContentsHelper::FromWebContents(
         source_tab->GetContents());
     if (helper && helper->session_handle()) {
-      source = helper->session_handle()->GetMetricsRecorder()->source();
-
-      auto* service =
-          ContextualSearchServiceFactory::GetForProfile(profile_.get());
-      if (service) {
-        // Create a new handle for existing session. The session handle should
-        // not be moved because there can be cases where a user opens the WebUI
-        // in a new tab (and would therefore leave the source tab without a
-        // handle).
-        session_handle =
-            service->GetSession(helper->session_handle()->session_id(),
-                                helper->session_handle()->invocation_source());
-        if (session_handle) {
-          session_handle->set_submitted_context_tokens(
-              helper->session_handle()->GetSubmittedContextTokens());
-          // TODO(crbug.com/469877869): Determine what to do with the return
-          // value of this call, or move this call to a different location.
-          session_handle->CheckSearchContentSharingSettings(
-              profile_->GetPrefs());
-          // Now that fulfillment has been passed to contextual tasks, the
-          // original session handle should clear its submitted context. Not
-          // clearing leaves it in a faulty state for future queries.
-          helper->session_handle()->ClearSubmittedContextTokens();
-        }
-      }
+      session_handle = helper->TakeSessionHandle();
+      source = session_handle->GetMetricsRecorder()->source();
     }
   }
   base::UmaHistogramEnumeration(
diff --git a/chrome/browser/data_sharing/BUILD.gn b/chrome/browser/data_sharing/BUILD.gn
index a41a4c9..8edccb83 100644
--- a/chrome/browser/data_sharing/BUILD.gn
+++ b/chrome/browser/data_sharing/BUILD.gn
@@ -460,6 +460,132 @@
 }
 
 source_set("data_sharing") {
-  sources = [ "data_sharing_service_factory.h" ]
-  public_deps = [ "//chrome/browser/profiles:profile" ]
+  sources = [
+    "data_sharing_navigation_throttle.h",
+    "data_sharing_navigation_utils.h",
+    "data_sharing_service_factory.h",
+    "migration/migratable_sync_service_coordinator_factory.h",
+    "personal_collaboration_data/personal_collaboration_data_service_factory.h",
+  ]
+  public_deps = [
+    "//chrome/browser/profiles:profile",
+    "//components/collaboration/public",
+    "//content/public/browser",
+  ]
+
+  if (is_android) {
+    sources += [
+      "android/data_sharing_ui_delegate_android.h",
+      "data_sharing_service_factory_bridge.h",
+    ]
+  } else {
+    sources += [
+      "desktop/data_sharing_conversion_utils.h",
+      "desktop/data_sharing_sdk_delegate_desktop.h",
+      "desktop/data_sharing_ui_delegate_desktop.h",
+    ]
+
+    public_deps += [
+      "//chrome/browser/ui/webui/data_sharing",
+      "//components/data_sharing/public/protocol:mojo_bindings",
+    ]
+  }
+}
+
+# The reason why `impl` target can't be merged into `data_sharing` target
+# above is that data_sharing_ui_delegate_desktop.cc includes headers from
+# //c/b/ui:ui that are not componentized yet, namely:
+#
+# - c/b/ui/webui/data_sharing/data_sharing_page_handler.h
+# - c/b/ui/views/data_sharing/collaboration_controller_delegate_desktop.h
+# - c/b/ui/browser_finder.h
+#
+# Hence, depending on //c/b/ui:ui isn't itself a problem. However, in this case,
+# if `data_sharing` depends on it, there will be non-trivial circular dependency errors.
+source_set("impl") {
+  sources = [
+    "data_sharing_navigation_throttle.cc",
+    "data_sharing_navigation_utils.cc",
+    "data_sharing_service_factory.cc",
+    "migration/migratable_sync_service_coordinator_factory.cc",
+    "personal_collaboration_data/personal_collaboration_data_service_factory.cc",
+  ]
+  deps = [
+    ":data_sharing",
+    "//chrome/browser/collaboration",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/signin",
+    "//chrome/browser/sync",
+    "//chrome/browser/sync:factories",
+    "//chrome/common:channel_info",
+    "//components/data_sharing/internal",
+    "//components/data_sharing/internal:personal_collaboration_data_internal",
+    "//components/data_sharing/migration/internal",
+    "//components/data_sharing/public:personal_collaboration_data",
+    "//content/public/browser",
+  ]
+
+  if (is_android) {
+    sources += [
+      "android/data_sharing_service_factory_android.cc",
+      "android/data_sharing_ui_delegate_android.cc",
+      "data_sharing_service_factory_bridge.cc",
+    ]
+    deps += [ ":jni_headers" ]
+  } else {
+    sources += [
+      "desktop/data_sharing_conversion_utils.cc",
+      "desktop/data_sharing_sdk_delegate_desktop.cc",
+      "desktop/data_sharing_ui_delegate_desktop.cc",
+    ]
+    deps += [
+      "//chrome/browser/ui",
+      "//chrome/browser/ui/browser_window",
+    ]
+  }
+
+  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "data_sharing_navigation_throttle_unittest.cc",
+    "data_sharing_service_factory_unittest.cc",
+    "personal_collaboration_data/personal_collaboration_data_service_factory_unittest.cc",
+  ]
+  deps = [
+    ":data_sharing",
+    "//base/test:test_support",
+    "//chrome/browser/collaboration",
+    "//chrome/test:test_support",
+    "//components/collaboration:test_support",
+    "//components/data_sharing/internal",
+    "//components/data_sharing/public:personal_collaboration_data",
+    "//content/test:test_support",
+  ]
+
+  if (!is_android) {
+    sources += [ "desktop/data_sharing_conversion_utils_unittest.cc" ]
+  }
+}
+
+if (!is_android) {
+  source_set("browser_tests") {
+    testonly = true
+    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+    sources = [
+      "desktop/data_sharing_sdk_delegate_desktop_browsertest.cc",
+      "desktop/data_sharing_service_browsertest.cc",
+    ]
+    deps = [
+      ":data_sharing",
+      "//base",
+      "//base/test:test_support",
+      "//chrome/browser/ui",
+      "//chrome/common",
+      "//chrome/test:test_support_ui",
+      "//components/data_sharing/public",
+    ]
+  }
 }
diff --git a/chrome/browser/data_sharing/desktop/data_sharing_ui_delegate_desktop.cc b/chrome/browser/data_sharing/desktop/data_sharing_ui_delegate_desktop.cc
index 5638dd2..bc51231 100644
--- a/chrome/browser/data_sharing/desktop/data_sharing_ui_delegate_desktop.cc
+++ b/chrome/browser/data_sharing/desktop/data_sharing_ui_delegate_desktop.cc
@@ -10,7 +10,6 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.h"
-#include "chrome/browser/ui/views/data_sharing/data_sharing_bubble_controller.h"
 #include "components/collaboration/public/collaboration_service.h"
 #include "components/data_sharing/public/data_sharing_service.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/desktop_to_mobile_promos/promos_utils.cc b/chrome/browser/desktop_to_mobile_promos/promos_utils.cc
index 8cff2ba..ead0cad 100644
--- a/chrome/browser/desktop_to_mobile_promos/promos_utils.cc
+++ b/chrome/browser/desktop_to_mobile_promos/promos_utils.cc
@@ -233,7 +233,7 @@
       promo_impression = DesktopIOSPromoImpression::kThirdImpression;
       break;
     default:
-      NOTREACHED();
+      return;
   }
   base::UmaHistogramEnumeration(
       "IOS.Desktop." + promo_histogram_type + ".Shown", promo_impression);
diff --git a/chrome/browser/educational_tip/BUILD.gn b/chrome/browser/educational_tip/BUILD.gn
index 2f55a82..0495f812 100644
--- a/chrome/browser/educational_tip/BUILD.gn
+++ b/chrome/browser/educational_tip/BUILD.gn
@@ -32,18 +32,18 @@
     "java/src/org/chromium/chrome/browser/educational_tip/cards/TabGroupPromoCoordinator.java",
     "java/src/org/chromium/chrome/browser/educational_tip/cards/TabGroupSyncPromoCoordinator.java",
     "java/src/org/chromium/chrome/browser/educational_tip/cards/TipsNotificationsPromoCoordinator.java",
-    "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetContent.java",
-    "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinator.java",
-    "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetItem.java",
-    "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerView.java",
-    "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetProperties.java",
-    "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetViewBinder.java",
     "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellBuilder.java",
     "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellCoordinator.java",
     "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellProperties.java",
     "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellView.java",
     "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellViewBinder.java",
-    "java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipSetupListBottomSheetListItemView.java",
+    "java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetContent.java",
+    "java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinator.java",
+    "java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetItem.java",
+    "java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerView.java",
+    "java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListItemView.java",
+    "java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetProperties.java",
+    "java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetViewBinder.java",
     "java/src/org/chromium/chrome/browser/setup_list/SetupListCompletable.java",
     "java/src/org/chromium/chrome/browser/setup_list/SetupListManager.java",
     "java/src/org/chromium/chrome/browser/setup_list/SetupListModuleUtils.java",
@@ -157,12 +157,12 @@
     "junit/src/org/chromium/chrome/browser/educational_tip/TabGroupPromoCoordinatorUnitTest.java",
     "junit/src/org/chromium/chrome/browser/educational_tip/TabGroupSyncPromoCoordinatorUnitTest.java",
     "junit/src/org/chromium/chrome/browser/educational_tip/cards/SavePasswordsPromoCoordinatorUnitTest.java",
-    "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinatorUnitTest.java",
-    "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerViewUnitTest.java",
     "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellBuilderUnitTest.java",
     "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellCoordinatorUnitTest.java",
     "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellViewBinderUnitTest.java",
     "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellViewUnitTest.java",
+    "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinatorUnitTest.java",
+    "junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerViewUnitTest.java",
     "junit/src/org/chromium/chrome/browser/setup_list/SetupListCompletableUnitTest.java",
     "junit/src/org/chromium/chrome/browser/setup_list/SetupListManagerUnitTest.java",
   ]
diff --git a/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_bottom_sheet_list_item_view.xml b/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_bottom_sheet_list_item_view.xml
index 020bf1c..430c156 100644
--- a/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_bottom_sheet_list_item_view.xml
+++ b/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_bottom_sheet_list_item_view.xml
@@ -5,7 +5,7 @@
 found in the LICENSE file.
 -->
 
-<org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipSetupListBottomSheetListItemView
+<org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetListItemView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
@@ -62,4 +62,4 @@
         app:layout_constraintStart_toEndOf="@id/list_item_description"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"/>
-</org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipSetupListBottomSheetListItemView>
+</org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetListItemView>
diff --git a/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_see_more_bottom_sheet_layout.xml b/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_see_more_bottom_sheet_layout.xml
index e1b96f6..25116bf 100644
--- a/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_see_more_bottom_sheet_layout.xml
+++ b/chrome/browser/educational_tip/java/res/layout/educational_tip_setup_list_see_more_bottom_sheet_layout.xml
@@ -59,7 +59,7 @@
                 android:gravity="center_horizontal"
                 android:textAppearance="@style/TextAppearance.TextMedium.Secondary"/>
 
-            <org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetListContainerView
+            <org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetListContainerView
                 android:id="@+id/setup_list_bottom_sheet_container_view"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellCoordinator.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellCoordinator.java
index e945903..80419b35 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellCoordinator.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipModuleTwoCellCoordinator.java
@@ -31,6 +31,8 @@
 import org.chromium.chrome.browser.educational_tip.EducationalTipCardProviderFactory;
 import org.chromium.chrome.browser.educational_tip.EducationalTipModuleUtils;
 import org.chromium.chrome.browser.educational_tip.R;
+import org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetCoordinator;
+import org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetItem;
 import org.chromium.chrome.browser.magic_stack.ModuleDelegate;
 import org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType;
 import org.chromium.chrome.browser.magic_stack.ModuleProvider;
@@ -118,9 +120,12 @@
         EducationalTipCardProvider item1Provider = mProviders.get(mItem1Type);
         EducationalTipCardProvider item2Provider = mProviders.get(mItem2Type);
 
-        EducationalTipBottomSheetCoordinator educationalTipBottomSheetCoordinator =
-                getEducationalTipBottomSheetCoordinator();
-        mModel.set(SEE_MORE_CLICK_HANDLER, educationalTipBottomSheetCoordinator::showBottomSheet);
+        EducationalTipSetupListBottomSheetCoordinator
+                educationalTipSetupListBottomSheetCoordinator =
+                        getEducationalTipSetupListBottomSheetCoordinator();
+        mModel.set(
+                SEE_MORE_CLICK_HANDLER,
+                educationalTipSetupListBottomSheetCoordinator::showBottomSheet);
 
         // Refresh Slot 1
         if (item1Provider != null) {
@@ -160,23 +165,26 @@
     }
 
     @NonNull
-    private EducationalTipBottomSheetCoordinator getEducationalTipBottomSheetCoordinator() {
-        Supplier<List<EducationalTipBottomSheetItem>> bottomSheetSupplier =
+    private EducationalTipSetupListBottomSheetCoordinator
+            getEducationalTipSetupListBottomSheetCoordinator() {
+        Supplier<List<EducationalTipSetupListBottomSheetItem>> bottomSheetSupplier =
                 () -> {
-                    List<EducationalTipBottomSheetItem> output = new ArrayList<>();
+                    List<EducationalTipSetupListBottomSheetItem> output = new ArrayList<>();
                     for (@ModuleType int type : mCurrentRankedModuleTypes) {
                         EducationalTipCardProvider provider = mProviders.get(type);
                         if (provider != null) {
                             SetupListCompletable.CompletionState completionState =
                                     SetupListCompletable.getCompletionState(provider, type);
                             output.add(
-                                    new EducationalTipBottomSheetItem(provider, completionState));
+                                    new EducationalTipSetupListBottomSheetItem(
+                                            provider, completionState));
                         }
                     }
                     return output;
                 };
 
-        return new EducationalTipBottomSheetCoordinator(mActionDelegate, bottomSheetSupplier);
+        return new EducationalTipSetupListBottomSheetCoordinator(
+                mActionDelegate, bottomSheetSupplier);
     }
 
     /**
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetContent.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetContent.java
similarity index 87%
rename from chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetContent.java
rename to chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetContent.java
index a540afd0..b347a8e 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetContent.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetContent.java
@@ -2,7 +2,7 @@
 // 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.educational_tip.two_cell;
+package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet;
 
 import android.content.Context;
 import android.view.View;
@@ -14,8 +14,7 @@
 
 /** Bottom sheet content of the educational tip two-cell layout. */
 @NullMarked
-public class EducationalTipBottomSheetContent implements BottomSheetContent {
-    // TODO(crbug.com/479597724): Implement BottomSheetContent and add relevant tests.
+public class EducationalTipSetupListBottomSheetContent implements BottomSheetContent {
     private final View mContentView;
     private final Context mContext;
 
@@ -23,7 +22,7 @@
      * @param context Context of the bottom sheet.
      * @param contentView Main view for the bottom sheet.
      */
-    public EducationalTipBottomSheetContent(Context context, View contentView) {
+    public EducationalTipSetupListBottomSheetContent(Context context, View contentView) {
         mContext = context;
         mContentView = contentView;
     }
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinator.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinator.java
similarity index 72%
rename from chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinator.java
rename to chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinator.java
index 5570aec..8002e34 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinator.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinator.java
@@ -2,12 +2,12 @@
 // 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.educational_tip.two_cell;
+package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet;
 
-import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_DESCRIPTION;
-import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS;
-import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS_ON_CLICK;
-import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_TITLE;
+import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_DESCRIPTION;
+import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS;
+import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS_ON_CLICK;
+import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_TITLE;
 
 import android.content.Context;
 import android.view.LayoutInflater;
@@ -29,21 +29,22 @@
  * fetching and preparing the content for the bottom sheet.
  */
 @NullMarked
-public class EducationalTipBottomSheetCoordinator {
+public class EducationalTipSetupListBottomSheetCoordinator {
     private final Context mContext;
     private final BottomSheetContent mBottomSheetContent;
     private final BottomSheetController mBottomSheetController;
     private final PropertyModel mModel;
 
-    private final Supplier<List<EducationalTipBottomSheetItem>>
+    private final Supplier<List<EducationalTipSetupListBottomSheetItem>>
             mRankedEducationalTipProviderSupplier;
 
     /**
      * @param actionDelegate The instance of {@link EducationTipModuleActionDelegate}.
      */
-    public EducationalTipBottomSheetCoordinator(
+    public EducationalTipSetupListBottomSheetCoordinator(
             EducationTipModuleActionDelegate actionDelegate,
-            Supplier<List<EducationalTipBottomSheetItem>> rankedEducationalTipProviderSupplier) {
+            Supplier<List<EducationalTipSetupListBottomSheetItem>>
+                    rankedEducationalTipProviderSupplier) {
         mRankedEducationalTipProviderSupplier = rankedEducationalTipProviderSupplier;
         mContext = actionDelegate.getContext();
         mBottomSheetController = actionDelegate.getBottomSheetController();
@@ -52,11 +53,13 @@
                         .inflate(
                                 R.layout.educational_tip_setup_list_see_more_bottom_sheet_layout,
                                 /* root= */ null);
-        mBottomSheetContent = new EducationalTipBottomSheetContent(mContext, contentView);
+        mBottomSheetContent = new EducationalTipSetupListBottomSheetContent(mContext, contentView);
 
-        mModel = new PropertyModel.Builder(EducationalTipBottomSheetProperties.ALL_KEYS).build();
+        mModel =
+                new PropertyModel.Builder(EducationalTipSetupListBottomSheetProperties.ALL_KEYS)
+                        .build();
         PropertyModelChangeProcessor.create(
-                mModel, contentView, EducationalTipBottomSheetViewBinder::bind);
+                mModel, contentView, EducationalTipSetupListBottomSheetViewBinder::bind);
 
         mModel.set(
                 BOTTOM_SHEET_LIST_ITEMS_ON_CLICK, () -> dismissBottomSheet(/* animate= */ false));
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetItem.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetItem.java
similarity index 81%
rename from chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetItem.java
rename to chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetItem.java
index 8a06633..0aa22626 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetItem.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetItem.java
@@ -2,7 +2,7 @@
 // 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.educational_tip.two_cell;
+package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet;
 
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
@@ -11,11 +11,11 @@
 
 /** Data holder for items displayed in the educational tip bottom sheet. */
 @NullMarked
-public class EducationalTipBottomSheetItem {
+public class EducationalTipSetupListBottomSheetItem {
     public final EducationalTipCardProvider provider;
     public final SetupListCompletable.@Nullable CompletionState completionState;
 
-    public EducationalTipBottomSheetItem(
+    public EducationalTipSetupListBottomSheetItem(
             EducationalTipCardProvider provider,
             SetupListCompletable.@Nullable CompletionState completionState) {
         this.provider = provider;
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerView.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerView.java
similarity index 90%
rename from chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerView.java
rename to chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerView.java
index 7cb41a8..2c7d4df 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerView.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerView.java
@@ -2,7 +2,7 @@
 // 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.educational_tip.two_cell;
+package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet;
 
 import android.content.Context;
 import android.util.AttributeSet;
@@ -23,12 +23,10 @@
  * bottom sheet.
  */
 @NullMarked
-public class EducationalTipBottomSheetListContainerView extends LinearLayout {
-    // TODO(crbug.com/479597724): Implement container view.
-
+public class EducationalTipSetupListBottomSheetListContainerView extends LinearLayout {
     @Nullable private Runnable mDismissBottomSheetRunnable;
 
-    public EducationalTipBottomSheetListContainerView(
+    public EducationalTipSetupListBottomSheetListContainerView(
             Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
     }
@@ -40,12 +38,13 @@
      * @param rankedEducationalTips list of {@link EducationalTipCardProvider} that will be
      *     displayed in the container.
      */
-    public void renderSetUpList(List<EducationalTipBottomSheetItem> rankedEducationalTips) {
+    public void renderSetUpList(
+            List<EducationalTipSetupListBottomSheetItem> rankedEducationalTips) {
         // Clears all previous list items.
         destroy();
 
         for (int i = 0; i < rankedEducationalTips.size(); i++) {
-            EducationalTipBottomSheetItem item = rankedEducationalTips.get(i);
+            EducationalTipSetupListBottomSheetItem item = rankedEducationalTips.get(i);
             EducationalTipCardProvider educationalTip = item.provider;
             EducationalTipSetupListBottomSheetListItemView listItemView =
                     (EducationalTipSetupListBottomSheetListItemView) createListItemView();
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipSetupListBottomSheetListItemView.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListItemView.java
similarity index 86%
rename from chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipSetupListBottomSheetListItemView.java
rename to chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListItemView.java
index 685d5f80..3e9a8e8 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipSetupListBottomSheetListItemView.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListItemView.java
@@ -2,7 +2,7 @@
 // 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.educational_tip.two_cell;
+package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet;
 
 import android.content.Context;
 import android.graphics.Paint;
@@ -18,8 +18,8 @@
 import org.chromium.chrome.browser.setup_list.SetupListModuleUtils;
 
 /**
- * The list item view within a {@link EducationalTipBottomSheetListContainerView} that is in a
- * bottom sheet.
+ * The list item view within a {@link EducationalTipSetupListBottomSheetListContainerView} that is
+ * in a bottom sheet.
  */
 @NullMarked
 public class EducationalTipSetupListBottomSheetListItemView extends ConstraintLayout {
@@ -27,7 +27,8 @@
     private TextView mTitle;
     private TextView mDescription;
 
-    public EducationalTipSetupListBottomSheetListItemView(Context context, @Nullable AttributeSet attrs) {
+    public EducationalTipSetupListBottomSheetListItemView(
+            Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
     }
 
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetProperties.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetProperties.java
similarity index 86%
rename from chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetProperties.java
rename to chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetProperties.java
index c93ba7e3..a936408b 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetProperties.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetProperties.java
@@ -2,7 +2,7 @@
 // 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.educational_tip.two_cell;
+package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet;
 
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.ui.modelutil.PropertyKey;
@@ -12,12 +12,12 @@
 
 /** The properties associated with rendering the educational tip bottom sheet. */
 @NullMarked
-public class EducationalTipBottomSheetProperties {
+public class EducationalTipSetupListBottomSheetProperties {
     public static final WritableObjectPropertyKey<String> BOTTOM_SHEET_TITLE =
             new WritableObjectPropertyKey<>();
     public static final WritableObjectPropertyKey<String> BOTTOM_SHEET_DESCRIPTION =
             new WritableObjectPropertyKey<>();
-    public static final WritableObjectPropertyKey<List<EducationalTipBottomSheetItem>>
+    public static final WritableObjectPropertyKey<List<EducationalTipSetupListBottomSheetItem>>
             BOTTOM_SHEET_LIST_ITEMS = new WritableObjectPropertyKey<>();
     public static final WritableObjectPropertyKey<Runnable> BOTTOM_SHEET_LIST_ITEMS_ON_CLICK =
             new WritableObjectPropertyKey<>();
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetViewBinder.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetViewBinder.java
similarity index 71%
rename from chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetViewBinder.java
rename to chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetViewBinder.java
index e69285c..175be99 100644
--- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetViewBinder.java
+++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetViewBinder.java
@@ -2,12 +2,12 @@
 // 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.educational_tip.two_cell;
+package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet;
 
-import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_DESCRIPTION;
-import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS;
-import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS_ON_CLICK;
-import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_TITLE;
+import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_DESCRIPTION;
+import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS;
+import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS_ON_CLICK;
+import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_TITLE;
 
 import android.view.View;
 import android.widget.TextView;
@@ -19,7 +19,7 @@
 
 /** ViewBinder for a generic two-cell educational tip bottom sheet. */
 @NullMarked
-public class EducationalTipBottomSheetViewBinder {
+public class EducationalTipSetupListBottomSheetViewBinder {
     public static void bind(PropertyModel model, View view, PropertyKey propertyKey) {
         if (propertyKey == BOTTOM_SHEET_TITLE) {
             TextView title = view.findViewById(R.id.setup_list_bottom_sheet_title);
@@ -28,11 +28,11 @@
             TextView description = view.findViewById(R.id.setup_list_bottom_sheet_description);
             description.setText(model.get(BOTTOM_SHEET_DESCRIPTION));
         } else if (propertyKey == BOTTOM_SHEET_LIST_ITEMS) {
-            EducationalTipBottomSheetListContainerView listContainerView =
+            EducationalTipSetupListBottomSheetListContainerView listContainerView =
                     view.findViewById(R.id.setup_list_bottom_sheet_container_view);
             listContainerView.renderSetUpList(model.get(BOTTOM_SHEET_LIST_ITEMS));
         } else if (propertyKey == BOTTOM_SHEET_LIST_ITEMS_ON_CLICK) {
-            EducationalTipBottomSheetListContainerView listContainerView =
+            EducationalTipSetupListBottomSheetListContainerView listContainerView =
                     view.findViewById(R.id.setup_list_bottom_sheet_container_view);
             listContainerView.setDismissBottomSheet(model.get(BOTTOM_SHEET_LIST_ITEMS_ON_CLICK));
         }
diff --git a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinatorUnitTest.java b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinatorUnitTest.java
similarity index 65%
rename from chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinatorUnitTest.java
rename to chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinatorUnitTest.java
index 459c4e2..b89b726 100644
--- a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetCoordinatorUnitTest.java
+++ b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetCoordinatorUnitTest.java
@@ -2,17 +2,17 @@
 // 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.educational_tip.two_cell;
+package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet;
 
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_DESCRIPTION;
-import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS;
-import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS_ON_CLICK;
-import static org.chromium.chrome.browser.educational_tip.two_cell.EducationalTipBottomSheetProperties.BOTTOM_SHEET_TITLE;
+import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_DESCRIPTION;
+import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS;
+import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_LIST_ITEMS_ON_CLICK;
+import static org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet.EducationalTipSetupListBottomSheetProperties.BOTTOM_SHEET_TITLE;
 
 import android.content.Context;
 
@@ -44,29 +44,33 @@
 import java.util.List;
 import java.util.function.Supplier;
 
-/** Unit tests for {@link EducationalTipBottomSheetCoordinator} */
+/** Unit tests for {@link EducationalTipSetupListBottomSheetCoordinator} */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(
         manifest = Config.NONE,
         shadows = {ShadowAppCompatResources.class})
-public class EducationalTipBottomSheetCoordinatorUnitTest {
+public class EducationalTipSetupListBottomSheetCoordinatorUnitTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     @Mock private BottomSheetController mBottomSheetController;
     @Mock private EducationTipModuleActionDelegate mActionDelegate;
-    @Mock private Supplier<List<EducationalTipBottomSheetItem>> mEducationalTipCardProviderSupplier;
+
+    @Mock
+    private Supplier<List<EducationalTipSetupListBottomSheetItem>>
+            mEducationalTipCardProviderSupplier;
 
     private Context mContext;
-    private List<EducationalTipBottomSheetItem> mListOfEducationalTipBottomSheetItem;
+    private List<EducationalTipSetupListBottomSheetItem>
+            mListOfEducationalTipSetupListBottomSheetItem;
 
     @Before
     public void setUp() {
         mContext = ApplicationProvider.getApplicationContext();
         when(mActionDelegate.getContext()).thenReturn(ApplicationProvider.getApplicationContext());
         when(mActionDelegate.getBottomSheetController()).thenReturn(mBottomSheetController);
-        mListOfEducationalTipBottomSheetItem = createListOfEducationalTipBottomSheetItem();
+        mListOfEducationalTipSetupListBottomSheetItem = createListOfEducationalTipBottomSheetItem();
         when(mEducationalTipCardProviderSupplier.get())
-                .thenReturn(mListOfEducationalTipBottomSheetItem);
+                .thenReturn(mListOfEducationalTipSetupListBottomSheetItem);
 
         List<Integer> moduleTypeList = new ArrayList<>();
         for (int i = 0; i < 5; i++) {
@@ -78,11 +82,12 @@
     @Test
     @SmallTest
     public void testShowBottomSheet() {
-        EducationalTipBottomSheetCoordinator educationalTipBottomSheetCoordinator =
-                new EducationalTipBottomSheetCoordinator(
-                        mActionDelegate, mEducationalTipCardProviderSupplier);
-        PropertyModel model = educationalTipBottomSheetCoordinator.getModelForTesting();
-        educationalTipBottomSheetCoordinator.showBottomSheet();
+        EducationalTipSetupListBottomSheetCoordinator
+                educationalTipSetupListBottomSheetCoordinator =
+                        new EducationalTipSetupListBottomSheetCoordinator(
+                                mActionDelegate, mEducationalTipCardProviderSupplier);
+        PropertyModel model = educationalTipSetupListBottomSheetCoordinator.getModelForTesting();
+        educationalTipSetupListBottomSheetCoordinator.showBottomSheet();
 
         Assert.assertEquals(
                 "Bottom sheet title should be default",
@@ -95,20 +100,21 @@
         Assert.assertEquals(
                 "Bottom sheet list items should be set",
                 model.get(BOTTOM_SHEET_LIST_ITEMS),
-                mListOfEducationalTipBottomSheetItem);
+                mListOfEducationalTipSetupListBottomSheetItem);
         verify(mBottomSheetController).requestShowContent(any(), /* animate= */ eq(true));
     }
 
     @Test
     @SmallTest
     public void testDismissBottomSheet() {
-        EducationalTipBottomSheetCoordinator educationalTipBottomSheetCoordinator =
-                new EducationalTipBottomSheetCoordinator(
-                        mActionDelegate, mEducationalTipCardProviderSupplier);
-        PropertyModel model = educationalTipBottomSheetCoordinator.getModelForTesting();
+        EducationalTipSetupListBottomSheetCoordinator
+                educationalTipSetupListBottomSheetCoordinator =
+                        new EducationalTipSetupListBottomSheetCoordinator(
+                                mActionDelegate, mEducationalTipCardProviderSupplier);
+        PropertyModel model = educationalTipSetupListBottomSheetCoordinator.getModelForTesting();
 
         // 1. Verify dismissal with animation.
-        educationalTipBottomSheetCoordinator.dismissBottomSheet(true);
+        educationalTipSetupListBottomSheetCoordinator.dismissBottomSheet(true);
         verify(mBottomSheetController).hideContent(any(), eq(true));
 
         // 2. Verify dismissal without animation (triggered by item click).
@@ -118,8 +124,9 @@
         verify(mBottomSheetController).hideContent(any(), eq(false));
     }
 
-    private List<EducationalTipBottomSheetItem> createListOfEducationalTipBottomSheetItem() {
-        List<EducationalTipBottomSheetItem> output = new ArrayList<>();
+    private List<EducationalTipSetupListBottomSheetItem>
+            createListOfEducationalTipBottomSheetItem() {
+        List<EducationalTipSetupListBottomSheetItem> output = new ArrayList<>();
         for (int i = 0; i < 5; i++) {
             EducationalTipCardProvider provider =
                     EducationalTipCardProviderFactory.createInstance(
@@ -128,7 +135,7 @@
                             null,
                             mActionDelegate,
                             () -> {});
-            output.add(new EducationalTipBottomSheetItem(provider, null));
+            output.add(new EducationalTipSetupListBottomSheetItem(provider, null));
         }
         return output;
     }
diff --git a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerViewUnitTest.java b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerViewUnitTest.java
similarity index 69%
rename from chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerViewUnitTest.java
rename to chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerViewUnitTest.java
index ebfa27c..6479d97 100644
--- a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/EducationalTipBottomSheetListContainerViewUnitTest.java
+++ b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/two_cell/see_more_bottomsheet/EducationalTipSetupListBottomSheetListContainerViewUnitTest.java
@@ -2,7 +2,7 @@
 // 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.educational_tip.two_cell;
+package org.chromium.chrome.browser.educational_tip.two_cell.see_more_bottomsheet;
 
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
@@ -31,26 +31,27 @@
 import java.util.ArrayList;
 import java.util.List;
 
-/** Unit tests for {@link EducationalTipBottomSheetListContainerView} */
+/** Unit tests for {@link EducationalTipSetupListBottomSheetListContainerView} */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(
         manifest = Config.NONE,
         shadows = {ShadowAppCompatResources.class})
-public class EducationalTipBottomSheetListContainerViewUnitTest {
+public class EducationalTipSetupListBottomSheetListContainerViewUnitTest {
     private static final int EDUCATIONAL_TIP_MODULES_SIZE = 5;
 
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     @Mock private EducationalTipCardProvider mEducationalTipCardProvider;
 
-    private EducationalTipBottomSheetListContainerView mContainerView;
-    private List<EducationalTipBottomSheetItem> mListOfEducationalTipBottomSheetItem;
+    private EducationalTipSetupListBottomSheetListContainerView mContainerView;
+    private List<EducationalTipSetupListBottomSheetItem>
+            mListOfEducationalTipSetupListBottomSheetItem;
 
     @Before
     public void setUp() {
         Context context = ApplicationProvider.getApplicationContext();
-        mContainerView = new EducationalTipBottomSheetListContainerView(context, null);
-        mListOfEducationalTipBottomSheetItem = createListOfEducationalTipBottomSheetItem();
+        mContainerView = new EducationalTipSetupListBottomSheetListContainerView(context, null);
+        mListOfEducationalTipSetupListBottomSheetItem = createListOfEducationalTipBottomSheetItem();
 
         List<Integer> moduleTypeList = new ArrayList<>();
         for (int i = 0; i < 5; i++) {
@@ -61,13 +62,13 @@
 
     @Test
     public void testRenderSetUpList_numberOfListItemsCreated() {
-        mContainerView.renderSetUpList(mListOfEducationalTipBottomSheetItem);
+        mContainerView.renderSetUpList(mListOfEducationalTipSetupListBottomSheetItem);
         Assert.assertEquals(
                 "All educational tip modules should be added",
                 EDUCATIONAL_TIP_MODULES_SIZE,
                 mContainerView.getChildCount());
 
-        mContainerView.renderSetUpList(mListOfEducationalTipBottomSheetItem);
+        mContainerView.renderSetUpList(mListOfEducationalTipSetupListBottomSheetItem);
         Assert.assertEquals(
                 "Previous educational tip list items should be destroyed",
                 EDUCATIONAL_TIP_MODULES_SIZE,
@@ -77,7 +78,9 @@
     @Test
     public void testEducationalTipCardProviderInRenderSetUpList() {
         mContainerView.renderSetUpList(
-                List.of(new EducationalTipBottomSheetItem(mEducationalTipCardProvider, null)));
+                List.of(
+                        new EducationalTipSetupListBottomSheetItem(
+                                mEducationalTipCardProvider, null)));
         verify(mEducationalTipCardProvider, times(1)).getCardImage();
         verify(mEducationalTipCardProvider, times(1)).getCardTitle();
         verify(mEducationalTipCardProvider, times(1)).getCardDescription();
@@ -88,7 +91,9 @@
         Runnable mockDismissRunnable = mock(Runnable.class);
         mContainerView.setDismissBottomSheet(mockDismissRunnable);
         mContainerView.renderSetUpList(
-                List.of(new EducationalTipBottomSheetItem(mEducationalTipCardProvider, null)));
+                List.of(
+                        new EducationalTipSetupListBottomSheetItem(
+                                mEducationalTipCardProvider, null)));
 
         mContainerView.getChildAt(0).performClick();
 
@@ -96,11 +101,12 @@
         verify(mockDismissRunnable, times(1)).run();
     }
 
-    private List<EducationalTipBottomSheetItem> createListOfEducationalTipBottomSheetItem() {
-        List<EducationalTipBottomSheetItem> output = new ArrayList<>();
+    private List<EducationalTipSetupListBottomSheetItem>
+            createListOfEducationalTipBottomSheetItem() {
+        List<EducationalTipSetupListBottomSheetItem> output = new ArrayList<>();
         for (int i = 0; i < EDUCATIONAL_TIP_MODULES_SIZE; i++) {
             output.add(
-                    new EducationalTipBottomSheetItem(
+                    new EducationalTipSetupListBottomSheetItem(
                             mock(EducationalTipCardProvider.class), null));
         }
         return output;
diff --git a/chrome/browser/extensions/api/history/history_apitest.cc b/chrome/browser/extensions/api/history/history_apitest.cc
index fe3951e..7c6257a 100644
--- a/chrome/browser/extensions/api/history/history_apitest.cc
+++ b/chrome/browser/extensions/api/history/history_apitest.cc
@@ -66,12 +66,9 @@
 
 namespace extensions {
 
-using ContextType = extensions::browser_test_util::ContextType;
-
-class HistoryApiTest : public ExtensionApiTest,
-                       public testing::WithParamInterface<ContextType> {
+class HistoryApiTest : public ExtensionApiTest {
  public:
-  HistoryApiTest() : ExtensionApiTest(GetParam()) {}
+  HistoryApiTest() = default;
 
   void SetUpOnMainThread() override {
     ExtensionApiTest::SetUpOnMainThread();
@@ -91,18 +88,6 @@
   }
 };
 
-// Android only supports MV3 and later, therefore don't need to test for
-// persistent background context.
-#if !BUILDFLAG(IS_ANDROID)
-INSTANTIATE_TEST_SUITE_P(PersistentBackground,
-                         HistoryApiTest,
-                         ::testing::Values(ContextType::kPersistentBackground));
-#endif  // !BUILDFLAG(IS_ANDROID)
-
-INSTANTIATE_TEST_SUITE_P(ServiceWorker,
-                         HistoryApiTest,
-                         ::testing::Values(ContextType::kServiceWorker));
-
 class HistoryApi404Test : public HistoryApiTest {
  public:
   HistoryApi404Test() {
@@ -114,18 +99,6 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-// Android only supports Manifest V3 and later, and persistent background
-// context is removed in MV3.
-#if !BUILDFLAG(IS_ANDROID)
-INSTANTIATE_TEST_SUITE_P(PersistentBackground,
-                         HistoryApi404Test,
-                         ::testing::Values(ContextType::kPersistentBackground));
-#endif  // !BUILDFLAG(IS_ANDROID)
-
-INSTANTIATE_TEST_SUITE_P(ServiceWorker,
-                         HistoryApi404Test,
-                         ::testing::Values(ContextType::kServiceWorker));
-
 class SyncEnabledHistoryApiTest : public HistoryApiTest {
  public:
   void SetUpBrowserContextKeyedServices(
@@ -163,41 +136,34 @@
   const history::HistoryAddPageArgs args_;
 };
 
-INSTANTIATE_TEST_SUITE_P(PersistentBackground,
-                         SyncEnabledHistoryApiTest,
-                         ::testing::Values(ContextType::kPersistentBackground));
-INSTANTIATE_TEST_SUITE_P(ServiceWorker,
-                         SyncEnabledHistoryApiTest,
-                         ::testing::Values(ContextType::kServiceWorker));
-
-IN_PROC_BROWSER_TEST_P(HistoryApiTest, MiscSearch) {
+IN_PROC_BROWSER_TEST_F(HistoryApiTest, MiscSearch) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("history/regular/misc_search")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(HistoryApiTest, TimedSearch) {
+IN_PROC_BROWSER_TEST_F(HistoryApiTest, TimedSearch) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("history/regular/timed_search")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(HistoryApiTest, Delete) {
+IN_PROC_BROWSER_TEST_F(HistoryApiTest, Delete) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("history/regular/delete")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(HistoryApiTest, DeleteProhibited) {
+IN_PROC_BROWSER_TEST_F(HistoryApiTest, DeleteProhibited) {
   profile()->GetPrefs()->SetBoolean(prefs::kAllowDeletingBrowserHistory, false);
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("history/regular/delete_prohibited"))
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(HistoryApiTest, GetVisits) {
+IN_PROC_BROWSER_TEST_F(HistoryApiTest, GetVisits) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("history/regular/get_visits")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(HistoryApi404Test, GetVisits_Excludes404Visits) {
+IN_PROC_BROWSER_TEST_F(HistoryApi404Test, GetVisits_Excludes404Visits) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   history::HistoryService* history_service =
       HistoryServiceFactory::GetForProfile(profile(),
@@ -237,11 +203,10 @@
       R"({
         "name": "chrome.history",
         "version": "0.1",
-        "manifest_version": 2,
+        "manifest_version": 3,
         "permissions": ["history"],
         "background": {
-          "scripts": ["get_visits_404.js"],
-          "persistent": true
+          "service_worker": "get_visits_404.js"
         }
       })";
   static constexpr char kBackgroundJs[] =
@@ -271,7 +236,7 @@
   ASSERT_TRUE(RunExtensionTest(test_dir.UnpackedPath(), {}, {})) << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(SyncEnabledHistoryApiTest, GetVisits_Foreign) {
+IN_PROC_BROWSER_TEST_F(SyncEnabledHistoryApiTest, GetVisits_Foreign) {
   ASSERT_TRUE(StartEmbeddedTestServer());
 
   // Setup: Add a foreign (aka synced) history entry to the DB.
@@ -299,11 +264,10 @@
       R"({
         "name": "chrome.history",
         "version": "0.1",
-        "manifest_version": 2,
+        "manifest_version": 3,
         "permissions": ["history"],
         "background": {
-          "scripts": ["get_visits_foreign.js"],
-          "persistent": true
+          "service_worker": "get_visits_foreign.js"
         }
       })";
   static constexpr char kBackgroundJs[] =
@@ -334,7 +298,7 @@
   ASSERT_TRUE(RunExtensionTest(test_dir.UnpackedPath(), {}, {})) << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(SyncEnabledHistoryApiTest, SearchIncludesActorVisits) {
+IN_PROC_BROWSER_TEST_F(SyncEnabledHistoryApiTest, SearchIncludesActorVisits) {
   ASSERT_TRUE(StartEmbeddedTestServer());
 
   history::HistoryService* history_service =
@@ -363,11 +327,10 @@
       R"({
          "name": "chrome.history.actor",
          "version": "0.1",
-         "manifest_version": 2,
+         "manifest_version": 3,
          "permissions": ["history"],
          "background": {
-           "scripts": ["search_actor.js"],
-           "persistent": true
+           "service_worker": "search_actor.js"
          }
        })";
   static constexpr char kBackgroundJs[] =
@@ -389,14 +352,14 @@
   ASSERT_TRUE(RunExtensionTest(test_dir.UnpackedPath(), {}, {})) << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(HistoryApiTest, SearchAfterAdd) {
+IN_PROC_BROWSER_TEST_F(HistoryApiTest, SearchAfterAdd) {
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("history/regular/search_after_add")) << message_;
 }
 
 // Test when History API is used from incognito mode, it has access to the
 // regular mode history and actual incognito navigation has no effect on it.
-IN_PROC_BROWSER_TEST_P(HistoryApiTest, Incognito) {
+IN_PROC_BROWSER_TEST_F(HistoryApiTest, Incognito) {
   // TODO(crbug.com/40937027): Convert test to use HTTPS and then remove.
   ScopedAllowHttpForHostnamesForTesting allow_http({"www.b.com"},
                                                    profile()->GetPrefs());
diff --git a/chrome/browser/extensions/api/management/management_api_browsertest.cc b/chrome/browser/extensions/api/management/management_api_browsertest.cc
index 642056d..739603f 100644
--- a/chrome/browser/extensions/api/management/management_api_browsertest.cc
+++ b/chrome/browser/extensions/api/management/management_api_browsertest.cc
@@ -68,9 +68,7 @@
 
 class ExtensionManagementApiBrowserTest : public ExtensionBrowserTest {
  public:
-  explicit ExtensionManagementApiBrowserTest(
-      ContextType context_type = ContextType::kNone)
-      : ExtensionBrowserTest(context_type) {}
+  ExtensionManagementApiBrowserTest() = default;
   ~ExtensionManagementApiBrowserTest() override = default;
   ExtensionManagementApiBrowserTest(const ExtensionManagementApiBrowserTest&) =
       delete;
@@ -93,14 +91,10 @@
   ScopedInstallVerifierBypassForTest install_verifier_bypass_;
 };
 
-using ContextType = extensions::browser_test_util::ContextType;
-
 class ExtensionManagementApiTestWithBackgroundType
-    : public ExtensionManagementApiBrowserTest,
-      public ::testing::WithParamInterface<ContextType> {
+    : public ExtensionManagementApiBrowserTest {
  public:
-  ExtensionManagementApiTestWithBackgroundType()
-      : ExtensionManagementApiBrowserTest(GetParam()) {
+  ExtensionManagementApiTestWithBackgroundType() {
 #if !BUILDFLAG(IS_ANDROID)
     // Android does not support Chrome apps and does not have access to the
     // variable g_enable_chrome_apps_for_testing.
@@ -125,20 +119,9 @@
 #endif
 };
 
-#if !BUILDFLAG(IS_ANDROID)
-// Android does not support persistent background pages.
-INSTANTIATE_TEST_SUITE_P(PersistentBackground,
-                         ExtensionManagementApiTestWithBackgroundType,
-                         ::testing::Values(ContextType::kPersistentBackground));
-#endif  // !BUILDFLAG(IS_ANDROID)
-
-INSTANTIATE_TEST_SUITE_P(ServiceWorker,
-                         ExtensionManagementApiTestWithBackgroundType,
-                         ::testing::Values(ContextType::kServiceWorker));
-
 // We test this here instead of in an ExtensionApiTest because normal extensions
 // are not allowed to call the install function.
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType,
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType,
                        InstallEvent) {
   ExtensionTestMessageListener listener1("ready");
   ASSERT_TRUE(
@@ -154,7 +137,7 @@
 
 #if !BUILDFLAG(IS_ANDROID)
 // Android does not support Chrome apps.
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType,
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType,
                        LaunchApp) {
   ExtensionTestMessageListener listener1("app_launched");
   ExtensionTestMessageListener listener2("got_expected_error");
@@ -171,7 +154,7 @@
 }
 
 // Android does not support Chrome apps.
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType,
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType,
                        NoLaunchAppDeprecated) {
   extensions::testing::g_enable_chrome_apps_for_testing = false;
   const Extension* packaged_app =
@@ -194,7 +177,7 @@
 }
 
 // Android does not support Chrome apps.
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType,
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType,
                        LaunchAppFromBackground) {
   ExtensionTestMessageListener listener1("success");
   ASSERT_TRUE(
@@ -206,7 +189,7 @@
 }
 
 // Android does not support Chrome apps.
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType,
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType,
                        NoLaunchAppFromBackgroundDeprecated) {
   extensions::testing::g_enable_chrome_apps_for_testing = false;
   const Extension* packaged_app =
@@ -234,7 +217,7 @@
 }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType,
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType,
                        SelfUninstall) {
   // Wait for the helper script to finish before loading the primary
   // extension. This ensures that the onUninstall event listener is
@@ -249,7 +232,7 @@
   ASSERT_TRUE(listener2.WaitUntilSatisfied());
 }
 
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType,
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType,
                        SelfUninstallNoPermissions) {
   // Wait for the helper script to finish before loading the primary
   // extension. This ensures that the onUninstall event listener is
@@ -264,7 +247,7 @@
   ASSERT_TRUE(listener2.WaitUntilSatisfied());
 }
 
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType, Get) {
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType, Get) {
   ExtensionTestMessageListener listener("success");
   ASSERT_TRUE(
       LoadExtension(test_data_dir_.AppendASCII("management/simple_extension"),
@@ -273,7 +256,7 @@
   ASSERT_TRUE(listener.WaitUntilSatisfied());
 }
 
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTestWithBackgroundType,
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTestWithBackgroundType,
                        GetSelfNoPermissions) {
   ExtensionTestMessageListener listener1("success");
   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("management/get_self")));
diff --git a/chrome/browser/extensions/api/management/management_apitest.cc b/chrome/browser/extensions/api/management/management_apitest.cc
index dae1080..d2f8009d 100644
--- a/chrome/browser/extensions/api/management/management_apitest.cc
+++ b/chrome/browser/extensions/api/management/management_apitest.cc
@@ -72,10 +72,9 @@
     R"({
           "name": "Management API Test",
           "version": "0.1",
-          "manifest_version": 2,
+          "manifest_version": 3,
           "background": {
-            "scripts": ["background.js"],
-            "persistent": true
+            "service_worker": "background.js"
           },
           "replacement_web_app": "%s"
         })";
@@ -91,13 +90,9 @@
 
 }  // namespace
 
-using ContextType = extensions::browser_test_util::ContextType;
-
-class ExtensionManagementApiTest
-    : public extensions::ExtensionApiTest,
-      public testing::WithParamInterface<ContextType> {
+class ExtensionManagementApiTest : public extensions::ExtensionApiTest {
  public:
-  ExtensionManagementApiTest() : ExtensionApiTest(GetParam()) {
+  ExtensionManagementApiTest() {
 #if !BUILDFLAG(IS_ANDROID)
     enable_chrome_apps_ = std::make_unique<base::AutoReset<bool>>(
         &extensions::testing::g_enable_chrome_apps_for_testing, true);
@@ -176,18 +171,7 @@
 #endif
 };
 
-#if !BUILDFLAG(IS_ANDROID)
-// Android does not support persistent background pages.
-INSTANTIATE_TEST_SUITE_P(PersistentBackground,
-                         ExtensionManagementApiTest,
-                         ::testing::Values(ContextType::kPersistentBackground));
-#endif  // !BUILDFLAG(IS_ANDROID)
-
-INSTANTIATE_TEST_SUITE_P(ServiceWorker,
-                         ExtensionManagementApiTest,
-                         ::testing::Values(ContextType::kServiceWorker));
-
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, Basics) {
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, Basics) {
 #if !BUILDFLAG(IS_ANDROID)
   // Android does not provide the XmlUnitTestResultPrinter this method needs.
   base::AddFeatureIdTagToTestResult(
@@ -214,12 +198,12 @@
 #else
 #define MAYBE_NoPermission NoPermission
 #endif
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, MAYBE_NoPermission) {
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, MAYBE_NoPermission) {
   LoadExtensions();
   ASSERT_TRUE(RunExtensionTest("management/no_permission"));
 }
 
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, Uninstall) {
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, Uninstall) {
   LoadExtensions();
   // Confirmation dialog will be shown for uninstallations except for self.
   extensions::ScopedTestDialogAutoConfirm auto_confirm(
@@ -229,7 +213,7 @@
 
 #if !BUILDFLAG(IS_ANDROID)
 // Skipped on Android because it does not support Chrome apps.
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, CreateAppShortcut) {
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, CreateAppShortcut) {
   LoadExtensions();
   base::FilePath basedir = test_data_dir_.AppendASCII("management");
   LoadNamedExtension(basedir, "packaged_app");
@@ -239,7 +223,7 @@
 }
 
 // Skipped on Android because it does not support Chrome apps.
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, GenerateAppForLink) {
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, GenerateAppForLink) {
   web_app::test::WaitUntilReady(web_app::WebAppProvider::GetForTest(profile()));
   ASSERT_TRUE(RunExtensionTest("management/generate_app_for_link"));
 }
@@ -331,14 +315,7 @@
   net::EmbeddedTestServer https_test_server_;
 };
 
-INSTANTIATE_TEST_SUITE_P(PersistentBackground,
-                         InstallReplacementWebAppApiTest,
-                         ::testing::Values(ContextType::kPersistentBackground));
-INSTANTIATE_TEST_SUITE_P(ServiceWorker,
-                         InstallReplacementWebAppApiTest,
-                         ::testing::Values(ContextType::kServiceWorker));
-
-IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppApiTest, NotWebstore) {
+IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest, NotWebstore) {
   static constexpr char kBackground[] = R"(
   chrome.management.installReplacementWebApp(function() {
     chrome.test.assertLastError(
@@ -352,7 +329,7 @@
       kBackground, false /* from_webstore */);
 }
 
-IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppApiTest, NoGesture) {
+IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest, NoGesture) {
   static constexpr char kBackground[] = R"(
   chrome.management.installReplacementWebApp(function() {
     chrome.test.assertLastError(
@@ -366,7 +343,7 @@
       kBackground, true /* from_webstore */);
 }
 
-IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppApiTest, NotInstallableWebApp) {
+IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest, NotInstallableWebApp) {
   static constexpr char kBackground[] =
       R"(chrome.test.runWithUserGesture(function() {
            chrome.management.installReplacementWebApp(function() {
@@ -381,7 +358,7 @@
           kBackground, true /* from_webstore */);
 }
 
-IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppApiTest, InstallableWebApp) {
+IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest, InstallableWebApp) {
   static constexpr char kGoodWebAppURL[] =
       "/management/install_replacement_web_app/acceptable_web_app/index.html";
 
@@ -390,7 +367,7 @@
 
 // Check that web app still installs and launches correctly when start_url does
 // not match replacement_web_app_url.
-IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppApiTest,
+IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest,
                        InstallableWebAppWithStartUrl) {
   static constexpr char kGoodWebAppUrl[] =
       "/management/install_replacement_web_app/"
@@ -404,7 +381,7 @@
   RunInstallableWebAppTest(kManifest, kGoodWebAppUrl, kGoodWebAppStartUrl);
 }
 
-IN_PROC_BROWSER_TEST_P(InstallReplacementWebAppApiTest,
+IN_PROC_BROWSER_TEST_F(InstallReplacementWebAppApiTest,
                        InstallableWebAppInPlatformApp) {
   static constexpr char kAppManifest[] =
       R"({
@@ -424,7 +401,7 @@
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 // Tests actions on extensions when no management policy is in place.
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, ManagementPolicyAllowed) {
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, ManagementPolicyAllowed) {
   LoadExtensions();
   extensions::ScopedTestDialogAutoConfirm auto_confirm(
       extensions::ScopedTestDialogAutoConfirm::ACCEPT);
@@ -447,7 +424,7 @@
 }
 
 // Tests actions on extensions when management policy prohibits those actions.
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, ManagementPolicyProhibited) {
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, ManagementPolicyProhibited) {
   LoadExtensions();
   extensions::ExtensionRegistry* registry =
       extensions::ExtensionRegistry::Get(profile());
@@ -469,7 +446,7 @@
 
 #if !BUILDFLAG(IS_ANDROID)
 // Skipped on Android because it does not support Chrome apps.
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, LaunchPanelApp) {
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, LaunchPanelApp) {
   // Load an extension that calls launchApp() on any app that gets
   // installed.
   ExtensionTestMessageListener launcher_loaded("launcher loaded");
@@ -525,7 +502,7 @@
 }
 
 // Skipped on Android because it does not support Chrome apps.
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, LaunchTabApp) {
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, LaunchTabApp) {
   // Load an extension that calls launchApp() on any app that gets
   // installed.
   ExtensionTestMessageListener launcher_loaded("launcher loaded");
@@ -579,7 +556,7 @@
 }
 
 // Skipped on Android because it does not support Chrome apps.
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest,
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest,
                        NoLaunchPanelAppsDeprecated) {
   extensions::testing::g_enable_chrome_apps_for_testing = false;
   // Load an extension that calls launchApp() on any app that gets
@@ -609,7 +586,7 @@
 }
 
 // Skipped on Android because it does not support Chrome apps.
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, NoLaunchTabAppDeprecated) {
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, NoLaunchTabAppDeprecated) {
   extensions::testing::g_enable_chrome_apps_for_testing = false;
   // Load an extension that calls launchApp() on any app that gets
   // installed.
@@ -649,7 +626,7 @@
 #else
 #define MAYBE_LaunchType LaunchType
 #endif
-IN_PROC_BROWSER_TEST_P(ExtensionManagementApiTest, MAYBE_LaunchType) {
+IN_PROC_BROWSER_TEST_F(ExtensionManagementApiTest, MAYBE_LaunchType) {
   LoadExtensions();
   base::FilePath basedir = test_data_dir_.AppendASCII("management");
   LoadNamedExtension(basedir, "packaged_app");
diff --git a/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc b/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc
index 7487061..739f849e 100644
--- a/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc
+++ b/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc
@@ -43,17 +43,12 @@
 namespace extensions {
 namespace {
 
-using ContextType = extensions::browser_test_util::ContextType;
-
-class NativeMessagingApiTestBase : public ExtensionApiTest {
+class NativeMessagingApiTest : public ExtensionApiTest {
  public:
-  explicit NativeMessagingApiTestBase(
-      ContextType context_type = ContextType::kNone)
-      : ExtensionApiTest(context_type) {}
-  ~NativeMessagingApiTestBase() override = default;
-  NativeMessagingApiTestBase(const NativeMessagingApiTestBase&) = delete;
-  NativeMessagingApiTestBase& operator=(const NativeMessagingApiTestBase&) =
-      delete;
+  NativeMessagingApiTest() = default;
+  ~NativeMessagingApiTest() override = default;
+  NativeMessagingApiTest(const NativeMessagingApiTest&) = delete;
+  NativeMessagingApiTest& operator=(const NativeMessagingApiTest&) = delete;
 
  protected:
   size_t GetTotalTabCount() const {
@@ -69,13 +64,13 @@
 
 // Tests basic functionality of chrome.runtime.sendNativeMessage in an MV3
 // extension.
-IN_PROC_BROWSER_TEST_F(NativeMessagingApiTestBase, SendNativeMessage) {
+IN_PROC_BROWSER_TEST_F(NativeMessagingApiTest, SendNativeMessage) {
   constexpr bool kUserLevel = false;
   ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(kUserLevel));
   ASSERT_TRUE(RunExtensionTest("native_messaging_send_native_message"));
 }
 
-IN_PROC_BROWSER_TEST_F(NativeMessagingApiTestBase, UserLevelSendNativeMessage) {
+IN_PROC_BROWSER_TEST_F(NativeMessagingApiTest, UserLevelSendNativeMessage) {
   constexpr bool kUserLevel = true;
   ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(kUserLevel));
   ASSERT_TRUE(RunExtensionTest("native_messaging_send_native_message"));
@@ -85,7 +80,7 @@
 // On Windows, a new codepath is used to directly launch .EXE-based Native
 // Hosts. This codepath allows launching of Native Hosts even when cmd.exe is
 // disabled or misconfigured.
-class NativeMessagingLaunchExeTest : public NativeMessagingApiTestBase,
+class NativeMessagingLaunchExeTest : public NativeMessagingApiTest,
                                      public testing::WithParamInterface<bool> {
  public:
   NativeMessagingLaunchExeTest() {
@@ -134,55 +129,27 @@
 }
 #endif
 
-class NativeMessagingApiTest : public NativeMessagingApiTestBase,
-                               public testing::WithParamInterface<ContextType> {
- public:
-  NativeMessagingApiTest() : NativeMessagingApiTestBase(GetParam()) {}
-  ~NativeMessagingApiTest() override = default;
-  NativeMessagingApiTest(const NativeMessagingApiTest&) = delete;
-  NativeMessagingApiTest& operator=(const NativeMessagingApiTest&) = delete;
-
- protected:
-  bool RunTest(const char* extension_name) {
-    if (GetParam() == ContextType::kPersistentBackground) {
-      return RunExtensionTest(extension_name);
-    }
-    std::string lazy_extension_name = base::StrCat({extension_name, "/lazy"});
-    return RunExtensionTest(lazy_extension_name.c_str());
-  }
-};
-
-INSTANTIATE_TEST_SUITE_P(PersistentBackground,
-                         NativeMessagingApiTest,
-                         ::testing::Values(ContextType::kPersistentBackground));
-INSTANTIATE_TEST_SUITE_P(EventPage,
-                         NativeMessagingApiTest,
-                         ::testing::Values(ContextType::kEventPage));
-INSTANTIATE_TEST_SUITE_P(ServiceWorker,
-                         NativeMessagingApiTest,
-                         ::testing::Values(ContextType::kServiceWorker));
-
 // Tests chrome.runtime.sendNativeMessage to a native messaging host.
-IN_PROC_BROWSER_TEST_P(NativeMessagingApiTest, NativeMessagingBasic) {
+IN_PROC_BROWSER_TEST_F(NativeMessagingApiTest, NativeMessagingBasic) {
   ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(false));
-  ASSERT_TRUE(RunTest("native_messaging")) << message_;
+  ASSERT_TRUE(RunExtensionTest("native_messaging")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(NativeMessagingApiTest, UserLevelNativeMessaging) {
+IN_PROC_BROWSER_TEST_F(NativeMessagingApiTest, UserLevelNativeMessaging) {
   ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(true));
-  ASSERT_TRUE(RunTest("native_messaging")) << message_;
+  ASSERT_TRUE(RunExtensionTest("native_messaging")) << message_;
 }
 
 // Tests chrome.runtime.connectNative to a native messaging host.
-IN_PROC_BROWSER_TEST_P(NativeMessagingApiTest, ConnectNative) {
+IN_PROC_BROWSER_TEST_F(NativeMessagingApiTest, ConnectNative) {
   ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(false));
-  ASSERT_TRUE(RunTest("native_messaging_connect")) << message_;
+  ASSERT_TRUE(RunExtensionTest("native_messaging_connect")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(NativeMessagingApiTest,
+IN_PROC_BROWSER_TEST_F(NativeMessagingApiTest,
                        UserLevelNativeMessagingConnectNative) {
   ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(true));
-  ASSERT_TRUE(RunTest("native_messaging_connect")) << message_;
+  ASSERT_TRUE(RunExtensionTest("native_messaging_connect")) << message_;
 }
 
 #if !BUILDFLAG(IS_CHROMEOS)
@@ -204,7 +171,7 @@
   return command_line;
 }
 
-class NativeMessagingLaunchApiTest : public NativeMessagingApiTestBase {
+class NativeMessagingLaunchApiTest : public NativeMessagingApiTest {
  public:
   NativeMessagingLaunchApiTest() {
     feature_list_.InitAndEnableFeature(features::kOnConnectNative);
@@ -494,7 +461,7 @@
 
 #if BUILDFLAG(IS_WIN)
 class NativeHostExecutablesLaunchDirectlyPolicyTest
-    : public extensions::NativeMessagingApiTestBase,
+    : public extensions::NativeMessagingApiTest,
       public testing::WithParamInterface<bool> {
  public:
   NativeHostExecutablesLaunchDirectlyPolicyTest() {
diff --git a/chrome/browser/extensions/api/metrics_private/metrics_apitest.cc b/chrome/browser/extensions/api/metrics_private/metrics_apitest.cc
index 25c6d24..05a906fd 100644
--- a/chrome/browser/extensions/api/metrics_private/metrics_apitest.cc
+++ b/chrome/browser/extensions/api/metrics_private/metrics_apitest.cc
@@ -149,27 +149,15 @@
 
 }  // namespace
 
-using ContextType = extensions::browser_test_util::ContextType;
-
-class ExtensionMetricsApiTest
-    : public ExtensionApiTest,
-      public testing::WithParamInterface<ContextType> {
+class ExtensionMetricsApiTest : public ExtensionApiTest {
  public:
-  ExtensionMetricsApiTest() : ExtensionApiTest(GetParam()) {}
+  ExtensionMetricsApiTest() = default;
   ~ExtensionMetricsApiTest() override = default;
   ExtensionMetricsApiTest(const ExtensionMetricsApiTest&) = delete;
   ExtensionMetricsApiTest& operator=(const ExtensionMetricsApiTest&) = delete;
 };
 
-INSTANTIATE_TEST_SUITE_P(PersistentBackground,
-                         ExtensionMetricsApiTest,
-                         ::testing::Values(ContextType::kPersistentBackground));
-
-INSTANTIATE_TEST_SUITE_P(ServiceWorker,
-                         ExtensionMetricsApiTest,
-                         ::testing::Values(ContextType::kServiceWorker));
-
-IN_PROC_BROWSER_TEST_P(ExtensionMetricsApiTest, Metrics) {
+IN_PROC_BROWSER_TEST_F(ExtensionMetricsApiTest, Metrics) {
   ukm::TestAutoSetUkmRecorder ukm_recorder;
 
   base::UserActionTester user_action_tester;
diff --git a/chrome/browser/extensions/api/module/module_apitest.cc b/chrome/browser/extensions/api/module/module_apitest.cc
index 18c9158..63d3f1f 100644
--- a/chrome/browser/extensions/api/module/module_apitest.cc
+++ b/chrome/browser/extensions/api/module/module_apitest.cc
@@ -14,47 +14,34 @@
 
 namespace {
 
-using ContextType = extensions::browser_test_util::ContextType;
-
-class ExtensionModuleApiTest : public ExtensionApiTest,
-                               public testing::WithParamInterface<ContextType> {
+class ExtensionModuleApiTest : public ExtensionApiTest {
  public:
-  ExtensionModuleApiTest() : ExtensionApiTest(GetParam()) {}
+  ExtensionModuleApiTest() = default;
   ~ExtensionModuleApiTest() override = default;
   ExtensionModuleApiTest(const ExtensionModuleApiTest&) = delete;
   ExtensionModuleApiTest& operator=(const ExtensionModuleApiTest&) = delete;
 };
 
-// Android only supports service worker.
-#if !BUILDFLAG(IS_ANDROID)
-INSTANTIATE_TEST_SUITE_P(PersistentBackground,
-                         ExtensionModuleApiTest,
-                         ::testing::Values(ContextType::kPersistentBackground));
-#endif
-INSTANTIATE_TEST_SUITE_P(ServiceWorker,
-                         ExtensionModuleApiTest,
-                         ::testing::Values(ContextType::kServiceWorker));
-
 }  // namespace
 
-IN_PROC_BROWSER_TEST_P(ExtensionModuleApiTest, CognitoFile) {
+IN_PROC_BROWSER_TEST_F(ExtensionModuleApiTest, CognitoFile) {
   ASSERT_TRUE(RunExtensionTest("extension_module/cognito_file", {},
                                {.allow_file_access = true}))
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(ExtensionModuleApiTest, IncognitoFile) {
+IN_PROC_BROWSER_TEST_F(ExtensionModuleApiTest, IncognitoFile) {
   ASSERT_TRUE(
       RunExtensionTest("extension_module/incognito_file", {},
                        {.allow_in_incognito = true, .allow_file_access = true}))
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(ExtensionModuleApiTest, CognitoNoFile) {
+IN_PROC_BROWSER_TEST_F(ExtensionModuleApiTest, CognitoNoFile) {
   ASSERT_TRUE(RunExtensionTest("extension_module/cognito_nofile")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_P(ExtensionModuleApiTest, IncognitoNoFile) {
+IN_PROC_BROWSER_TEST_F(ExtensionModuleApiTest, IncognitoNoFile) {
   ASSERT_TRUE(RunExtensionTest("extension_module/incognito_nofile", {},
                                {.allow_in_incognito = true}))
       << message_;
diff --git a/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc b/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
index f7b1754..783a107 100644
--- a/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
+++ b/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
@@ -123,13 +123,10 @@
   }
 }
 
-using ContextType = browser_test_util::ContextType;
-
-class OmniboxApiTestBase : public ExtensionApiTest {
+class OmniboxApiTest : public ExtensionApiTest {
  public:
-  explicit OmniboxApiTestBase(ContextType context_type = ContextType::kNone)
-      : ExtensionApiTest(context_type) {}
-  ~OmniboxApiTestBase() override = default;
+  OmniboxApiTest() = default;
+  ~OmniboxApiTest() override = default;
 
   void SetUpOnMainThread() override {
     ExtensionApiTest::SetUpOnMainThread();
@@ -172,30 +169,6 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 };
 
-class OmniboxApiTest : public OmniboxApiTestBase,
-                       public testing::WithParamInterface<ContextType> {
- public:
-  OmniboxApiTest() : OmniboxApiTestBase(GetParam()) {}
-  ~OmniboxApiTest() override = default;
-};
-
-INSTANTIATE_TEST_SUITE_P(ServiceWorker,
-                         OmniboxApiTest,
-                         testing::Values(ContextType::kServiceWorker));
-
-// Desktop Android only supports service worker.
-#if !BUILDFLAG(IS_ANDROID)
-INSTANTIATE_TEST_SUITE_P(PersistentBackground,
-                         OmniboxApiTest,
-                         testing::Values(ContextType::kPersistentBackground));
-
-using OmniboxApiBackgroundPageTest = OmniboxApiTest;
-
-INSTANTIATE_TEST_SUITE_P(All,
-                         OmniboxApiBackgroundPageTest,
-                         testing::Values(ContextType::kNone));
-#endif  // !BUILDFLAG(IS_ANDROID)
-
 }  // namespace
 
 // TODO(crbug.com/326903502): Flaky on TSan.
@@ -204,14 +177,14 @@
 #else
 #define MAYBE_SendSuggestions SendSuggestions
 #endif
-IN_PROC_BROWSER_TEST_P(OmniboxApiTest, MAYBE_SendSuggestions) {
+IN_PROC_BROWSER_TEST_F(OmniboxApiTest, MAYBE_SendSuggestions) {
   constexpr char kManifest[] =
       R"({
            "name": "Basic Send Suggestions",
-           "manifest_version": 2,
+           "manifest_version": 3,
            "version": "0.1",
            "omnibox": { "keyword": "alpha" },
-           "background": { "scripts": [ "background.js" ], "persistent": true }
+           "background": { "service_worker": "background.js" }
          })";
   constexpr char kBackground[] =
       R"(chrome.omnibox.onInputChanged.addListener((text, suggest) => {
@@ -329,14 +302,14 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 // TODO(crbug.com/405219624): Port these tests to desktop Android. Most require
 // access to the Views location bar, which is not available on Android.
-IN_PROC_BROWSER_TEST_P(OmniboxApiTest, OnInputEntered) {
+IN_PROC_BROWSER_TEST_F(OmniboxApiTest, OnInputEntered) {
   constexpr char kManifest[] =
       R"({
            "name": "Basic Send Suggestions",
-           "manifest_version": 2,
+           "manifest_version": 3,
            "version": "0.1",
            "omnibox": { "keyword": "alpha" },
-           "background": { "scripts": [ "background.js" ], "persistent": true }
+           "background": { "service_worker": "background.js" }
          })";
   // This extension will collect input entered into the omnibox and pass it
   // to the browser when instructed.
@@ -388,15 +361,15 @@
 // Tests receiving suggestions from and sending input to the incognito context
 // of an incognito split mode extension.
 // Regression test for https://crbug.com/40100987.
-IN_PROC_BROWSER_TEST_P(OmniboxApiTest, IncognitoSplitMode) {
+IN_PROC_BROWSER_TEST_F(OmniboxApiTest, IncognitoSplitMode) {
   static constexpr char kManifest[] =
       R"({
            "name": "SetDefaultSuggestion",
-           "manifest_version": 2,
+           "manifest_version": 3,
            "version": "0.1",
            "omnibox": { "keyword": "alpha" },
            "incognito": "split",
-           "background": { "scripts": [ "background.js" ], "persistent": true }
+           "background": { "service_worker": "background.js" }
          })";
   static constexpr char kBackground[] =
       R"(let suggestionSuffix =
@@ -506,7 +479,7 @@
 // Tests that the autocomplete popup doesn't reopen after accepting input for
 // a given query.
 // http://crbug.com/88552
-IN_PROC_BROWSER_TEST_P(OmniboxApiBackgroundPageTest, MAYBE_PopupStaysClosed) {
+IN_PROC_BROWSER_TEST_F(OmniboxApiTest, MAYBE_PopupStaysClosed) {
   ASSERT_TRUE(RunExtensionTest("omnibox")) << message_;
 
   LocationBar* location_bar = GetLocationBar(browser());
@@ -550,14 +523,14 @@
 #else
 #define MAYBE_DeleteOmniboxSuggestionResult DeleteOmniboxSuggestionResult
 #endif
-IN_PROC_BROWSER_TEST_P(OmniboxApiTest, MAYBE_DeleteOmniboxSuggestionResult) {
+IN_PROC_BROWSER_TEST_F(OmniboxApiTest, MAYBE_DeleteOmniboxSuggestionResult) {
   static constexpr char kManifest[] =
       R"({
            "name": "Basic Send Suggestions",
-           "manifest_version": 2,
+           "manifest_version": 3,
            "version": "0.1",
            "omnibox": { "keyword": "alpha" },
-           "background": { "scripts": [ "background.js" ], "persistent": true }
+           "background": { "service_worker": "background.js" }
          })";
   static constexpr char kBackground[] =
       R"(chrome.omnibox.onInputChanged.addListener((text, suggest) => {
@@ -659,15 +632,15 @@
 // Tests that if the user hits "backspace" (leaving the extension keyword mode),
 // the extension suggestions are not sent.
 // TODO(crbug.com/40839815): Flaky.
-IN_PROC_BROWSER_TEST_P(OmniboxApiTest,
+IN_PROC_BROWSER_TEST_F(OmniboxApiTest,
                        DISABLED_ExtensionSuggestionsOnlyInKeywordMode) {
   static constexpr char kManifest[] =
       R"({
            "name": "Basic Send Suggestions",
-           "manifest_version": 2,
+           "manifest_version": 3,
            "version": "0.1",
            "omnibox": { "keyword": "kw" },
-           "background": { "scripts": [ "background.js" ], "persistent": true }
+           "background": { "service_worker": "background.js" }
          })";
   static constexpr char kBackground[] =
       R"(chrome.omnibox.onInputChanged.addListener((text, suggest) => {
@@ -751,14 +724,14 @@
 }
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
-IN_PROC_BROWSER_TEST_P(OmniboxApiTest, SetDefaultSuggestionFailures) {
+IN_PROC_BROWSER_TEST_F(OmniboxApiTest, SetDefaultSuggestionFailures) {
   constexpr char kManifest[] =
       R"({
            "name": "SetDefaultSuggestion",
-           "manifest_version": 2,
+           "manifest_version": 3,
            "version": "0.1",
            "omnibox": { "keyword": "word" },
-           "background": { "scripts": [ "background.js" ], "persistent": true }
+           "background": { "service_worker": "background.js" }
          })";
   constexpr char kBackground[] =
       R"(chrome.test.runTests([
@@ -820,14 +793,14 @@
 #else
 #define MAYBE_SetDefaultSuggestion SetDefaultSuggestion
 #endif
-IN_PROC_BROWSER_TEST_P(OmniboxApiTest, MAYBE_SetDefaultSuggestion) {
+IN_PROC_BROWSER_TEST_F(OmniboxApiTest, MAYBE_SetDefaultSuggestion) {
   constexpr char kManifest[] =
       R"({
            "name": "SetDefaultSuggestion",
-           "manifest_version": 2,
+           "manifest_version": 3,
            "version": "0.1",
            "omnibox": { "keyword": "word" },
-           "background": { "scripts": [ "background.js" ], "persistent": true }
+           "background": { "service_worker": "background.js" }
          })";
   constexpr char kBackground[] =
       R"(chrome.test.runTests([
@@ -888,14 +861,14 @@
 #else
 #define MAYBE_PassEmptySuggestions PassEmptySuggestions
 #endif
-IN_PROC_BROWSER_TEST_P(OmniboxApiTest, MAYBE_PassEmptySuggestions) {
+IN_PROC_BROWSER_TEST_F(OmniboxApiTest, MAYBE_PassEmptySuggestions) {
   static constexpr char kManifest[] =
       R"({
            "name": "Basic Send Suggestions",
-           "manifest_version": 2,
+           "manifest_version": 3,
            "version": "0.1",
            "omnibox": { "keyword": "alpha" },
-           "background": { "scripts": [ "background.js" ], "persistent": true }
+           "background": { "service_worker": "background.js" }
          })";
   // Register a listener that passes back empty suggestions if there is no
   // text content.
@@ -965,7 +938,7 @@
 }
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
-class UnscopedOmniboxApiTest : public OmniboxApiTestBase {
+class UnscopedOmniboxApiTest : public OmniboxApiTest {
  public:
   UnscopedOmniboxApiTest() {
     // TODO(crbug.com/441102004): Update UnscopedExtensionZeroSuggest to support
@@ -983,7 +956,7 @@
 
  private:
   void SetUpOnMainThread() override {
-    OmniboxApiTestBase::SetUpOnMainThread();
+    OmniboxApiTest::SetUpOnMainThread();
     // Prevent the stop timer from killing the hints fetch early, which might
     // cause test flakiness due to timeout.
     SetStopTimerDuration(base::Seconds(30));
diff --git a/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc b/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc
index dd5a498..a6dcdf3 100644
--- a/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc
+++ b/chrome/browser/extensions/api/printer_provider/printer_provider_apitest.cc
@@ -74,15 +74,14 @@
 }
 
 // Tests for chrome.printerProvider API.
-class PrinterProviderApiTest : public ExtensionApiTest,
-                               public testing::WithParamInterface<ContextType> {
+class PrinterProviderApiTest : public ExtensionApiTest {
  public:
   enum PrintRequestDataType {
     PRINT_REQUEST_DATA_TYPE_NOT_SET,
     PRINT_REQUEST_DATA_TYPE_BYTES
   };
 
-  PrinterProviderApiTest() : ExtensionApiTest(GetParam()) {}
+  PrinterProviderApiTest() = default;
   ~PrinterProviderApiTest() override = default;
   PrinterProviderApiTest(const PrinterProviderApiTest&) = delete;
   PrinterProviderApiTest& operator=(const PrinterProviderApiTest&) = delete;
@@ -254,44 +253,37 @@
   }
 };
 
-INSTANTIATE_TEST_SUITE_P(EventPage,
-                         PrinterProviderApiTest,
-                         ::testing::Values(ContextType::kEventPage));
-INSTANTIATE_TEST_SUITE_P(ServiceWorker,
-                         PrinterProviderApiTest,
-                         ::testing::Values(ContextType::kServiceWorker));
-
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, PrintJobSuccess) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, PrintJobSuccess) {
   RunPrintRequestTestExtension("OK", PRINT_REQUEST_DATA_TYPE_BYTES, "OK");
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, PrintJobAsyncSuccess) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, PrintJobAsyncSuccess) {
   RunPrintRequestTestExtension("ASYNC_RESPONSE", PRINT_REQUEST_DATA_TYPE_BYTES,
                                "OK");
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, PrintJobFailed) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, PrintJobFailed) {
   RunPrintRequestTestExtension("INVALID_TICKET", PRINT_REQUEST_DATA_TYPE_BYTES,
                                "INVALID_TICKET");
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, NoPrintEventListener) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, NoPrintEventListener) {
   RunPrintRequestTestExtension("NO_LISTENER", PRINT_REQUEST_DATA_TYPE_BYTES,
                                "FAILED");
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest,
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest,
                        PrintRequestInvalidCallbackParam) {
   RunPrintRequestTestExtension("INVALID_VALUE", PRINT_REQUEST_DATA_TYPE_BYTES,
                                "FAILED");
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, PrintRequestDataNotSet) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, PrintRequestDataNotSet) {
   RunPrintRequestTestExtension("IGNORE_CALLBACK",
                                PRINT_REQUEST_DATA_TYPE_NOT_SET, "FAILED");
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, PrintRequestExtensionUnloaded) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, PrintRequestExtensionUnloaded) {
   ResultCatcher catcher;
 
   ExtensionId extension_id;
@@ -317,28 +309,28 @@
   EXPECT_EQ("FAILED", status);
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetCapabilitySuccess) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetCapabilitySuccess) {
   RunPrinterCapabilitiesRequestTest("OK", "{\"capability\":\"value\"}");
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetCapabilityAsyncSuccess) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetCapabilityAsyncSuccess) {
   RunPrinterCapabilitiesRequestTest("ASYNC_RESPONSE",
                                     "{\"capability\":\"value\"}");
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, EmptyCapability) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, EmptyCapability) {
   RunPrinterCapabilitiesRequestTest("EMPTY", "{}");
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, NoCapabilityEventListener) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, NoCapabilityEventListener) {
   RunPrinterCapabilitiesRequestTest("NO_LISTENER", "{}");
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, CapabilityInvalidValue) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, CapabilityInvalidValue) {
   RunPrinterCapabilitiesRequestTest("INVALID_VALUE", "{}");
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetCapabilityExtensionUnloaded) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetCapabilityExtensionUnloaded) {
   ResultCatcher catcher;
 
   ExtensionId extension_id;
@@ -356,7 +348,7 @@
   EXPECT_EQ("{}", *result);
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersSuccess) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersSuccess) {
   ResultCatcher catcher;
 
   ExtensionId extension_id;
@@ -391,7 +383,7 @@
   ValidatePrinterListValue(printers, expected_printers);
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersAsyncSuccess) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersAsyncSuccess) {
   ResultCatcher catcher;
 
   ExtensionId extension_id;
@@ -419,7 +411,7 @@
   ValidatePrinterListValue(printers, expected_printers);
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersTwoExtensions) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersTwoExtensions) {
   ResultCatcher catcher;
 
   ExtensionId extension_id_1;
@@ -474,7 +466,7 @@
   ValidatePrinterListValue(printers, expected_printers);
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest,
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest,
                        GetPrintersTwoExtensionsBothUnloaded) {
   ResultCatcher catcher;
 
@@ -504,7 +496,7 @@
   EXPECT_TRUE(printers.empty());
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest,
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest,
                        GetPrintersTwoExtensionsOneFails) {
   ResultCatcher catcher;
 
@@ -546,7 +538,7 @@
   ValidatePrinterListValue(printers, expected_printers);
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest,
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest,
                        GetPrintersTwoExtensionsOneWithNoListener) {
   ResultCatcher catcher;
 
@@ -588,7 +580,7 @@
   ValidatePrinterListValue(printers, expected_printers);
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersNoListener) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersNoListener) {
   ResultCatcher catcher;
 
   ExtensionId extension_id;
@@ -607,7 +599,7 @@
   EXPECT_TRUE(printers.empty());
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersNotArray) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersNotArray) {
   ResultCatcher catcher;
 
   ExtensionId extension_id;
@@ -626,7 +618,7 @@
   EXPECT_TRUE(printers.empty());
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest,
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest,
                        GetPrintersInvalidPrinterValueType) {
   ResultCatcher catcher;
 
@@ -646,7 +638,7 @@
   EXPECT_TRUE(printers.empty());
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderApiTest, GetPrintersInvalidPrinterValue) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderApiTest, GetPrintersInvalidPrinterValue) {
   ResultCatcher catcher;
 
   ExtensionId extension_id;
@@ -710,11 +702,8 @@
 };
 
 // These are only instantiated for event page-based packaged apps.
-INSTANTIATE_TEST_SUITE_P(EventPage,
-                         PrinterProviderUsbApiTest,
-                         ::testing::Values(ContextType::kEventPage));
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderUsbApiTest, GetUsbPrinterInfo) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderUsbApiTest, GetUsbPrinterInfo) {
   ResultCatcher catcher;
   device::mojom::UsbDeviceInfoPtr device =
       usb_manager_.CreateAndAddDevice(0, 0, "Google", "USB Printer", "");
@@ -741,12 +730,12 @@
   ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderUsbApiTest,
+IN_PROC_BROWSER_TEST_F(PrinterProviderUsbApiTest,
                        GetUsbPrinterInfoEmptyResponse) {
   RunUsbPrinterInfoRequestTest("EMPTY_RESPONSE");
 }
 
-IN_PROC_BROWSER_TEST_P(PrinterProviderUsbApiTest, GetUsbPrinterInfoNoListener) {
+IN_PROC_BROWSER_TEST_F(PrinterProviderUsbApiTest, GetUsbPrinterInfoNoListener) {
   RunUsbPrinterInfoRequestTest("NO_LISTENER");
 }
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 191c7594..e9e398e 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1877,11 +1877,6 @@
     "expiry_milestone": 150
   },
   {
-    "name": "contextual-tasks-expand-button",
-    "owners": ["mdjones@google.com", "chrome-contextual-tasks-eng@google.com"],
-    "expiry_milestone": 150
-  },
-  {
     "name": "contextual-tasks-suggestions-enabled",
     "owners": ["dianaou@google.com", "chrome-contextual-tasks-eng@google.com"],
     "expiry_milestone": 150
@@ -2400,11 +2395,6 @@
     "expiry_milestone": 150
   },
   {
-    "name": "disable-quick-answers-v2-translation",
-    "owners": [ "croissant-eng@chromium.org" ],
-    "expiry_milestone": 100
-  },
-  {
     "name": "disable-u18-feedback-desktop",
     "owners": [ "chrome-signin-team@google.com" ],
     "expiry_milestone": 160
@@ -5747,7 +5737,7 @@
       "meacer@chromium.org",
       "chrome-secure-web-and-net@chromium.org"
     ],
-    "expiry_milestone": 125
+    "expiry_milestone": 150
   },
 
   {
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index e05ed20..3b7f520 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -7934,11 +7934,6 @@
     "Allows the ChromeOS print preview to be opened instead of the browser "
     " print preview.";
 
-inline constexpr char kDisableQuickAnswersV2TranslationName[] =
-    "Disable Quick Answers Translation";
-inline constexpr char kDisableQuickAnswersV2TranslationDescription[] =
-    "Disable translation services of the Quick Answers.";
-
 inline constexpr char kQuickAnswersRichCardName[] =
     "Enable Quick Answers Rich Card";
 inline constexpr char kQuickAnswersRichCardDescription[] =
diff --git a/chrome/browser/glic/browser_ui/tab_underline_view_controller_impl.cc b/chrome/browser/glic/browser_ui/tab_underline_view_controller_impl.cc
index 76cbeb58..59a64df2 100644
--- a/chrome/browser/glic/browser_ui/tab_underline_view_controller_impl.cc
+++ b/chrome/browser/glic/browser_ui/tab_underline_view_controller_impl.cc
@@ -22,12 +22,7 @@
 
 TabUnderlineViewControllerImpl::TabUnderlineViewControllerImpl() = default;
 
-TabUnderlineViewControllerImpl::~TabUnderlineViewControllerImpl() {
-  if (glic_service_ && !GlicEnabling::IsMultiInstanceEnabled()) {
-    glic_service_->GetSingleInstanceWindowController().RemoveStateObserver(
-        this);
-  }
-}
+TabUnderlineViewControllerImpl::~TabUnderlineViewControllerImpl() = default;
 
 // This implementation makes many references to "pinned" tabs. All of these
 // refer to tabs that are selected to be shared with Gemini under the glic
@@ -50,23 +45,6 @@
 
     GlicSharingManager& sharing_manager = glic_service_->sharing_manager();
 
-    if (!GlicEnabling::IsMultiInstanceEnabled()) {
-      // Subscribe to changes in the focused tab.
-      focus_change_subscription_ =
-          sharing_manager.AddFocusedTabChangedCallback(base::BindRepeating(
-              &TabUnderlineViewControllerImpl::OnFocusedTabChanged,
-              base::Unretained(this)));
-      // Subscribe to changes in the context access indicator status.
-      indicator_change_subscription_ =
-          glic_service_->AddContextAccessIndicatorStatusChangedCallback(
-              base::BindRepeating(
-                  &TabUnderlineViewControllerImpl::OnIndicatorStatusChanged,
-                  base::Unretained(this)));
-
-      // Observe changes in the floaty state.
-      glic_service_->GetSingleInstanceWindowController().AddStateObserver(this);
-    }
-
     // Subscribe to changes in the set of pinned tabs.
     pinned_tabs_change_subscription_ =
         sharing_manager.AddPinnedTabsChangedCallback(base::BindRepeating(
@@ -270,8 +248,7 @@
     case UpdateUnderlineReason::kContextAccessIndicatorOff: {
       // Underline should be hidden, with exception to pinned tabs while the
       // glic panel remains open.
-      if (IsUnderlineTabPinned() &&
-          (GlicEnabling::IsMultiInstanceEnabled() || IsGlicWindowShowing())) {
+      if (IsUnderlineTabPinned()) {
         break;
       }
       HideUnderline(/*triggered_by_glic=*/true);
@@ -320,24 +297,7 @@
       }
       break;
     case UpdateUnderlineReason::kPinnedTabsChanged_TabInPinnedSet:
-      if (GlicEnabling::IsMultiInstanceEnabled()) {
         ShowAndAnimateUnderline(/*triggered_by_glic=*/true);
-      } else {
-        // If `underline_view_` is not visible, then this tab was just added
-        // to the set of pinned tabs.
-        if (!underline_view_->IsShowing()) {
-          // Pinned tab underlines should only be visible while the glic panel
-          // is open. For multi-instance this is controlled via the pinned
-          // tabs api.
-          if (IsGlicWindowShowing()) {
-            ShowAndAnimateUnderline(/*triggered_by_glic=*/true);
-          }
-        } else {
-          // This tab was already pinned - re-animate to reflect the change in
-          // the set of pinned tabs.
-          AnimateUnderline();
-        }
-      }
       break;
     case UpdateUnderlineReason::kPinnedTabsChanged_TabNotInPinnedSet:
       // Re-animate to reflect the change in the set of pinned tabs.
@@ -429,13 +389,9 @@
   if (!IsUnderlineTabPinned()) {
     return;
   }
-  // For multi-instance, we rely on the umbrella sharing manager behavior to
-  // determine when to show or not show underlines via the pinned tabs api.
-  if (!GlicEnabling::IsMultiInstanceEnabled()) {
-    // Pinned underlines should never be visible if the glic window is closed.
-    if (!IsGlicWindowShowing()) {
-      return;
-    }
+  // Pinned underlines should never be visible if the glic window is closed.
+  if (!IsGlicWindowShowing()) {
+    return;
   }
   if (underline_view_->IsShowing()) {
     AnimateUnderline();
diff --git a/chrome/browser/glic/browser_ui/tab_underline_view_interactive_uitest.cc b/chrome/browser/glic/browser_ui/tab_underline_view_interactive_uitest.cc
index 0ea7d868..4e8de099 100644
--- a/chrome/browser/glic/browser_ui/tab_underline_view_interactive_uitest.cc
+++ b/chrome/browser/glic/browser_ui/tab_underline_view_interactive_uitest.cc
@@ -9,6 +9,7 @@
 #include "cc/test/pixel_test_utils.h"
 #include "chrome/browser/glic/browser_ui/tab_underline_view.h"
 #include "chrome/browser/glic/browser_ui/tab_underline_view_controller_impl.h"
+#include "chrome/browser/glic/host/glic.mojom.h"
 #include "chrome/browser/glic/host/glic_features.mojom.h"
 #include "chrome/browser/glic/test_support/glic_test_util.h"
 #include "chrome/browser/glic/test_support/interactive_glic_test.h"
@@ -174,14 +175,16 @@
         mojom::features::kGlicMultiTab.name;
     const std::string underline_feature_name =
         features::kGlicMultitabUnderlines.name;
+    const std::string multi_instance_feature_name =
+        features::kGlicMultiInstance.name;
     // Toggling `UiGpuRasterization` is only possible via command line.
     const std::string enabled_features =
         base::StrCat({multitab_feature_name, ",", underline_feature_name, ",",
-                      "UiGpuRasterization"});
+                      multi_instance_feature_name, ",", "UiGpuRasterization"});
     features_.InitFromCommandLine(enabled_features,
                                   /*disabled_features=*/
                                   "ContextualTasks,GlicForceSimplifiedBorder,"
-                                  "GlicForceNonSkSLBorder,GlicMultiInstance");
+                                  "GlicForceNonSkSLBorder");
   }
   ~TabUnderlineViewUiTest() override = default;
 
@@ -363,14 +366,6 @@
   EXPECT_TRUE(underline->IsShowing());
   tester->AdvanceTimeAndTickAnimation(base::TimeDelta());
   tester->AdvanceTimeAndTickAnimation(base::Seconds(0.3));
-
-  // The underline should hide when sharing is turned off.
-  glic_service()->SetContextAccessIndicator(false);
-  ASSERT_FALSE(
-      glic_service()->IsContextAccessIndicatorShown(GetActiveWebContents()));
-  tester->WaitForRampDownStarted();
-  tester->FinishRampDown();
-  EXPECT_FALSE(underline->IsShowing());
 }
 
 IN_PROC_BROWSER_TEST_F(TabUnderlineViewUiTest,
@@ -379,50 +374,27 @@
   EXPECT_TRUE(glic_service()->IsWindowShowing());
   auto* underline = GetUnderlineOfActiveTab();
   TesterImpl* tester = static_cast<TesterImpl*>(underline->tester());
-  EXPECT_FALSE(underline->IsShowing());
-
-  // The underline should show when its tab is pinned.
-  tabs::TabHandle tab_handle = TabHandleAtIndex(0);
-  PinTabs({tab_handle});
-  ASSERT_TRUE(sharing_manager().IsTabPinned(tab_handle));
-  tester->WaitForAnimationStart();
+  // Tab is pinned by default.
   EXPECT_TRUE(underline->IsShowing());
   tester->AdvanceTimeAndTickAnimation(base::TimeDelta());
   tester->AdvanceTimeAndTickAnimation(base::Seconds(0.3));
 
   // The underline should hide when its tab is unpinned.
   sharing_manager().UnpinAllTabs();
-  ASSERT_FALSE(sharing_manager().IsTabPinned(tab_handle));
+  ASSERT_FALSE(sharing_manager().IsTabPinned(TabHandleAtIndex(0)));
   tester->WaitForRampDownStarted();
   tester->FinishRampDown();
   EXPECT_FALSE(underline->IsShowing());
 }
 
 IN_PROC_BROWSER_TEST_F(TabUnderlineViewUiTest,
-                       SingleTabPinningWhileGlicWindowClosed) {
-  EXPECT_FALSE(glic_service()->IsWindowShowing());
-
-  // While the glic window is closed, changes to pinning have no effect on the
-  // underline UI.
-  auto* underline = GetUnderlineOfActiveTab();
-  tabs::TabHandle tab_handle = TabHandleAtIndex(0);
-  PinTabs({tab_handle});
-  EXPECT_TRUE(sharing_manager().IsTabPinned(tab_handle));
-  EXPECT_FALSE(underline->IsShowing());
-
-  sharing_manager().UnpinAllTabs();
-  ASSERT_FALSE(sharing_manager().IsTabPinned(tab_handle));
-  EXPECT_FALSE(underline->IsShowing());
-}
-
-IN_PROC_BROWSER_TEST_F(TabUnderlineViewUiTest,
                        ToggleGlicWindowVisibilityWithPinnedTab) {
   auto* underline = GetUnderlineOfActiveTab();
   TesterImpl* tester = static_cast<TesterImpl*>(underline->tester());
 
-  tabs::TabHandle tab_handle = TabHandleAtIndex(0);
-  PinTabs({tab_handle});
-  EXPECT_TRUE(sharing_manager().IsTabPinned(tab_handle));
+  // tabs::TabHandle tab_handle = TabHandleAtIndex(0);
+  // PinTabs({tab_handle});
+  // EXPECT_TRUE(sharing_manager().IsTabPinned(tab_handle));
 
   // The underline of a pinned tab should show when the glic window is opened.
   RunTestSequence(OpenGlic());
@@ -440,36 +412,26 @@
 
 IN_PROC_BROWSER_TEST_F(TabUnderlineViewUiTest, FocusedTabChange) {
   auto* underline1 = GetUnderlineOfActiveTab();
-  TesterImpl* tester1 = static_cast<TesterImpl*>(underline1->tester());
 
-  // Add second tab
+  // Add second tab.
   AppendTabAndNavigate(browser(), Title2());
   auto* underline2 = GetUnderlineOfActiveTab();
-  TesterImpl* tester2 = static_cast<TesterImpl*>(underline2->tester());
 
   // The underline of the active tab should show when sharing is turned on.
+  TrackOnlyGlicInstance();
   OpenGlicWindowAndStartSharing();
-  tester2->WaitForAnimationStart();
+  GlicInstance* instance = glic_service()->GetInstanceForActiveTab(browser());
   EXPECT_TRUE(underline2->IsShowing());
-  tester2->AdvanceTimeAndTickAnimation(base::TimeDelta());
-  tester2->AdvanceTimeAndTickAnimation(base::Seconds(0.3));
 
-  // For tabs that are not pinned while sharing is turned on, the underline of a
-  // tab that loses focus should hide and the underline of a tab that gains
-  // focus should show.
   ActivateTabAt(0);
-  tester1->WaitForAnimationStart();
+  instance->BindTabForTesting(TabHandleAtIndex(0).Get());
   EXPECT_TRUE(underline1->IsShowing());
-  tester1->AdvanceTimeAndTickAnimation(base::TimeDelta());
-  tester1->AdvanceTimeAndTickAnimation(base::Seconds(0.3));
-
-  tester2->WaitForRampDownStarted();
-  tester2->FinishRampDown();
-  EXPECT_FALSE(underline2->IsShowing());
+  EXPECT_TRUE(underline2->IsShowing());
 }
 
 IN_PROC_BROWSER_TEST_F(TabUnderlineViewUiTest,
                        FocusedTabChangeBetweenPinnedTabs) {
+  // Open two tabs, and bind/pin them to the same glic instance.
   auto* underline1 = GetUnderlineOfActiveTab();
   TesterImpl* tester1 = static_cast<TesterImpl*>(underline1->tester());
 
@@ -477,15 +439,19 @@
   auto* underline2 = GetUnderlineOfActiveTab();
   TesterImpl* tester2 = static_cast<TesterImpl*>(underline2->tester());
 
-  // Pin both tabs
-  PinTabs({TabHandleAtIndex(0), TabHandleAtIndex(1)});
-  EXPECT_TRUE(sharing_manager().IsTabPinned(TabHandleAtIndex(0)));
-  EXPECT_TRUE(sharing_manager().IsTabPinned(TabHandleAtIndex(1)));
+  TrackOnlyGlicInstance();
+  OpenGlicWindowAndStartSharing();
+  GlicInstance* instance = glic_service()->GetInstanceForActiveTab(browser());
+  instance->BindTabForTesting(TabHandleAtIndex(0).Get());
+  instance->BindTabForTesting(TabHandleAtIndex(1).Get());
 
   // Underlines of all pinned tabs should show when the glic window is opened.
-  RunTestSequence(OpenGlic());
-  tester1->WaitForAnimationStart();
-  tester2->WaitForAnimationStart();
+  if (!underline1->IsShowing()) {
+    tester1->WaitForAnimationStart();
+  }
+  if (!underline2->IsShowing()) {
+    tester2->WaitForAnimationStart();
+  }
   EXPECT_TRUE(underline1->IsShowing());
   EXPECT_TRUE(underline2->IsShowing());
   // Allow animations to reach their steady states.
@@ -526,19 +492,13 @@
 IN_PROC_BROWSER_TEST_F(TabUnderlineViewUiTest,
                        TabAlertIndicatorHidden_PinnedTab) {
   RunTestSequence(OpenGlic());
-  EXPECT_TRUE(glic_service()->IsWindowShowing());
   auto* underline = GetUnderlineOfActiveTab();
   TesterImpl* tester = static_cast<TesterImpl*>(underline->tester());
-  EXPECT_FALSE(underline->IsShowing());
-
-  tabs::TabHandle tab_handle = TabHandleAtIndex(0);
-  PinTabs({tab_handle});
-  ASSERT_TRUE(sharing_manager().IsTabPinned(tab_handle));
-  tester->WaitForAnimationStart();
   EXPECT_TRUE(underline->IsShowing());
 
   // The pinned tab should not have a visible tab alert indicator.
   EXPECT_FALSE(GetAlertIndicatorButtonOfActiveTab()->GetVisible());
+  tester->FinishRampDown();
 }
 
 // Ensure basic incognito window doesn't cause a crash. Simply opens an
@@ -550,78 +510,7 @@
       ui_test_utils::NavigateToURL(incognito_browser, GURL("about:blank")));
 }
 
-namespace {
-class TabUnderlineViewFeatureDisabledBrowserTest
-    : public TabUnderlineViewUiTest {
- public:
-  TabUnderlineViewFeatureDisabledBrowserTest() {
-    features_.InitAndDisableFeature(features::kGlicMultitabUnderlines);
-  }
-  ~TabUnderlineViewFeatureDisabledBrowserTest() override = default;
-
- private:
-  base::test::ScopedFeatureList features_;
-};
-}  // namespace
-
-IN_PROC_BROWSER_TEST_F(TabUnderlineViewFeatureDisabledBrowserTest,
-                       TabAlertIndicatorShown) {
-  AlertIndicatorButton* alert_button = GetAlertIndicatorButtonOfActiveTab();
-  EXPECT_FALSE(alert_button->GetVisible());
-
-  base::RunLoop wait_for_alert_loop;
-  auto callback_subscription = alert_button->AddVisibleChangedCallback(
-      wait_for_alert_loop.QuitClosure());
-
-  OpenGlicWindowAndStartSharing();
-  tabs::TabInterface* tab = browser()->tab_strip_model()->GetTabAtIndex(0);
-  ASSERT_EQ(tab, sharing_manager().GetFocusedTabData().focus());
-
-  // Wait for the view's visibility change to trigger.
-  wait_for_alert_loop.Run();
-
-  // The shared tab should have a visible tab alert indicator.
-  EXPECT_TRUE(alert_button->GetVisible());
-}
-
-IN_PROC_BROWSER_TEST_F(TabUnderlineViewFeatureDisabledBrowserTest,
-                       TabAlertIndicatorShown_PinnedTab) {
-  AlertIndicatorButton* alert_button = GetAlertIndicatorButtonOfActiveTab();
-  EXPECT_FALSE(alert_button->GetVisible());
-
-  RunTestSequence(OpenGlic());
-  EXPECT_TRUE(glic_service()->IsWindowShowing());
-
-  base::RunLoop wait_for_alert_loop;
-  auto callback_subscription = alert_button->AddVisibleChangedCallback(
-      wait_for_alert_loop.QuitClosure());
-
-  tabs::TabHandle tab_handle = TabHandleAtIndex(0);
-  PinTabs({tab_handle});
-  ASSERT_TRUE(sharing_manager().IsTabPinned(tab_handle));
-
-  // Wait for the view's visibility change to trigger.
-  wait_for_alert_loop.Run();
-
-  // The pinned tab should have a visible tab alert indicator.
-  EXPECT_TRUE(GetAlertIndicatorButtonOfActiveTab()->GetVisible());
-}
-
-class TabUnderlineViewMultiInstanceUiTest : public TabUnderlineViewUiTest {
- public:
-  TabUnderlineViewMultiInstanceUiTest() {
-    // kGlicMultiInstance, kGlicMultiTab, kGlicMultitabUnderlines are required
-    // for IsMultiInstanceEnabled().
-    scoped_feature_list_.InitWithFeatures({features::kGlic}, {});
-  }
-  ~TabUnderlineViewMultiInstanceUiTest() override = default;
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(TabUnderlineViewMultiInstanceUiTest,
-                       AttachPinnedTabToNewWindow) {
+IN_PROC_BROWSER_TEST_F(TabUnderlineViewUiTest, AttachPinnedTabToNewWindow) {
   // Set up two windows, each with one tab
   ASSERT_EQ(browser()->tab_strip_model()->count(), 1);
   // Second browser window; this will be active.
diff --git a/chrome/browser/glic/glic_metrics.h b/chrome/browser/glic/glic_metrics.h
index 6b848872..02aedec 100644
--- a/chrome/browser/glic/glic_metrics.h
+++ b/chrome/browser/glic/glic_metrics.h
@@ -221,7 +221,11 @@
   kIphAttachedAudio = 86,
   kIphDetachedText = 87,
   kIphDetachedAudio = 88,
-  kMaxValue = kIphDetachedAudio,
+  kAnchoredContextualCueAttachedText = 89,
+  kAnchoredContextualCueAttachedAudio = 90,
+  kAnchoredContextualCueDetachedText = 91,
+  kAnchoredContextualCueDetachedAudio = 92,
+  kMaxValue = kAnchoredContextualCueDetachedAudio,
 };
 // LINT.ThenChange(//tools/metrics/histograms/metadata/glic/enums.xml:GlicResponseSegmentation)
 
diff --git a/chrome/browser/glic/host/glic.mojom b/chrome/browser/glic/host/glic.mojom
index 52dd825..cc70ced 100644
--- a/chrome/browser/glic/host/glic.mojom
+++ b/chrome/browser/glic/host/glic.mojom
@@ -1523,6 +1523,8 @@
   [MinVersion=12] kCaptureRegionHotkey = 20,
   // From the in-product-help (IPH) entrypoint.
   [MinVersion=13] kIph = 21,
+  // User clicked an anchored contextual cue chip.
+  [MinVersion=14] kAnchoredContextualCue = 22,
 };
 
 // LINT.ThenChange(//tools/metrics/histograms/metadata/glic/enums.xml:GlicInvocationSource, //chrome/browser/glic/glic_metrics.h:ResponseSegmentation)
diff --git a/chrome/browser/glic/public/glic_enabling_browsertest.cc b/chrome/browser/glic/public/glic_enabling_browsertest.cc
index 3da7f89..0f8f97ac5 100644
--- a/chrome/browser/glic/public/glic_enabling_browsertest.cc
+++ b/chrome/browser/glic/public/glic_enabling_browsertest.cc
@@ -401,156 +401,5 @@
   }
 }
 
-// Test fixtures for testing-related flags for multi-instance enablement by
-// tier.
-class GlicMultiInstanceEnablingTestingFlagsBrowserTest
-    : public GlicEnablingTest {
- public:
-  void InitializeFeatureList() override {
-    scoped_feature_list_.InitWithFeatures(
-        {
-            features::kGlic,
-            features::kGlicEnableMultiInstanceBasedOnTier,
-#if BUILDFLAG(IS_CHROMEOS)
-            chromeos::features::kFeatureManagementGlic,
-#endif  // BUILDFLAG(IS_CHROMEOS)
-        },
-        {features::kGlicMultiInstance});
-  }
-  ~GlicMultiInstanceEnablingTestingFlagsBrowserTest() override = default;
-
- protected:
-  // Helper to set the AI subscription tier for the profile.
-  void SetAiSubscriptionTier(int tier) {
-    profile()->GetPrefs()->SetInteger(
-        subscription_eligibility::prefs::kAiSubscriptionTier, tier);
-  }
-
-  // Helper to set the kGlicMultiInstanceEnabledBySubscriptionTier pref.
-  void SetGlicMultiInstanceEnabledBySubscriptionTierPref(bool value) {
-    g_browser_process->local_state()->SetBoolean(
-        glic::prefs::kGlicMultiInstanceEnabledBySubscriptionTier, value);
-  }
-
-  // Helper to get the kGlicMultiInstanceEnabledBySubscriptionTier pref.
-  bool GetGlicMultiInstanceEnabledBySubscriptionTierPref() {
-    return g_browser_process->local_state()->GetBoolean(
-        glic::prefs::kGlicMultiInstanceEnabledBySubscriptionTier);
-  }
-};
-
-class GlicMultiInstanceEnablingNoFlagsBrowserTest
-    : public GlicMultiInstanceEnablingTestingFlagsBrowserTest,
-      public testing::WithParamInterface<std::tuple<bool, bool>> {
- public:
-  bool IsG1() const { return std::get<0>(GetParam()); }
-  bool WasPreviouslyEligible() const { return std::get<1>(GetParam()); }
-};
-
-IN_PROC_BROWSER_TEST_P(GlicMultiInstanceEnablingNoFlagsBrowserTest,
-                       NoFlagsTest) {
-  SetAiSubscriptionTier(IsG1() ? 1 : 0);
-  SetGlicMultiInstanceEnabledBySubscriptionTierPref(WasPreviouslyEligible());
-
-  bool expected_eligibility = IsG1() || WasPreviouslyEligible();
-  EXPECT_EQ(
-      GlicEnabling::GetAndUpdateEligibilityForGlicMultiInstanceTieredRollout(
-          profile()),
-      expected_eligibility);
-}
-
-INSTANTIATE_TEST_SUITE_P(All,
-                         GlicMultiInstanceEnablingNoFlagsBrowserTest,
-                         testing::Combine(testing::Bool(), testing::Bool()));
-
-class GlicMultiInstanceEnablingForceG1ForMiBrowserTest
-    : public GlicMultiInstanceEnablingTestingFlagsBrowserTest,
-      public testing::WithParamInterface<bool> {
- public:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    GlicMultiInstanceEnablingTestingFlagsBrowserTest::SetUpCommandLine(
-        command_line);
-    command_line->AppendSwitchASCII(
-        switches::kGlicForceG1StatusForMultiInstance,
-        GetParam() ? "true" : "false");
-  }
-};
-
-IN_PROC_BROWSER_TEST_P(GlicMultiInstanceEnablingForceG1ForMiBrowserTest,
-                       ForceG1ForMi) {
-  bool force_g1_for_mi = GetParam();
-  // Set the actual subscription tier to the opposite status of the test param,
-  // to ensure that it is overridden by the command line switch in effect.
-  SetAiSubscriptionTier(force_g1_for_mi ? 0 : 1);
-  EXPECT_EQ(
-      GlicEnabling::GetAndUpdateEligibilityForGlicMultiInstanceTieredRollout(
-          profile()),
-      force_g1_for_mi);
-}
-
-INSTANTIATE_TEST_SUITE_P(All,
-                         GlicMultiInstanceEnablingForceG1ForMiBrowserTest,
-                         testing::Bool());
-
-class GlicMultiInstanceEnablingResetPrefBrowserTest
-    : public GlicMultiInstanceEnablingTestingFlagsBrowserTest,
-      public testing::WithParamInterface<bool> {
- public:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    GlicMultiInstanceEnablingTestingFlagsBrowserTest::SetUpCommandLine(
-        command_line);
-    command_line->AppendSwitch(switches::kGlicResetMultiInstanceEnabledByTier);
-  }
-};
-
-IN_PROC_BROWSER_TEST_P(GlicMultiInstanceEnablingResetPrefBrowserTest,
-                       ResetMiEnabledByTierPref) {
-  bool is_eligible_by_tier = GetParam();
-  SetAiSubscriptionTier(is_eligible_by_tier ? 1 : 0);
-  SetGlicMultiInstanceEnabledBySubscriptionTierPref(/*value=*/true);
-
-  EXPECT_EQ(
-      GlicEnabling::GetAndUpdateEligibilityForGlicMultiInstanceTieredRollout(
-          profile()),
-      is_eligible_by_tier);
-  // The pref should be reset to false if the user is not eligible by tier.
-  EXPECT_EQ(GetGlicMultiInstanceEnabledBySubscriptionTierPref(),
-            is_eligible_by_tier);
-}
-
-INSTANTIATE_TEST_SUITE_P(All,
-                         GlicMultiInstanceEnablingResetPrefBrowserTest,
-                         testing::Bool());
-
-class GlicEnablingMultiInstanceFlagPrecedenceBrowserTest
-    : public GlicMultiInstanceEnablingTestingFlagsBrowserTest,
-      public testing::WithParamInterface<bool> {
- public:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    GlicMultiInstanceEnablingTestingFlagsBrowserTest::SetUpCommandLine(
-        command_line);
-    command_line->AppendSwitch(switches::kGlicResetMultiInstanceEnabledByTier);
-    command_line->AppendSwitchASCII(
-        switches::kGlicForceG1StatusForMultiInstance,
-        GetParam() ? "true" : "false");
-  }
-};
-
-IN_PROC_BROWSER_TEST_P(GlicEnablingMultiInstanceFlagPrecedenceBrowserTest,
-                       FlagPrecedence) {
-  bool force_g1_for_mi = GetParam();
-  SetAiSubscriptionTier(force_g1_for_mi ? 0 : 1);
-  SetGlicMultiInstanceEnabledBySubscriptionTierPref(/*value=*/true);
-
-  EXPECT_EQ(
-      GlicEnabling::GetAndUpdateEligibilityForGlicMultiInstanceTieredRollout(
-          profile()),
-      force_g1_for_mi);
-}
-
-INSTANTIATE_TEST_SUITE_P(All,
-                         GlicEnablingMultiInstanceFlagPrecedenceBrowserTest,
-                         testing::Bool());
-
 }  // namespace
 }  // namespace glic
diff --git a/chrome/browser/glic/selection/selection_overlay_untrusted_ui.cc b/chrome/browser/glic/selection/selection_overlay_untrusted_ui.cc
index e1025cf..4f84be7 100644
--- a/chrome/browser/glic/selection/selection_overlay_untrusted_ui.cc
+++ b/chrome/browser/glic/selection/selection_overlay_untrusted_ui.cc
@@ -75,6 +75,7 @@
   html_source->AddLocalizedString(
       "bottomLeftSliderAriaLabel",
       IDS_LENS_OVERLAY_BOTTOM_LEFT_CORNER_SLIDER_ACCESSIBILITY_LABEL);
+  html_source->AddBoolean("enableMultiRegionSelection", true);
 
   // TODO(b/489801993): Refactor shared resources into a common directory to
   // avoid manual path concatenation for Lens and the Glic selection overlay.
diff --git a/chrome/browser/glic/test_support/glic_browser_test.h b/chrome/browser/glic/test_support/glic_browser_test.h
index 7b96df2..302e406 100644
--- a/chrome/browser/glic/test_support/glic_browser_test.h
+++ b/chrome/browser/glic/test_support/glic_browser_test.h
@@ -38,12 +38,8 @@
 #endif
 
 #if defined(TOOLKIT_VIEWS)
-#include "ui/views/buildflags.h"
-
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
 #include "ui/views/test/mock_activation_controller.h"
 #endif
-#endif
 
 namespace glic {
 
@@ -98,12 +94,10 @@
 
   void SetUpOnMainThread() override {
     T::SetUpOnMainThread();
-#if defined(TOOLKIT_VIEWS)
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
+#if defined(USE_MOCK_ACTIVATION_CONTROLLER)
     activation_controller_ =
         std::make_unique<views::test::MockActivationController>();
 #endif
-#endif
 
     CHECK(glic_test_environment_.SetupEmbeddedTestServers(
         T::embedded_test_server(), &T::embedded_https_test_server()));
@@ -111,11 +105,9 @@
   }
 
   void TearDownOnMainThread() override {
-#if defined(TOOLKIT_VIEWS)
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
+#if defined(USE_MOCK_ACTIVATION_CONTROLLER)
     activation_controller_.reset();
 #endif
-#endif
     T::TearDownOnMainThread();
   }
 
@@ -264,11 +256,9 @@
  private:
   GlicTestEnvironment glic_test_environment_;
   base::test::ScopedFeatureList scoped_feature_list_;
-#if defined(TOOLKIT_VIEWS)
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
+#if defined(USE_MOCK_ACTIVATION_CONTROLLER)
   std::unique_ptr<views::test::MockActivationController> activation_controller_;
 #endif
-#endif
 };
 
 using GlicBrowserTest = GlicBrowserTestMixin<PlatformBrowserTest>;
diff --git a/chrome/browser/glic/test_support/non_interactive_glic_test.cc b/chrome/browser/glic/test_support/non_interactive_glic_test.cc
index 5bc2d19..a9af02a3 100644
--- a/chrome/browser/glic/test_support/non_interactive_glic_test.cc
+++ b/chrome/browser/glic/test_support/non_interactive_glic_test.cc
@@ -32,20 +32,16 @@
 
 void NonInteractiveGlicTest::SetUpOnMainThread() {
   test::InteractiveGlicTestMixin<InteractiveBrowserTest>::SetUpOnMainThread();
-#if defined(TOOLKIT_VIEWS)
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
+#if defined(USE_MOCK_ACTIVATION_CONTROLLER)
   activation_controller_ =
       std::make_unique<views::test::MockActivationController>();
 #endif
-#endif
 }
 
 void NonInteractiveGlicTest::TearDownOnMainThread() {
-#if defined(TOOLKIT_VIEWS)
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
+#if defined(USE_MOCK_ACTIVATION_CONTROLLER)
   activation_controller_.reset();
 #endif
-#endif
   test::InteractiveGlicTestMixin<
       InteractiveBrowserTest>::TearDownOnMainThread();
 }
diff --git a/chrome/browser/glic/test_support/non_interactive_glic_test.h b/chrome/browser/glic/test_support/non_interactive_glic_test.h
index ccbdd063..0b6235fe 100644
--- a/chrome/browser/glic/test_support/non_interactive_glic_test.h
+++ b/chrome/browser/glic/test_support/non_interactive_glic_test.h
@@ -12,12 +12,8 @@
 #include "chrome/browser/glic/test_support/interactive_glic_test.h"
 
 #if defined(TOOLKIT_VIEWS)
-#include "ui/views/buildflags.h"
-
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
 #include "ui/views/test/mock_activation_controller.h"
 #endif
-#endif
 
 namespace glic {
 
@@ -40,11 +36,9 @@
 
  private:
   base::test::ScopedFeatureList features_;
-#if defined(TOOLKIT_VIEWS)
-#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
+#if defined(USE_MOCK_ACTIVATION_CONTROLLER)
   std::unique_ptr<views::test::MockActivationController> activation_controller_;
 #endif
-#endif
 };
 
 }  // namespace glic
diff --git a/chrome/browser/indigo/BUILD.gn b/chrome/browser/indigo/BUILD.gn
index a17cab7a..e1c7417 100644
--- a/chrome/browser/indigo/BUILD.gn
+++ b/chrome/browser/indigo/BUILD.gn
@@ -36,6 +36,7 @@
   deps = [
     ":indigo",
     "//base",
+    "//chrome/browser/indigo/onboarding",
     "//chrome/browser/optimization_guide",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/signin",
diff --git a/chrome/browser/indigo/indigo_page_action_controller.cc b/chrome/browser/indigo/indigo_page_action_controller.cc
index 0abed2e8..de3906e 100644
--- a/chrome/browser/indigo/indigo_page_action_controller.cc
+++ b/chrome/browser/indigo/indigo_page_action_controller.cc
@@ -14,6 +14,7 @@
 #include "base/notimplemented.h"
 #include "chrome/browser/indigo/indigo_agent_host.h"
 #include "chrome/browser/indigo/indigo_alpha_rpc.h"
+#include "chrome/browser/indigo/onboarding/indigo_onboarding_dialog.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -35,6 +36,7 @@
 
 namespace {
 const char kForceIndigoSwitch[] = "force-indigo";
+const char kForceIndigoOnboardingSwitch[] = "force-indigo-onboarding";
 }  // namespace
 
 DEFINE_USER_DATA(IndigoPageActionController);
@@ -84,6 +86,21 @@
     return;
   }
 
+  // For now, onboarding is only triggered when forced, and the URL is specified
+  // in the command line switch. In the future, this will typically be triggered
+  // automatically based on the user's enrolment status, and the URL will be
+  // determined by a feature param.
+  std::string onboarding_url =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          kForceIndigoOnboardingSwitch);
+  if (!onboarding_url.empty()) {
+    onboarding_dialog_ = IndigoOnboardingDialog::Show(
+        tab(), GURL(onboarding_url),
+        base::BindOnce(&IndigoPageActionController::OnOnboardingDialogClosed,
+                       weak_ptr_factory_.GetWeakPtr()));
+    return;
+  }
+
   if (IndigoAgentHost::GetOrCreateForPage(web_contents->GetPrimaryPage())
           ->Invoke()) {
     return;
@@ -178,6 +195,10 @@
   is_shown_ = should_show;
 }
 
+void IndigoPageActionController::OnOnboardingDialogClosed() {
+  onboarding_dialog_.reset();
+}
+
 void IndigoPageActionController::OnOptimizationGuideDecision(
     const GURL& url,
     optimization_guide::OptimizationGuideDecision decision,
diff --git a/chrome/browser/indigo/indigo_page_action_controller.h b/chrome/browser/indigo/indigo_page_action_controller.h
index fc19a19..3c0b860 100644
--- a/chrome/browser/indigo/indigo_page_action_controller.h
+++ b/chrome/browser/indigo/indigo_page_action_controller.h
@@ -31,6 +31,8 @@
 
 namespace indigo {
 
+class IndigoOnboardingDialog;
+
 // Manages the Indigo page action and its various entry points, ensuring they
 // are correctly displayed.
 class IndigoPageActionController : public tabs::ContentsObservingTabFeature,
@@ -62,6 +64,9 @@
   // Updates the visibility and states of all entry points.
   void UpdateEntryPointsState();
 
+  // Called when the onboarding dialog is closed.
+  void OnOnboardingDialogClosed();
+
   // Called when optimization guide has decided whether this feature should be
   // enabled for the page.
   void OnOptimizationGuideDecision(
@@ -93,6 +98,9 @@
   // If true, the Indigo page action is currently shown.
   bool is_shown_ = false;
 
+  // The onboarding dialog, if shown.
+  std::unique_ptr<IndigoOnboardingDialog> onboarding_dialog_;
+
   base::ScopedObservation<signin::IdentityManager,
                           signin::IdentityManager::Observer>
       identity_manager_observation_{this};
diff --git a/chrome/browser/indigo/onboarding/BUILD.gn b/chrome/browser/indigo/onboarding/BUILD.gn
new file mode 100644
index 0000000..cf9e2d48
--- /dev/null
+++ b/chrome/browser/indigo/onboarding/BUILD.gn
@@ -0,0 +1,47 @@
+# Copyright 2026 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("onboarding") {
+  sources = [
+    "indigo_onboarding_dialog.cc",
+    "indigo_onboarding_dialog.h",
+  ]
+
+  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
+  deps = [
+    "//base",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui/tabs:tabs_public",
+    "//components/tabs:public",
+    "//content/public/browser",
+    "//ui/base",
+    "//ui/gfx/geometry",
+    "//ui/views",
+    "//ui/views/controls/webview",
+    "//url",
+  ]
+}
+
+if (!is_android) {
+  source_set("browser_tests") {
+    testonly = true
+    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+    sources = [ "indigo_onboarding_dialog_browsertest.cc" ]
+    deps = [
+      ":onboarding",
+      "//chrome/browser",
+      "//chrome/browser/ui/tabs:tabs_public",
+      "//chrome/test:test_support",
+      "//chrome/test:test_support_ui",
+      "//components/tabs:public",
+      "//content/public/browser",
+      "//content/test:test_support",
+      "//testing/gtest",
+      "//ui/base/interaction",
+      "//ui/views",
+      "//ui/views/controls/webview",
+      "//url",
+    ]
+  }
+}
diff --git a/chrome/browser/indigo/onboarding/indigo_onboarding_dialog.cc b/chrome/browser/indigo/onboarding/indigo_onboarding_dialog.cc
new file mode 100644
index 0000000..3a2acf3
--- /dev/null
+++ b/chrome/browser/indigo/onboarding/indigo_onboarding_dialog.cc
@@ -0,0 +1,122 @@
+// Copyright 2026 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/indigo/onboarding/indigo_onboarding_dialog.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/functional/bind.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/file_select_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/tabs/public/tab_dialog_manager.h"
+#include "chrome/browser/ui/tabs/public/tab_features.h"
+#include "components/tabs/public/tab_interface.h"
+#include "content/public/browser/file_select_listener.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/mojom/dialog_button.mojom.h"
+#include "ui/base/ui_base_types.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h"
+#include "ui/views/controls/webview/webview.h"
+#include "ui/views/view_class_properties.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/window/dialog_delegate.h"
+#include "url/gurl.h"
+
+namespace indigo {
+
+namespace {
+
+class OnboardingWebView : public views::WebView {
+ public:
+  using WebView::WebView;
+
+  // WebContentsDelegate:
+
+  void RunFileChooser(content::RenderFrameHost* render_frame_host,
+                      scoped_refptr<content::FileSelectListener> listener,
+                      const blink::mojom::FileChooserParams& params) override {
+    FileSelectHelper::RunFileChooser(render_frame_host, std::move(listener),
+                                     params);
+  }
+
+  bool HandleKeyboardEvent(
+      content::WebContents* web_contents,
+      const input::NativeWebKeyboardEvent& event) override {
+    return unhandled_keyboard_event_handler_.HandleKeyboardEvent(
+        event, GetFocusManager());
+  }
+
+ private:
+  views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_;
+};
+
+}  // namespace
+
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(IndigoOnboardingDialog, kWebViewId);
+
+// static
+std::unique_ptr<IndigoOnboardingDialog> IndigoOnboardingDialog::Show(
+    tabs::TabInterface& tab,
+    const GURL& onboarding_url,
+    base::OnceClosure close_callback) {
+  if (!tab.CanShowModalUI()) {
+    return nullptr;
+  }
+  return base::WrapUnique(new IndigoOnboardingDialog(
+      tab, onboarding_url, std::move(close_callback)));
+}
+
+IndigoOnboardingDialog::IndigoOnboardingDialog(tabs::TabInterface& tab,
+                                               const GURL& onboarding_url,
+                                               base::OnceClosure close_callback)
+    : close_callback_(std::move(close_callback)) {
+  Profile* profile =
+      Profile::FromBrowserContext(tab.GetContents()->GetBrowserContext());
+  auto web_view = std::make_unique<OnboardingWebView>(profile);
+  web_view->GetWebContents()->GetController().LoadURLWithParams(
+      content::NavigationController::LoadURLParams(onboarding_url));
+  web_view->SetPreferredSize(gfx::Size(800, 600));
+  web_view->SetProperty(views::kElementIdentifierKey, kWebViewId);
+
+  delegate_ = std::make_unique<views::DialogDelegate>();
+  delegate_->SetContentsView(std::move(web_view));
+  delegate_->SetButtons(static_cast<int>(ui::mojom::DialogButton::kNone));
+  delegate_->SetShowCloseButton(true);
+  delegate_->SetModalType(ui::mojom::ModalType::kChild);
+  delegate_->SetOwnershipOfNewWidget(
+      views::Widget::InitParams::CLIENT_OWNS_WIDGET);
+
+  auto* tab_dialog_manager = tab.GetTabFeatures()->tab_dialog_manager();
+  widget_ = tab_dialog_manager->CreateTabScopedDialog(delegate_.get());
+  widget_->MakeCloseSynchronous(base::BindOnce(
+      &IndigoOnboardingDialog::OnWidgetClosed, base::Unretained(this)));
+
+  auto params = std::make_unique<tabs::TabDialogManager::Params>();
+  tab_dialog_manager->ShowDialog(widget_.get(), std::move(params));
+}
+
+IndigoOnboardingDialog::~IndigoOnboardingDialog() = default;
+
+void IndigoOnboardingDialog::Close() {
+  if (widget_) {
+    widget_->Close();
+  }
+}
+
+void IndigoOnboardingDialog::OnWidgetClosed(
+    views::Widget::ClosedReason reason) {
+  // As recommended in the comment on `views::Widget::MakeCloseSynchronous`,
+  // destroy the widget here.
+  widget_.reset();
+
+  if (close_callback_) {
+    std::move(close_callback_).Run();
+  }
+}
+
+}  // namespace indigo
diff --git a/chrome/browser/indigo/onboarding/indigo_onboarding_dialog.h b/chrome/browser/indigo/onboarding/indigo_onboarding_dialog.h
new file mode 100644
index 0000000..8354ad60
--- /dev/null
+++ b/chrome/browser/indigo/onboarding/indigo_onboarding_dialog.h
@@ -0,0 +1,66 @@
+// Copyright 2026 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_INDIGO_ONBOARDING_INDIGO_ONBOARDING_DIALOG_H_
+#define CHROME_BROWSER_INDIGO_ONBOARDING_INDIGO_ONBOARDING_DIALOG_H_
+
+#include <memory>
+
+#include "base/functional/callback_forward.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/views/widget/widget.h"
+
+class GURL;
+
+namespace tabs {
+class TabInterface;
+}
+
+namespace views {
+class DialogDelegate;
+}
+
+namespace indigo {
+
+// Owns and manages an onboarding dialog which is mainly powered by a WebView.
+class IndigoOnboardingDialog {
+ public:
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kWebViewId);
+
+  // Creates and shows a tab-modal dialog embedding a WebView pointing to an
+  // external onboarding URL. Returns a new `IndigoOnboardingDialog`, or nullptr
+  // if the tab cannot show modal UI (e.g., another modal UI is already shown).
+  // `close_callback` is run when the dialog is closed.
+  static std::unique_ptr<IndigoOnboardingDialog> Show(
+      tabs::TabInterface& tab,
+      const GURL& onboarding_url,
+      base::OnceClosure close_callback);
+
+  IndigoOnboardingDialog(const IndigoOnboardingDialog&) = delete;
+  IndigoOnboardingDialog& operator=(const IndigoOnboardingDialog&) = delete;
+
+  ~IndigoOnboardingDialog();
+
+  // Closes the dialog immediately. This will call the `close_callback` passed
+  // to `Show`, likely resulting in the destruction of this object.
+  void Close();
+
+ private:
+  explicit IndigoOnboardingDialog(tabs::TabInterface& tab,
+                                  const GURL& onboarding_url,
+                                  base::OnceClosure close_callback);
+
+  void OnWidgetClosed(views::Widget::ClosedReason reason);
+
+  base::OnceClosure close_callback_;
+
+  // `widget_` must be destroyed before `delegate_` because the widget holds a
+  // raw pointer to the delegate.
+  std::unique_ptr<views::DialogDelegate> delegate_;
+  std::unique_ptr<views::Widget> widget_;
+};
+
+}  // namespace indigo
+
+#endif  // CHROME_BROWSER_INDIGO_ONBOARDING_INDIGO_ONBOARDING_DIALOG_H_
diff --git a/chrome/browser/indigo/onboarding/indigo_onboarding_dialog_browsertest.cc b/chrome/browser/indigo/onboarding/indigo_onboarding_dialog_browsertest.cc
new file mode 100644
index 0000000..8d8406c
--- /dev/null
+++ b/chrome/browser/indigo/onboarding/indigo_onboarding_dialog_browsertest.cc
@@ -0,0 +1,59 @@
+// Copyright 2026 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/indigo/onboarding/indigo_onboarding_dialog.h"
+
+#include "base/functional/callback_helpers.h"
+#include "base/test/bind.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/public/tab_dialog_manager.h"
+#include "chrome/browser/ui/tabs/public/tab_features.h"
+#include "chrome/test/interaction/interactive_browser_test.h"
+#include "components/tabs/public/tab_interface.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/controls/webview/webview.h"
+#include "ui/views/view_utils.h"
+#include "ui/views/widget/widget.h"
+#include "url/gurl.h"
+
+namespace indigo {
+namespace {
+
+class IndigoOnboardingDialogBrowserTest : public InteractiveBrowserTest {
+ protected:
+  void OpenDialog(tabs::TabInterface& tab, const GURL& url) {
+    dialog_ = IndigoOnboardingDialog::Show(tab, url,
+                                           base::BindLambdaForTesting([&]() {
+                                             closed_ = true;
+                                             dialog_.reset();
+                                           }));
+  }
+
+  bool WasDialogClosed() const { return closed_; }
+
+  std::unique_ptr<IndigoOnboardingDialog> dialog_;
+  bool closed_ = false;
+};
+
+IN_PROC_BROWSER_TEST_F(IndigoOnboardingDialogBrowserTest, ShowAndClose) {
+  tabs::TabInterface* tab = browser()->GetActiveTabInterface();
+  ASSERT_TRUE(tab);
+
+  const GURL example_url("https://www.example.com/");
+  RunTestSequence(
+      Do([&]() { OpenDialog(*tab, example_url); }),
+      WaitForShow(IndigoOnboardingDialog::kWebViewId),
+      CheckView(
+          IndigoOnboardingDialog::kWebViewId,
+          [](views::WebView* web_view) { return web_view->GetPreferredSize(); },
+          gfx::Size(800, 600)),
+      Do([&]() { dialog_->Close(); }),
+      WaitForHide(IndigoOnboardingDialog::kWebViewId),
+      Check([&]() { return WasDialogClosed(); }));
+}
+
+}  // namespace
+}  // namespace indigo
diff --git a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinator.java b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinator.java
index 5484195..1979420 100644
--- a/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinator.java
+++ b/chrome/browser/ntp_customization/java/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinator.java
@@ -184,6 +184,14 @@
         // We shouldn't use mTrackingTab, which can be set to null in removeObservers(), and it
         // won't be updated if mTabSupplierObserver is removed.
         Tab currentTab = mTabSupplier.get();
+        if (currentTab == null
+                && mLayoutStateProvider != null
+                && mLayoutStateProvider.getActiveLayoutType() == LayoutType.TAB_SWITCHER) {
+            // We don't update toolbar's top padding on Tab switcher until tab switches. Thus, we
+            // should keep mConsumeTopInset reflect whether the top inset is consumed on the last
+            // Tab. See https://crbug.com/491888405.
+            return windowInsetsCompat;
+        }
 
         mSystemInsets = windowInsetsCompat.getInsets(WindowInsetsCompat.Type.systemBars());
 
diff --git a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinatorUnitTest.java b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinatorUnitTest.java
index f25e8d4..f8a55e9 100644
--- a/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinatorUnitTest.java
+++ b/chrome/browser/ntp_customization/junit/src/org/chromium/chrome/browser/ntp_customization/edge_to_edge/TopInsetCoordinatorUnitTest.java
@@ -178,6 +178,21 @@
     }
 
     @Test
+    public void testOnApplyWindowInsets_TabSwitcher_ReturnEarly() {
+        // Tab switcher is showing.
+        mTabSupplier.set(null);
+        mLayoutStateProviderSupplier.set(mLayoutStateProvider);
+        setBackgroundType(NtpBackgroundType.DEFAULT, NtpBackgroundType.CHROME_COLOR);
+        when(mLayoutStateProvider.getActiveLayoutType()).thenReturn(LayoutType.TAB_SWITCHER);
+        clearInvocations(mObserver);
+
+        mTopInsetCoordinator.onApplyWindowInsets(mView, mWindowInsetsCompat);
+
+        // Verify that notifyObservers() is NOT called.
+        verify(mObserver, never()).onToEdgeChange(any(Integer.class), any(Boolean.class), anyInt());
+    }
+
+    @Test
     public void testOnTabSwitched_RetriggerOnApplyWindowInsets() {
         // Verifies that retriggerOnApplyWindowInsets() is called if the new tab is a NTP.
         setCurrentTab(mNtpTab);
diff --git a/chrome/browser/optimization_guide/model_execution/model_execution_browsertest.cc b/chrome/browser/optimization_guide/model_execution/model_execution_browsertest.cc
index f70af80..e4b2789c 100644
--- a/chrome/browser/optimization_guide/model_execution/model_execution_browsertest.cc
+++ b/chrome/browser/optimization_guide/model_execution/model_execution_browsertest.cc
@@ -716,7 +716,7 @@
 
   // Set up assets which are registered per-profile.
   void SetUpProfileAssets() {
-    compose_asset_.SendTo(broker_state()->service_controller());
+    compose_asset_.SendTo(broker_state()->base_model_controller());
   }
 
  private:
diff --git a/chrome/browser/page_info/BUILD.gn b/chrome/browser/page_info/BUILD.gn
index 6bef4be..9c5ac5c 100644
--- a/chrome/browser/page_info/BUILD.gn
+++ b/chrome/browser/page_info/BUILD.gn
@@ -11,21 +11,88 @@
 }
 
 source_set("page_info") {
-  public = [ "page_info_features.h" ]
-
+  sources = [
+    "about_this_site_service_factory.h",
+    "about_this_site_tab_helper.h",
+    "merchant_trust_service_delegate.h",
+    "merchant_trust_service_factory.h",
+    "page_info_features.h",
+    "privacy_policy_insights_service_factory.h",
+  ]
   public_deps = [
     "//base",
     "//chrome/browser:browser_process",
+    "//chrome/browser/profiles:profile",
+    "//components/optimization_guide/core",
+    "//components/page_info/core",
+    "//components/page_info/core:proto",
+    "//content/public/browser",
   ]
+
+  if (!is_android) {
+    sources += [ "web_view_side_panel_throttle.h" ]
+  }
 }
 
+# The reason `impl` is needed is because
+# about_this_site_controller_android.cc includes
+# c/b/android/android_theme_resources.h and c/b/android/resource_mapper.h,
+# both part of //c/b:b, which causes a circular dependency against it.
+#
+# TODO(crbug.com/353332589): Merge `impl` into `page_info` when they get
+# componentized.
 source_set("impl") {
-  sources = [ "page_info_features.cc" ]
+  sources = [
+    "about_this_site_service_factory.cc",
+    "about_this_site_tab_helper.cc",
+    "merchant_trust_service_delegate.cc",
+    "merchant_trust_service_factory.cc",
+    "page_info_features.cc",
+    "privacy_policy_insights_service_factory.cc",
+  ]
 
   deps = [
     ":page_info",
+    "//base",
+    "//chrome/browser:browser_process",
+    "//chrome/browser/engagement",
+    "//chrome/browser/optimization_guide",
+    "//chrome/browser/profiles:profile",
+    "//chrome/browser/search_engines",
     "//chrome/browser/ui:ui_features",
+    "//chrome/browser/ui/hats",
+    "//chrome/browser/ui/page_info",
     "//components/page_info/core",
     "//components/variations/service",
   ]
+
+  if (is_android) {
+    sources += [ "about_this_site_controller_android.cc" ]
+    deps += [ "//chrome/android:jni_headers" ]
+  } else {
+    sources += [ "web_view_side_panel_throttle.cc" ]
+    deps += [ "//components/navigation_interception" ]
+  }
+
+  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = []
+  deps = []
+
+  if (!is_android) {
+    sources += [ "merchant_trust_service_delegate_unittest.cc" ]
+    deps += [
+      ":page_info",
+      "//base/test:test_support",
+      "//chrome/test:test_support",
+      "//components/page_info/core",
+      "//components/site_engagement/content",
+      "//content/test:test_support",
+      "//testing/gmock",
+      "//testing/gtest",
+    ]
+  }
 }
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 0673041..1640225 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -3218,7 +3218,7 @@
       key::kSecondaryGoogleAccountSigninAllowed,
       ::account_manager::prefs::kSecondaryGoogleAccountSigninAllowed));
   handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>(
-      key::kUsageTimeLimit, prefs::kUsageTimeLimit, chrome_schema,
+      key::kUsageTimeLimit, ash::prefs::kUsageTimeLimit, chrome_schema,
       SCHEMA_ALLOW_UNKNOWN,
       SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED,
       SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED));
@@ -3250,21 +3250,22 @@
       SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED,
       SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED));
   handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>(
-      key::kParentAccessCodeConfig, prefs::kParentAccessCodeConfig,
+      key::kParentAccessCodeConfig, ash::prefs::kParentAccessCodeConfig,
       chrome_schema, SCHEMA_ALLOW_UNKNOWN,
       SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED,
       SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED));
   handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>(
-      key::kPerAppTimeLimits, prefs::kPerAppTimeLimitsPolicy, chrome_schema,
+      key::kPerAppTimeLimits, ash::prefs::kPerAppTimeLimitsPolicy,
+      chrome_schema, SCHEMA_ALLOW_UNKNOWN,
+      SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED,
+      SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED));
+  handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>(
+      key::kPerAppTimeLimitsAllowlist,
+      ash::prefs::kPerAppTimeLimitsAllowlistPolicy, chrome_schema,
       SCHEMA_ALLOW_UNKNOWN,
       SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED,
       SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED));
   handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>(
-      key::kPerAppTimeLimitsAllowlist, prefs::kPerAppTimeLimitsAllowlistPolicy,
-      chrome_schema, SCHEMA_ALLOW_UNKNOWN,
-      SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED,
-      SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED));
-  handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>(
       key::kEduCoexistenceToSVersion, ash::prefs::kEduCoexistenceToSVersion,
       chrome_schema, SCHEMA_ALLOW_UNKNOWN,
       SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED,
diff --git a/chrome/browser/profiles/BUILD.gn b/chrome/browser/profiles/BUILD.gn
index 1b1b58f..bdea8ab 100644
--- a/chrome/browser/profiles/BUILD.gn
+++ b/chrome/browser/profiles/BUILD.gn
@@ -334,6 +334,7 @@
     "//chrome/browser/page_content_annotations",
     "//chrome/browser/page_content_annotations:extraction_service",
     "//chrome/browser/page_image_service",
+    "//chrome/browser/page_info",
     "//chrome/browser/passage_embeddings",
     "//chrome/browser/password_manager/factories",
     "//chrome/browser/payments/browser_binding:browser_bound_key_deleter",
diff --git a/chrome/browser/resources/ash/settings/os_search_page/search_subpage.html b/chrome/browser/resources/ash/settings/os_search_page/search_subpage.html
index e3717ac0..42f5b22 100644
--- a/chrome/browser/resources/ash/settings/os_search_page/search_subpage.html
+++ b/chrome/browser/resources/ash/settings/os_search_page/search_subpage.html
@@ -44,16 +44,14 @@
         label="$i18n{quickAnswersDefinitionEnable}"
         deep-link-focus-id$="[[Setting.kQuickAnswersDefinition]]">
     </settings-toggle-button>
-    <template is="dom-if" if="[[!quickAnswersTranslationDisabled_]]" restamp>
-      <settings-toggle-button id="quick-answers-translation-enable"
-          class="hr subsection"
-          pref="{{prefs.settings.quick_answers.translation.enabled}}"
-          label="$i18n{quickAnswersTranslationEnable}"
-          sub-label-with-link="[[translationSubLabel_]]"
-          on-sub-label-link-clicked="onSettingsLinkClick_"
-          deep-link-focus-id$="[[Setting.kQuickAnswersTranslation]]">
-      </settings-toggle-button>
-    </template>
+    <settings-toggle-button id="quick-answers-translation-enable"
+        class="hr subsection"
+        pref="{{prefs.settings.quick_answers.translation.enabled}}"
+        label="$i18n{quickAnswersTranslationEnable}"
+        sub-label-with-link="[[translationSubLabel_]]"
+        on-sub-label-link-clicked="onSettingsLinkClick_"
+        deep-link-focus-id$="[[Setting.kQuickAnswersTranslation]]">
+    </settings-toggle-button>
     <settings-toggle-button id="quick-answers-unit-conversion-enable"
         class="hr subsection"
         pref="{{prefs.settings.quick_answers.unit_conversion.enabled}}"
diff --git a/chrome/browser/resources/ash/settings/os_search_page/search_subpage.ts b/chrome/browser/resources/ash/settings/os_search_page/search_subpage.ts
index 7937586..4edeeee6 100644
--- a/chrome/browser/resources/ash/settings/os_search_page/search_subpage.ts
+++ b/chrome/browser/resources/ash/settings/os_search_page/search_subpage.ts
@@ -48,13 +48,6 @@
 
   static get properties() {
     return {
-      quickAnswersTranslationDisabled_: {
-        type: Boolean,
-        value() {
-          return loadTimeData.getBoolean('quickAnswersTranslationDisabled');
-        },
-      },
-
       quickAnswersSubToggleEnabled_: {
         type: Boolean,
         value() {
@@ -83,7 +76,6 @@
 
   private quickAnswersSubLabel_: string;
   private quickAnswersSubToggleEnabled_: boolean;
-  private quickAnswersTranslationDisabled_: boolean;
   private translationSubLabel_: string;
 
   constructor() {
diff --git a/chrome/browser/resources/certificate_manager/BUILD.gn b/chrome/browser/resources/certificate_manager/BUILD.gn
index d4c84ae..291ecd4 100644
--- a/chrome/browser/resources/certificate_manager/BUILD.gn
+++ b/chrome/browser/resources/certificate_manager/BUILD.gn
@@ -23,7 +23,6 @@
     "favicon.svg",
   ]
   web_component_files = [
-    "certificate_list.ts",
     "certificate_manager.ts",
     "certificate_subpage.ts",
     "crs_section.ts",
@@ -36,6 +35,8 @@
     "certificate_entry.ts",
     "certificate_info_dialog.html.ts",
     "certificate_info_dialog.ts",
+    "certificate_list.html.ts",
+    "certificate_list.ts",
     "certificate_password_dialog.html.ts",
     "certificate_password_dialog.ts",
     "certificates_browser_proxy.ts",
@@ -48,7 +49,9 @@
   icons_html_files = [ "certificate_manager_icons.html" ]
   css_files = [
     "certificate_entry.css",
+    "certificate_list.css",
     "certificate_manager_style.css",
+    "certificate_manager_style_lit.css",
   ]
 
   if (is_chromeos) {
diff --git a/chrome/browser/resources/certificate_manager/certificate_list.css b/chrome/browser/resources/certificate_manager/certificate_list.css
new file mode 100644
index 0000000..de07b22
--- /dev/null
+++ b/chrome/browser/resources/certificate_manager/certificate_list.css
@@ -0,0 +1,30 @@
+/* Copyright 2026 The Chromium Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+/* #css_wrapper_metadata_start
+ * #type=style-lit
+ * #import=//resources/cr_elements/cr_shared_vars.css.js
+ * #import=//resources/cr_elements/cr_shared_style_lit.css.js
+ * #import=./certificate_manager_style_lit.css.js
+ * #scheme=relative
+ * #include=cr-shared-style-lit certificate-manager-style-lit
+ * #css_wrapper_metadata_end */
+
+.header-buttons {
+  margin-left: auto;
+}
+
+.no-certs {
+  border-top: none;
+}
+
+/* Used when list is embedded in a subpage; added in .ts code */
+.subpage-padding {
+  padding: 0 var(--cr-section-padding);
+}
+
+.list-title {
+  display: flex;
+  align-items: center;
+}
diff --git a/chrome/browser/resources/certificate_manager/certificate_list.html b/chrome/browser/resources/certificate_manager/certificate_list.html
deleted file mode 100644
index 039f4a5..0000000
--- a/chrome/browser/resources/certificate_manager/certificate_list.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<style include="cr-shared-style certificate-manager-style">
-
-  .header-buttons {
-    margin-left: auto;
-  }
-
-  .no-certs {
-    border-top: none;
-  }
-
-  /* Used when list is embedded in a subpage; added in .ts code */
-  .subpage-padding {
-    padding: 0 var(--cr-section-padding);
-  }
-
-  .list-title {
-    display: flex;
-    align-items: center;
-  }
-</style>
-<div hidden$="[[hideEverything_]]">
-  <div id="listHeader" class="section-title list-title first"
-      hidden$="[[hideHeader]]">
-    [[headerText]]
-    <div class="header-buttons">
-      <cr-button hidden$="[[!showImport]]" id="importCert"
-          aria-label="[[i18n('certificateManagerV2ImportButtonAriaLabel', headerText)]]"
-          on-click="onImportCertClick_">
-        $i18n{certificateManagerV2ImportButtonLabel}
-      </cr-button>
-      <cr-button hidden$="[[!showImportAndBind]]" id="importAndBindCert"
-          aria-label="[[i18n('certificateManagerV2ImportAndBindButtonAriaLabel', headerText)]]"
-          on-click="onImportAndBindCertClick_">
-        $i18n{certificateManagerV2ImportAndBindButtonLabel}
-      </cr-button>
-      <cr-button hidden$="[[hideExportButton_(hideExport, hasCerts_)]]"
-          aria-label="[[i18n('certificateManagerV2ExportButtonAriaLabel', headerText)]]"
-          id="exportCerts" on-click="onExportCertsClick_">
-        $i18n{certificateManagerV2ExportButtonLabel}
-      </cr-button>
-    </div>
-    <cr-expand-button id="expandButton" expanded="{{expanded_}}" no-hover
-        aria-label="[[i18n('certificateManagerV2ListExpandAriaLabel', headerText)]]"
-        hidden$="[[hideCollapseButton_(noCollapse, hasCerts_)]]">
-    </cr-expand-button>
-  </div>
-
-  <cr-collapse id="certs" opened="[[expanded_]]" aria-live="polite">
-    <template is="dom-repeat" items="[[certificates_]]">
-      <certificate-entry
-          cert-source="[[certSource]]"
-          display-name="[[item.displayName]]"
-          sha256hash-hex="[[item.sha256hashHex]]"
-          show-edit-icon="[[certMetadataEditable]]"
-          is-deletable="[[item.isDeletable]]"
-          on-delete-result="onDeleteResult_">
-      </certificate-entry>
-    </template>
-    <div id="noCertsRow" class="cr-row no-certs" hidden="[[hasCerts_]]">
-      $i18n{certificateManagerV2NoCertificatesRow}
-    </div>
-  </cr-collapse>
-</div>
diff --git a/chrome/browser/resources/certificate_manager/certificate_list.html.ts b/chrome/browser/resources/certificate_manager/certificate_list.html.ts
new file mode 100644
index 0000000..e6e4683
--- /dev/null
+++ b/chrome/browser/resources/certificate_manager/certificate_list.html.ts
@@ -0,0 +1,66 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {html} from '//resources/lit/v3_0/lit.rollup.js';
+
+import type {CertificateListElement} from './certificate_list.js';
+
+export function getHtml(this: CertificateListElement) {
+  // clang-format off
+  return html`<!--_html_template_start_-->
+<div ?hidden="${this.shouldHideEverything_()}">
+  <div id="listHeader"
+      class="section-title list-title first
+          ${this.getListHeaderAdditionalClass_()}"
+      ?hidden="${this.hideHeader}">
+    ${this.headerText}
+    <div class="header-buttons">
+      <cr-button ?hidden="${!this.showImport}" id="importCert"
+          aria-label="${this.i18n('certificateManagerV2ImportButtonAriaLabel',
+              this.headerText)}"
+          @click="${this.onImportCertClick_}">
+        $i18n{certificateManagerV2ImportButtonLabel}
+      </cr-button>
+      <cr-button ?hidden="${!this.showImportAndBind}" id="importAndBindCert"
+          aria-label="${this.i18n(
+              'certificateManagerV2ImportAndBindButtonAriaLabel',
+              this.headerText)}"
+          @click="${this.onImportAndBindCertClick_}">
+        $i18n{certificateManagerV2ImportAndBindButtonLabel}
+      </cr-button>
+      <cr-button ?hidden="${this.hideExportButton_()}"
+          aria-label="${this.i18n('certificateManagerV2ExportButtonAriaLabel',
+              this.headerText)}"
+          id="exportCerts" @click="${this.onExportCertsClick_}">
+        $i18n{certificateManagerV2ExportButtonLabel}
+      </cr-button>
+    </div>
+    <cr-expand-button id="expandButton" ?expanded="${this.expanded_}" no-hover
+        aria-label="${this.i18n('certificateManagerV2ListExpandAriaLabel',
+            this.headerText)}"
+        ?hidden="${this.hideCollapseButton_()}"
+        @expanded-changed="${this.onExpandedChanged_}">
+    </cr-expand-button>
+  </div>
+
+  <cr-collapse id="certs" ?opened="${this.expanded_}" aria-live="polite"
+      class="${this.getCertsClass_()}">
+    ${this.certificates_.map(item => html`
+      <certificate-entry
+          .certSource="${this.certSource}"
+          .displayName="${item.displayName}"
+          .sha256hashHex="${item.sha256hashHex}"
+          ?show-edit-icon="${this.certMetadataEditable}"
+          ?is-deletable="${item.isDeletable}"
+          @delete-result="${this.onDeleteResult_}">
+      </certificate-entry>
+    `)}
+    <div id="noCertsRow" class="cr-row no-certs" ?hidden="${this.hasCerts_}">
+      $i18n{certificateManagerV2NoCertificatesRow}
+    </div>
+  </cr-collapse>
+</div>
+<!--_html_template_end_-->`;
+  // clang-format on
+}
diff --git a/chrome/browser/resources/certificate_manager/certificate_list.ts b/chrome/browser/resources/certificate_manager/certificate_list.ts
index da55d195..41a67c7 100644
--- a/chrome/browser/resources/certificate_manager/certificate_list.ts
+++ b/chrome/browser/resources/certificate_manager/certificate_list.ts
@@ -14,22 +14,22 @@
 
 import '/strings.m.js';
 import './certificate_entry.js';
-import './certificate_manager_style.css.js';
 import '//resources/cr_elements/cr_expand_button/cr_expand_button.js';
 import '//resources/cr_elements/cr_button/cr_button.js';
 import '//resources/cr_elements/cr_collapse/cr_collapse.js';
-import '//resources/cr_elements/cr_shared_style.css.js';
-import '//resources/cr_elements/cr_shared_vars.css.js';
 
 import type {CrCollapseElement} from '//resources/cr_elements/cr_collapse/cr_collapse.js';
-import {I18nMixin} from '//resources/cr_elements/i18n_mixin.js';
-import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {I18nMixinLit} from '//resources/cr_elements/i18n_mixin_lit.js';
+import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
+import type {PropertyValues} from '//resources/lit/v3_0/lit.rollup.js';
 
-import {getTemplate} from './certificate_list.html.js';
-import type {ActionResult, CertificateSource, SummaryCertInfo} from './certificate_manager.mojom-webui.js';
+import {getCss} from './certificate_list.css.js';
+import {getHtml} from './certificate_list.html.js';
+import {CertificateSource} from './certificate_manager.mojom-webui.js';
+import type {ActionResult, SummaryCertInfo} from './certificate_manager.mojom-webui.js';
 import {CertificatesBrowserProxy} from './certificates_browser_proxy.js';
 
-const CertificateListElementBase = I18nMixin(PolymerElement);
+const CertificateListElementBase = I18nMixinLit(CrLitElement);
 
 export interface CertificateListElement {
   $: {
@@ -48,112 +48,60 @@
     return 'certificate-list';
   }
 
-  static get template() {
-    return getTemplate();
+  static override get styles() {
+    return getCss();
   }
 
-  static get properties() {
+  override render() {
+    return getHtml.bind(this)();
+  }
+
+  static override get properties() {
     return {
-      certSource: Number,
-      headerText: String,
-
-      showImport: {
-        type: Boolean,
-        value: false,
-      },
-
-      showImportAndBind: {
-        type: Boolean,
-        value: false,
-      },
-
-      // True if the list should not be collapsible.
-      // Empty lists will always not be collapsible.
-      noCollapse: {
-        type: Boolean,
-        value: false,
-      },
-
-      // True if the export button should be hidden.
-      // Export button may also be hidden if there are no certs in the list.
-      hideExport: {
-        type: Boolean,
-        value: false,
-      },
-
-      // True if the entire list (including the header) should be hidden if the
-      // list is empty.
-      hideIfEmpty: {
-        type: Boolean,
-        value: false,
-      },
-
-      // True if the header should be hidden. This will make the list
-      // non-collapsible.
-      hideHeader: {
-        type: Boolean,
-        value: false,
-      },
-
-      // True if the cert metadata is editable
-      certMetadataEditable: {
-        type: Boolean,
-        value: false,
-      },
-
-      inSubpage: {
-        type: Boolean,
-        value: false,
-      },
-
-      expanded_: {
-        type: Boolean,
-        value: true,
-      },
-
-      certificates_: {
-        type: Array,
-        value: () => [],
-      },
-
-      hideEverything_: {
-        type: Boolean,
-        computed: 'computeHideEverything_(certificates_)',
-      },
-
-      hasCerts_: {
-        type: Boolean,
-        computed: 'computeHasCerts_(certificates_)',
-      },
+      certSource: {type: Number},
+      headerText: {type: String},
+      showImport: {type: Boolean},
+      showImportAndBind: {type: Boolean},
+      noCollapse: {type: Boolean},
+      hideExport: {type: Boolean},
+      hideIfEmpty: {type: Boolean},
+      hideHeader: {type: Boolean},
+      certMetadataEditable: {type: Boolean},
+      inSubpage: {type: Boolean},
+      expanded_: {type: Boolean},
+      certificates_: {type: Array},
+      hasCerts_: {type: Boolean},
     };
   }
 
-  declare certSource: CertificateSource;
-  declare headerText: string;
-  declare certMetadataEditable: boolean;
-  declare showImport: boolean;
-  declare showImportAndBind: boolean;
-  declare hideExport: boolean;
-  declare hideHeader: boolean;
-  declare inSubpage: boolean;
-  declare noCollapse: boolean;
-  declare hideIfEmpty: boolean;
-  declare private expanded_: boolean;
-  declare private hideEverything_: boolean;
-  declare private certificates_: SummaryCertInfo[];
-  declare private hasCerts_: boolean;
+  accessor certSource: CertificateSource = CertificateSource.MIN_VALUE;
+  accessor headerText: string = '';
+  accessor showImport: boolean = false;
+  accessor showImportAndBind: boolean = false;
+  accessor noCollapse: boolean = false;
+  accessor hideExport: boolean = false;
+  accessor hideIfEmpty: boolean = false;
+  accessor hideHeader: boolean = false;
+  accessor certMetadataEditable: boolean = false;
+  accessor inSubpage: boolean = false;
+  protected accessor expanded_: boolean = true;
+  protected accessor certificates_: SummaryCertInfo[] = [];
+  protected accessor hasCerts_: boolean = false;
 
-  override ready() {
-    super.ready();
+  override willUpdate(changedProperties: PropertyValues<this>) {
+    super.willUpdate(changedProperties);
 
-    this.refreshCertificates();
-
-    if (!this.inSubpage) {
-      this.$.certs.classList.add('card');
+    const changedPrivateProperties =
+        changedProperties as Map<PropertyKey, unknown>;
+    if (changedPrivateProperties.has('certificates_')) {
+      this.hasCerts_ = this.certificates_.length > 0;
     }
-    if (this.inSubpage) {
-      this.$.listHeader.classList.add('subpage-padding');
-    }
+  }
+
+  override firstUpdated(changedProperties: PropertyValues<this>) {
+    super.firstUpdated(changedProperties);
+
+    this.refreshCertificates_();
 
     const proxy = CertificatesBrowserProxy.getInstance();
     proxy.callbackRouter.triggerReload.addListener(
@@ -162,11 +110,11 @@
 
   private onRefreshRequested_(certSources: CertificateSource[]) {
     if (certSources.includes(this.certSource)) {
-      this.refreshCertificates();
+      this.refreshCertificates_();
     }
   }
 
-  private refreshCertificates() {
+  protected refreshCertificates_() {
     CertificatesBrowserProxy.getInstance()
         .handler.getCertificates(this.certSource)
         .then((results: {certs: SummaryCertInfo[]}) => {
@@ -174,14 +122,14 @@
         });
   }
 
-  private onExportCertsClick_(e: Event) {
+  protected onExportCertsClick_(e: Event) {
     // Export button click shouldn't collapse the list as well.
     e.stopPropagation();
     CertificatesBrowserProxy.getInstance().handler.exportCertificates(
         this.certSource);
   }
 
-  private onImportCertClick_(e: Event) {
+  protected onImportCertClick_(e: Event) {
     // Import button click shouldn't collapse the list as well.
     e.stopPropagation();
     CertificatesBrowserProxy.getInstance()
@@ -189,7 +137,7 @@
         .then(this.handleImportResult.bind(this));
   }
 
-  private onImportAndBindCertClick_(e: Event) {
+  protected onImportAndBindCertClick_(e: Event) {
     // Import button click shouldn't collapse the list as well.
     e.stopPropagation();
     CertificatesBrowserProxy.getInstance()
@@ -200,37 +148,43 @@
   private handleImportResult(value: {result: ActionResult|null}) {
     if (value.result !== null && value.result.success !== undefined) {
       // On successful import, refresh the certificate list.
-      this.refreshCertificates();
+      this.refreshCertificates_();
     }
-    this.dispatchEvent(new CustomEvent(
-        'import-result',
-        {composed: true, bubbles: true, detail: value.result}));
+    this.fire('import-result', value.result);
   }
 
-  private onDeleteResult_(e: CustomEvent<ActionResult|null>) {
+  protected onDeleteResult_(e: CustomEvent<ActionResult|null>) {
     const result = e.detail;
     if (result !== null && result.success !== undefined) {
       // On successful deletion, refresh the certificate list.
-      this.refreshCertificates();
+      this.refreshCertificates_();
       this.$.importCert.focus();
     }
   }
 
-  private computeHasCerts_(): boolean {
-    return this.certificates_.length > 0;
-  }
-
-  private computeHideEverything_(): boolean {
+  protected shouldHideEverything_(): boolean {
     return this.hideIfEmpty && this.certificates_.length === 0;
   }
 
-  private hideCollapseButton_(): boolean {
+  protected hideCollapseButton_(): boolean {
     return this.noCollapse || !this.hasCerts_;
   }
 
-  private hideExportButton_(): boolean {
+  protected hideExportButton_(): boolean {
     return this.hideExport || !this.hasCerts_;
   }
+
+  protected onExpandedChanged_(e: CustomEvent<{value: boolean}>) {
+    this.expanded_ = e.detail.value;
+  }
+
+  protected getListHeaderAdditionalClass_(): string {
+    return this.inSubpage ? 'subpage-padding' : '';
+  }
+
+  protected getCertsClass_(): string {
+    return this.inSubpage ? '' : 'card';
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/certificate_manager/certificate_manager_style.css b/chrome/browser/resources/certificate_manager/certificate_manager_style.css
index 48949f8..5740038d 100644
--- a/chrome/browser/resources/certificate_manager/certificate_manager_style.css
+++ b/chrome/browser/resources/certificate_manager/certificate_manager_style.css
@@ -8,28 +8,5 @@
  * #import=//resources/cr_elements/cr_shared_vars.css.js
  * #css_wrapper_metadata_end */
 
-.section-title {
-  color: var(--cr-primary-text-color);
-  font-size: 108%;
-  font-weight: 400;
-  letter-spacing: .25px;
-  margin-bottom: 12px;
-  margin-top: var(--cr-section-vertical-margin);
-  outline: none;
-  padding-bottom: 4px;
-  padding-top: 8px;
-}
-
-.page-title {
-  font-weight: 400;
-}
-
-.card {
-  background-color: var(--cr-card-background-color);
-  border-radius: var(--cr-card-border-radius);
-  box-shadow: var(--cr-card-shadow);
-}
-
-.cr-centered-card-container {
-  padding-bottom: 20px;
-}
+/* Purposefully empty since this style is generated at build time from the
+ * equivalent Lit version. */
diff --git a/chrome/browser/resources/certificate_manager/certificate_manager_style_lit.css b/chrome/browser/resources/certificate_manager/certificate_manager_style_lit.css
new file mode 100644
index 0000000..8f720c9
--- /dev/null
+++ b/chrome/browser/resources/certificate_manager/certificate_manager_style_lit.css
@@ -0,0 +1,35 @@
+/* Copyright 2026 The Chromium Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file. */
+
+/* #css_wrapper_metadata_start
+ * #type=style-lit
+ * #scheme=relative
+ * #import=//resources/cr_elements/cr_shared_vars.css.js
+ * #css_wrapper_metadata_end */
+
+.section-title {
+  color: var(--cr-primary-text-color);
+  font-size: 108%;
+  font-weight: 400;
+  letter-spacing: .25px;
+  margin-bottom: 12px;
+  margin-top: var(--cr-section-vertical-margin);
+  outline: none;
+  padding-bottom: 4px;
+  padding-top: 8px;
+}
+
+.page-title {
+  font-weight: 400;
+}
+
+.card {
+  background-color: var(--cr-card-background-color);
+  border-radius: var(--cr-card-border-radius);
+  box-shadow: var(--cr-card-shadow);
+}
+
+.cr-centered-card-container {
+  padding-bottom: 20px;
+}
diff --git a/chrome/browser/resources/contextual_tasks/app.css b/chrome/browser/resources/contextual_tasks/app.css
index 449e0a1..f004444 100644
--- a/chrome/browser/resources/contextual_tasks/app.css
+++ b/chrome/browser/resources/contextual_tasks/app.css
@@ -10,6 +10,8 @@
 
 :host {
   --color-composebox-cancel-button: rgba(10,10,10, 1);
+  --color-composebox-context-menu-checkbox-override: rgba(11, 87, 208, 1);
+  --color-composebox-context-menu-text: rgba(31, 31, 31, 1);
   /* The top border of matches/tab suggestions dropdown in context menu. */
   --color-composebox-file-carousel-divider: rgba(210, 225, 252, 1);
   --color-composebox-file-carousel-remove-gradient-end: rgba(0, 0, 0, 0.2);
@@ -42,6 +44,7 @@
   --color-composebox-lens-active-background: rgba(225, 227, 232, 1);
   --color-composebox-zero-state-suggestions-background-hovered:
       rgba(247, 248, 250, 1);
+  --color-context-menu-hover-background: rgba(242, 242, 242, 1);
   --color-file-chip-remove-button-fill: rgba(255, 255, 255, 1);
   --color-full-file-chip-remove-button-hover-background:
     rgba(0, 0, 0, 0.6);
@@ -180,6 +183,8 @@
 :host([dark-mode_]) {
   --color-composebox-background: rgba(31, 31, 31, 1);
   --color-composebox-cancel-button: rgba(191, 191, 191, 1);
+  --color-composebox-context-menu-checkbox-override: rgba(168, 199, 250, 1);
+  --color-composebox-context-menu-text: rgba(227, 227, 227, 1);
   --color-composebox-file-carousel-divider: rgba(94, 94, 94, 1);
   --color-composebox-file-carousel-remove-gradient-end: rgba(100, 100, 100, 0.2);
   --color-composebox-file-carousel-remove-gradient-start: rgba(100, 100, 100, 0.2);
@@ -201,9 +206,10 @@
   --color-composebox-type-ahead-chip: rgba(199, 199, 199, 1);
   --color-composebox-voice-lens-button: rgba(191, 191, 191, 1);
   --color-composebox-lens-active-background: rgba(42, 43, 54, 1);
-  --color-searchbox-results-background-hovered: rgba(54, 54, 54, 1);
   --color-composebox-zero-state-suggestions-background-hovered:
       rgba(23, 24, 31, 1);
+  --color-context-menu-hover-background: rgba(53, 53, 53, 1);
+  --color-searchbox-results-background-hovered: rgba(54, 54, 54, 1);
   --color-suggestions-background-loading: rgba(60, 64, 67, 1);
   --color-zero-state-greeting: rgba(230, 232, 240, 1);
   --color-zero-state-match-icon: rgba(139, 140, 145, 1);
diff --git a/chrome/browser/resources/glic/glic_api/glic_api.ts b/chrome/browser/resources/glic/glic_api/glic_api.ts
index f64b02b..10b0be1 100644
--- a/chrome/browser/resources/glic/glic_api/glic_api.ts
+++ b/chrome/browser/resources/glic/glic_api/glic_api.ts
@@ -2760,6 +2760,8 @@
   CAPTURE_REGION_HOTKEY = 20,
   // From the in-product-help (IPH) entrypoint.
   IPH = 21,
+  // User clicked an anchored contextual cue chip.
+  ANCHORED_CONTEXTUAL_CUE = 22,
 }
 
 ///////////////////////////////////////////////
diff --git a/chrome/browser/resources/intro/dice_app.html.ts b/chrome/browser/resources/intro/dice_app.html.ts
index 460940a..3871ecdf 100644
--- a/chrome/browser/resources/intro/dice_app.html.ts
+++ b/chrome/browser/resources/intro/dice_app.html.ts
@@ -4,9 +4,9 @@
 
 import {html} from '//resources/lit/v3_0/lit.rollup.js';
 
-import type {DiceAppElement} from './dice_app.js';
+import type {IntroAppElement} from './dice_app.js';
 
-export function getHtml(this: DiceAppElement) {
+export function getHtml(this: IntroAppElement) {
   return html`<!--_html_template_start_-->
 <cr-view-manager id="viewManager">
   <div id="splash" slot="view">
diff --git a/chrome/browser/resources/intro/dice_app.ts b/chrome/browser/resources/intro/dice_app.ts
index 9e89c9d..b8a0283 100644
--- a/chrome/browser/resources/intro/dice_app.ts
+++ b/chrome/browser/resources/intro/dice_app.ts
@@ -12,13 +12,13 @@
 import {getCss} from './dice_app.css.js';
 import {getHtml} from './dice_app.html.js';
 
-export interface DiceAppElement {
+export interface IntroAppElement {
   $: {
     viewManager: CrViewManagerElement,
   };
 }
 
-export class DiceAppElement extends CrLitElement {
+export class IntroAppElement extends CrLitElement {
   static get is() {
     return 'intro-app';
   }
@@ -55,8 +55,8 @@
 
 declare global {
   interface HTMLElementTagNameMap {
-    'intro-app': DiceAppElement;
+    'intro-app': IntroAppElement;
   }
 }
 
-customElements.define(DiceAppElement.is, DiceAppElement);
+customElements.define(IntroAppElement.is, IntroAppElement);
diff --git a/chrome/browser/resources/new_tab_page/action_chips/action_chips.html.ts b/chrome/browser/resources/new_tab_page/action_chips/action_chips.html.ts
index 9e0cfd69..3d60343 100644
--- a/chrome/browser/resources/new_tab_page/action_chips/action_chips.html.ts
+++ b/chrome/browser/resources/new_tab_page/action_chips/action_chips.html.ts
@@ -15,7 +15,9 @@
     ${
       this.actionChips_.length ?
       html`
-      <div class="action-chips-container">
+      <div class="action-chips-container"
+        @contextmenu="${this.showBackground && this.showSimplifiedUI_
+          ? this.onContextmenu_ : nothing}">
       ${
           this.actionChips_.map(
               (chip: ActionChip, index: number) => html`
@@ -24,7 +26,8 @@
             class="action-chip"
             data-index="${index}"
             title="${this.getChipTitle_(chip)}"
-            @click="${this.onClick_}">
+            @click="${this.onClick_}"
+            @contextmenu="${this.onContextmenu_}">
             <div class="action-chip-icon-container ${
                     this.getAdditionalIconClasses_(chip)}">
               ${
@@ -57,6 +60,11 @@
       </div>
       ` : nothing}
   </div>
+  <cr-action-menu id="actionMenu">
+    <button class="dropdown-item" @click="${this.onDisableSuggestionClick_}">
+      $i18n{disableSuggestion}
+    </button>
+  </cr-action-menu>
   <!--_html_template_end_-->`;
   // clang-format on
 }
diff --git a/chrome/browser/resources/new_tab_page/action_chips/action_chips.ts b/chrome/browser/resources/new_tab_page/action_chips/action_chips.ts
index 92270ae..7a7536a 100644
--- a/chrome/browser/resources/new_tab_page/action_chips/action_chips.ts
+++ b/chrome/browser/resources/new_tab_page/action_chips/action_chips.ts
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
 
 import type {TabUpload} from 'chrome://resources/cr_components/composebox/common.js';
 import {TabUploadOrigin} from 'chrome://resources/cr_components/composebox/common.js';
+import type {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
@@ -48,6 +50,12 @@
 const kActionChipsRetrievalStateChangedEvent =
     'action-chips-retrieval-state-changed';
 
+export interface ActionChipsElement {
+  $: {
+    actionMenu: CrActionMenuElement,
+  };
+}
+
 /**
  * The element for displaying Action Chips.
  */
@@ -190,6 +198,18 @@
         this.actionChips_.filter((c) => c.suggestion !== chip.suggestion);
   }
 
+  protected onContextmenu_(e: MouseEvent) {
+    e.preventDefault();
+    e.stopPropagation();
+    this.$.actionMenu.showAt(e.target as HTMLElement);
+  }
+
+  protected onDisableSuggestionClick_() {
+    this.$.actionMenu.close();
+    this.handler.setActionChipsVisibility(false);
+  }
+
+
   protected getFaviconUrl_(url: string): string {
     const faviconUrl = new URL('chrome://favicon2/');
     faviconUrl.searchParams.set('size', '24');
diff --git a/chrome/browser/resources/new_tab_page/modules/authentication/microsoft_auth_module.ts b/chrome/browser/resources/new_tab_page/modules/authentication/microsoft_auth_module.ts
index d5d6aee..d00f5606 100644
--- a/chrome/browser/resources/new_tab_page/modules/authentication/microsoft_auth_module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/authentication/microsoft_auth_module.ts
@@ -36,7 +36,7 @@
  */
 export class MicrosoftAuthModuleElement extends MicrosoftAuthModuleElementBase {
   static get is() {
-    return 'ntp-microsoft-authentication-module';
+    return 'ntp-microsoft-auth-module';
   }
 
   static override get styles() {
@@ -106,7 +106,7 @@
 
 declare global {
   interface HTMLElementTagNameMap {
-    'ntp-microsoft-authentication-module': MicrosoftAuthModuleElement;
+    'ntp-microsoft-auth-module': MicrosoftAuthModuleElement;
   }
 }
 
diff --git a/chrome/browser/resources/omnibox_popup/aim_app.css b/chrome/browser/resources/omnibox_popup/aim_app.css
index 4a70cda3..6fd6fbf6 100644
--- a/chrome/browser/resources/omnibox_popup/aim_app.css
+++ b/chrome/browser/resources/omnibox_popup/aim_app.css
@@ -95,7 +95,7 @@
 }
 
 cr-composebox::part(context-menu-and-tools) {
-  margin-top: 2px;
+  margin-top: var(--omnibox-context-menu-container-margin-top);
 }
 
 cr-composebox[searchbox-layout-mode='TallBottomContext']::part(context-menu-and-tools) {
diff --git a/chrome/browser/resources/omnibox_popup/app.css b/chrome/browser/resources/omnibox_popup/app.css
index 9333214..1636f9af 100644
--- a/chrome/browser/resources/omnibox_popup/app.css
+++ b/chrome/browser/resources/omnibox_popup/app.css
@@ -69,7 +69,7 @@
 }
 
 .context-menu-container {
-  margin-top: 2px;
+  margin-top: var(--omnibox-context-menu-container-margin-top);
 }
 
 :host([searchbox-layout-mode_='TallBottomContext']) .context-menu-container:has(*),
diff --git a/chrome/browser/resources/omnibox_popup/omnibox_popup_shared_style.css b/chrome/browser/resources/omnibox_popup/omnibox_popup_shared_style.css
index f202129..539b05d 100644
--- a/chrome/browser/resources/omnibox_popup/omnibox_popup_shared_style.css
+++ b/chrome/browser/resources/omnibox_popup/omnibox_popup_shared_style.css
@@ -29,4 +29,5 @@
    * TODO(crbug.com/450640041): Investigate webUI blink & views rendering
    * difference. */
   --omnibox-font-size: 14.3px;
+  --omnibox-context-menu-container-margin-top: 2px;
 }
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts
index 5cea6b7..f5cef48 100644
--- a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts
+++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts
@@ -138,12 +138,14 @@
   private getMenuItemsForBookmarks_(): MenuItem[] {
     // TODO(crbug.com/40262319): Factor in URLs not available in incognito.
     let bookmarkCount = 0;
-    this.bookmarks_.forEach((bookmark) => {
+    // Filter out undefined bookmarks which might exist temporarily as the
+    // bookmarks tree is being manipulated.
+    this.bookmarks_.filter(bookmark => !!bookmark).forEach((bookmark) => {
       if (bookmark.url) {
         bookmarkCount += 1;
       } else if (bookmark.children) {
         bookmarkCount +=
-            bookmark.children.filter((child) => !!child.url).length;
+            bookmark.children.filter((child) => child && !!child.url).length;
       }
     });
     const menuItems: MenuItem[] = [
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
index 7fb971a8..6b5207f 100644
--- a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
+++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
@@ -535,6 +535,9 @@
     this.updatedElementIds_ = [bookmark.parentId];
     this.set(`trackedProductInfos_.${bookmark.id}`, null);
     this.availableProductInfos_.delete(bookmark.id);
+    if (this.selectedBookmarks_[bookmark.id]) {
+      this.set(`selectedBookmarks_.${bookmark.id}`, false);
+    }
 
     // If the parent folder is visible, notify to ensure its displayed
     // child count is updated.
@@ -1052,8 +1055,9 @@
     const selectedEntries = Object.entries(this.selectedBookmarks_)
                                 .filter(([_id, selected]) => selected);
     const selectedIds = selectedEntries.map(([id, _selected]) => id);
-    return selectedIds.map(
-        (id) => this.bookmarksService_.findBookmarkWithId(id)!);
+    return selectedIds
+        .map((id) => this.bookmarksService_.findBookmarkWithId(id)!)
+        .filter(b => !!b);
   }
 
   private getSelectedBookmarksLength_(): number {
diff --git a/chrome/browser/resources/side_panel/read_anything/menus/simple_action_menu.ts b/chrome/browser/resources/side_panel/read_anything/menus/simple_action_menu.ts
index 6f827e0c..9386c0f 100644
--- a/chrome/browser/resources/side_panel/read_anything/menus/simple_action_menu.ts
+++ b/chrome/browser/resources/side_panel/read_anything/menus/simple_action_menu.ts
@@ -66,7 +66,9 @@
   accessor label: string = '';
 
   open(anchor: HTMLElement, showAtConfig?: ShowAtConfigPrefs) {
-    openMenu(this.$.lazyMenu.get(), anchor, showAtConfig);
+    openMenu(
+        this.$.lazyMenu.get(), anchor, showAtConfig, /* onShow= */ undefined,
+        this.nonModal);
   }
 
   close() {
diff --git a/chrome/browser/resources/side_panel/read_anything/shared/common.ts b/chrome/browser/resources/side_panel/read_anything/shared/common.ts
index 70a9c00..a396ebb 100644
--- a/chrome/browser/resources/side_panel/read_anything/shared/common.ts
+++ b/chrome/browser/resources/side_panel/read_anything/shared/common.ts
@@ -26,7 +26,8 @@
 
 export function openMenu(
     menuToOpen: CrActionMenuElement, target: HTMLElement,
-    showAtConfig?: ShowAtConfigPrefs, onShow?: () => void) {
+    showAtConfig?: ShowAtConfigPrefs, onShow?: () => void,
+    isSubmenu: boolean = false) {
   // The button should stay active while the menu is open and deactivate when
   // the menu closes.
   menuToOpen.addEventListener('close', () => {
@@ -53,7 +54,6 @@
               },
               showAtConfig));
 
-      const isSubmenu = menuToOpen.nonModal;
       // We manually override submenu positions here because cr-action-menu's
       // native side-collision aggressively flips the entire menu. We must do
       // this after showAt() because <cr-lazy-render> keeps offsetWidth at 0
diff --git a/chrome/browser/search_integrity/search_integrity.cc b/chrome/browser/search_integrity/search_integrity.cc
index 6f48af9..c2069bfd 100644
--- a/chrome/browser/search_integrity/search_integrity.cc
+++ b/chrome/browser/search_integrity/search_integrity.cc
@@ -50,8 +50,10 @@
     for (auto piece : base::SplitStringPiece(text, base::kWhitespaceUTF16,
                                              base::TRIM_WHITESPACE,
                                              base::SPLIT_WANT_NONEMPTY)) {
-      if (piece.length() >= kMinWordLength) {
-        words.push_back(base::i18n::ToLower(piece));
+      std::u16string cleaned_piece;
+      base::RemoveChars(piece, u".,!?:;\"'()[]{}<>-", &cleaned_piece);
+      if (cleaned_piece.length() >= kMinWordLength) {
+        words.push_back(base::i18n::ToLower(cleaned_piece));
       }
     }
     return words;
diff --git a/chrome/browser/search_integrity/search_integrity_unittest.cc b/chrome/browser/search_integrity/search_integrity_unittest.cc
index 5d79c384..d0dd1ca 100644
--- a/chrome/browser/search_integrity/search_integrity_unittest.cc
+++ b/chrome/browser/search_integrity/search_integrity_unittest.cc
@@ -236,6 +236,20 @@
   EXPECT_TRUE(report.is_default_custom_with_matching_policy_engine);
 }
 
+TEST_F(SearchIntegrityTest, IsNameMatch_PunctuationIsIgnored) {
+  // "Yahoo!" and "Yahoo" should match because "!" is stripped.
+  TemplateURL* custom_engine =
+      AddSearchEngine(u"Goog", "http://custom.goog.com");
+  AddSearchEngine(u"Goog!", "http://policy.goog.com",
+                  /*created_by_policy=*/true);
+  SetDefaultSearchProvider(custom_engine);
+
+  SearchIntegrityReport report = CheckSearchEnginesReport();
+
+  EXPECT_TRUE(report.is_default_custom);
+  EXPECT_TRUE(report.is_default_custom_with_matching_policy_engine);
+}
+
 TEST_F(SearchIntegrityTest, Histograms_LoggedCorrectly) {
   base::HistogramTester histogram_tester;
 
diff --git a/chrome/browser/ssl/ssl_prerender_browsertest.cc b/chrome/browser/ssl/ssl_prerender_browsertest.cc
index ec9fc6d..365542ae 100644
--- a/chrome/browser/ssl/ssl_prerender_browsertest.cc
+++ b/chrome/browser/ssl/ssl_prerender_browsertest.cc
@@ -33,6 +33,7 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/request_handler_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
 #include "url/gurl.h"
 
 using chrome_browser_interstitials::IsShowingSSLInterstitial;
@@ -79,11 +80,22 @@
 
 }  // namespace
 
-class SSLPrerenderTest : public InProcessBrowserTest {
+// With the parameter being true, `kPrerenderActivationByFormSubmission` will be
+// enabled to support non mixed prerender form submission.
+class SSLPrerenderTest : public InProcessBrowserTest,
+                         public testing::WithParamInterface<bool> {
  public:
   SSLPrerenderTest()
       : prerender_helper_(base::BindRepeating(&SSLPrerenderTest::web_contents,
-                                              base::Unretained(this))) {}
+                                              base::Unretained(this))) {
+    if (GetParam()) {
+      feature_list_.InitAndEnableFeature(
+          blink::features::kPrerenderActivationByFormSubmission);
+    } else {
+      feature_list_.InitAndDisableFeature(
+          blink::features::kPrerenderActivationByFormSubmission);
+    }
+  }
   ~SSLPrerenderTest() override = default;
 
   void SetUpOnMainThread() override {
@@ -96,6 +108,9 @@
   }
 
   content::test::PrerenderTestHelper prerender_helper_;
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
 class SecurityVisibleStateObserver : public content::WebContentsObserver {
@@ -111,12 +126,14 @@
   bool is_visible_state_changed_ = false;
 };
 
+INSTANTIATE_TEST_SUITE_P(All, SSLPrerenderTest, ::testing::Bool());
+
 // Verifies that a certificate error in a prerendered page causes cancelation
 // of prerendering without showing an interstitial.
 // TODO(bokan): In the future, when prerendering supports cross origin
 // triggering, this test can be more straightforward by using one server for
 // the initial page and another, with bad certs, for the prerendering page.
-IN_PROC_BROWSER_TEST_F(SSLPrerenderTest, TestNoInterstitialInPrerender) {
+IN_PROC_BROWSER_TEST_P(SSLPrerenderTest, TestNoInterstitialInPrerender) {
   auto server = CreateExpiredCertServer(GetChromeTestDataDir());
   ASSERT_TRUE(server->Start());
 
@@ -180,7 +197,7 @@
 // triggering, this test can be more straightforward by using one server for
 // the initial page and another, with bad certs, for the prerendering page.
 // TODO(crbug.com/40923072): the test has been flaky across platforms.
-IN_PROC_BROWSER_TEST_F(SSLPrerenderTest,
+IN_PROC_BROWSER_TEST_P(SSLPrerenderTest,
                        DISABLED_TestNoInterstitialInPrerenderSW) {
   auto server = CreateExpiredCertServer(GetChromeTestDataDir());
   ASSERT_TRUE(server->Start());
@@ -245,7 +262,7 @@
 
 // Prerenders a page that tries to submit an insecure form and checks that this
 // cancels the prerender instead.
-IN_PROC_BROWSER_TEST_F(SSLPrerenderTest,
+IN_PROC_BROWSER_TEST_P(SSLPrerenderTest,
                        InsecureFormSubmissionCancelsPrerender) {
   auto https_server = CreateHTTPSServer(GetChromeTestDataDir());
   ASSERT_TRUE(https_server->Start());
@@ -296,7 +313,7 @@
 // Prerenders a page that tries to submit an insecure form and checks that this
 // cancels the prerender even if the primary page is proceeding on an insecure
 // form.
-IN_PROC_BROWSER_TEST_F(SSLPrerenderTest,
+IN_PROC_BROWSER_TEST_P(SSLPrerenderTest,
                        InsecureFormSubmissionCancelsPrerenderEvenIfProceeding) {
   auto https_server = CreateHTTPSServer(GetChromeTestDataDir());
   ASSERT_TRUE(https_server->Start());
@@ -372,7 +389,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(SSLPrerenderTest,
+IN_PROC_BROWSER_TEST_P(SSLPrerenderTest,
                        TestNoVisibleStateChangedOnInitialPrerendering) {
   auto https_server = CreateHTTPSServer(GetChromeTestDataDir());
   ASSERT_TRUE(https_server->Start());
@@ -404,3 +421,42 @@
     EXPECT_TRUE(visible_state_observer.is_visible_state_changed());
   }
 }
+
+// Prerenders a page that tries to submit a HTTPS form submission without a
+// schema change and checks that this is proceeded normally.
+IN_PROC_BROWSER_TEST_P(SSLPrerenderTest, HTTPSFormSubmissionIsAllowed) {
+  if (!GetParam()) {
+    GTEST_SKIP() << "This test requires the feature to be enabled";
+  }
+  auto https_server = CreateHTTPSServer(GetChromeTestDataDir());
+  ASSERT_TRUE(https_server->Start());
+
+  // Navigate to an initial page.
+  GURL url = https_server->GetURL("a.test", "/empty.html");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
+
+  // Trigger prerender with form submission.
+  GURL prerender_url = https_server->GetURL("a.test", "/english_page.html?");
+  content::TestActivationManager activation_manager(web_contents(),
+                                                    prerender_url);
+  prerender_helper_.AddPrerendersAsync(
+      {prerender_url},
+      /*eagerness=*/std::nullopt,
+      /*no_vary_search_hint=*/std::nullopt,
+      /*target_hint=*/std::string(),
+      /*ruleset_tag=*/std::nullopt,
+      /*world_id=*/content::ISOLATED_WORLD_ID_GLOBAL,
+      /*form_submission=*/true);
+  content::test::PrerenderTestHelper::WaitForPrerenderLoadCompletion(
+      *web_contents(), prerender_url);
+
+  ASSERT_TRUE(content::ExecJs(web_contents()->GetPrimaryMainFrame(),
+                              content::JsReplace(R"(
+                                const form = document.createElement('form');
+                                form.action = $1;
+                                document.body.appendChild(form);
+                                form.submit(); )",
+                                                 prerender_url)));
+  activation_manager.WaitForNavigationFinished();
+  EXPECT_TRUE(activation_manager.was_activated());
+}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 32f3d82c..f81e133 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2165,7 +2165,6 @@
       "//ash/webui/scanner_feedback_ui",
       "//ash/webui/scanning",
       "//ash/webui/settings/public/constants",
-      "//ash/webui/settings/public/constants:mojom",
       "//ash/webui/shimless_rma",
       "//ash/webui/shortcut_customization_ui",
       "//ash/webui/shortcut_customization_ui/backend/search:mojo_bindings",
@@ -2438,7 +2437,6 @@
       "//chrome/browser/ui/webui/ash/print_preview_cros",
       "//chrome/browser/ui/webui/ash/sanitize_dialog",
       "//chrome/browser/ui/webui/ash/settings",
-      "//chrome/browser/ui/webui/ash/settings/app_management",
       "//chrome/browser/ui/webui/ash/settings/pages",
       "//chrome/browser/ui/webui/ash/settings/pages/a11y",
       "//chrome/browser/ui/webui/ash/settings/pages/about",
@@ -2675,7 +2673,6 @@
       "//chrome/browser/ash/usb",
       "//chrome/browser/ui/ash/login",
       "//chrome/browser/ui/webui/ash/login",
-      "//chrome/browser/ui/webui/ash/settings/app_management",
       "//ui/base/mojom:ui_base_types",
     ]
 
@@ -4534,8 +4531,6 @@
       "views/tabs/tab_layout_state.h",
       "views/tabs/tab_search_button.cc",
       "views/tabs/tab_search_button.h",
-      "views/tabs/tab_search_container.cc",
-      "views/tabs/tab_search_container.h",
       "views/tabs/tab_slot_animation_delegate.cc",
       "views/tabs/tab_slot_animation_delegate.h",
       "views/tabs/tab_slot_controller.h",
@@ -4663,6 +4658,10 @@
       "views/web_apps/web_app_identity_update_confirmation_view.h",
       "views/web_apps/web_app_install_dialog_delegate.cc",
       "views/web_apps/web_app_install_dialog_delegate.h",
+      "views/web_apps/web_app_install_dialog_flow_view.cc",
+      "views/web_apps/web_app_install_dialog_flow_view.h",
+      "views/web_apps/web_app_install_flow_dialog_delegate.cc",
+      "views/web_apps/web_app_install_flow_dialog_delegate.h",
       "views/web_apps/web_app_install_not_supported_dialog.cc",
       "views/web_apps/web_app_launch_dialog.cc",
       "views/web_apps/web_app_modal_dialog_delegate.cc",
diff --git a/chrome/browser/ui/actions/chrome_action_id.h b/chrome/browser/ui/actions/chrome_action_id.h
index ac894a39..6a06451 100644
--- a/chrome/browser/ui/actions/chrome_action_id.h
+++ b/chrome/browser/ui/actions/chrome_action_id.h
@@ -605,6 +605,7 @@
   E(kActionTabSearch, IDC_TAB_SEARCH) \
   E(kActionSplitTab, IDC_SPLIT_TAB) \
   E(kActionFederation) \
+  E(kActionGlicContextualCueing) \
 
 #define CHROME_ACTION_IDS \
     CHROME_COMMON_ACTION_IDS \
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java
index 03643422..aa1ac67f 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java
@@ -82,8 +82,8 @@
 
         mModel.set(
                 BackButtonProperties.CLICK_LISTENER,
-                (metaState) -> {
-                    onBackPressed.onClickWithMeta(metaState);
+                (metaState, buttonState) -> {
+                    onBackPressed.onClickWithMeta(metaState, buttonState);
                     updateButtonEnabledState();
                 });
         mModel.set(
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediatorTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediatorTest.java
index f4a9da1e..63c73c3 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediatorTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediatorTest.java
@@ -16,6 +16,7 @@
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.os.Looper;
+import android.view.MotionEvent;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -157,8 +158,15 @@
 
     @Test
     public void testClick_shouldForwardCallToParent() {
-        mModel.get(BackButtonProperties.CLICK_LISTENER).onClickWithMeta(0);
-        verify(mOnBackPressed).onClickWithMeta(0);
+        mModel.get(BackButtonProperties.CLICK_LISTENER).onClickWithMeta(0, 0);
+        verify(mOnBackPressed).onClickWithMeta(0, 0);
+    }
+
+    @Test
+    public void testMiddleClick_shouldForwardCallToParent() {
+        mModel.get(BackButtonProperties.CLICK_LISTENER)
+                .onClickWithMeta(0, MotionEvent.BUTTON_TERTIARY);
+        verify(mOnBackPressed).onClickWithMeta(0, MotionEvent.BUTTON_TERTIARY);
     }
 
     @Test
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/forward_button/ForwardButtonCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/forward_button/ForwardButtonCoordinator.java
index 186e33f2..451cd24ab 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/forward_button/ForwardButtonCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/forward_button/ForwardButtonCoordinator.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.View;
 
 import androidx.core.widget.ImageViewCompat;
@@ -85,7 +86,9 @@
                 mTopUiThemeColorProvider.getBrandedColorScheme());
         onIncognitoStateChanged(mIncognitoStateProvider.isIncognitoSelected());
 
-        mImageButton.setClickCallback(metaState -> forward(metaState, "MobileToolbarForward"));
+        mImageButton.setClickCallback(
+                (metaState, buttonState) ->
+                        forward(metaState, buttonState, "MobileToolbarForward"));
         mImageButton.setLongClickable(true);
     }
 
@@ -167,13 +170,15 @@
      *
      * @return Whether or not the current Tab did go forward.
      */
-    protected boolean forward(int metaState, String reportingTagPrefix) {
+    protected boolean forward(int metaState, int buttonState, String reportingTagPrefix) {
         if (!mActivityLifecycleDispatcher.isNativeInitializationFinished()) return false;
 
         maybeUnfocusUrlBar();
         boolean hasControl = (metaState & KeyEvent.META_CTRL_ON) != 0;
         boolean hasShift = (metaState & KeyEvent.META_SHIFT_ON) != 0;
-        if (hasControl && hasShift) {
+        boolean isMiddleClick = (buttonState & MotionEvent.BUTTON_TERTIARY) != 0;
+
+        if ((hasControl && hasShift) || (isMiddleClick && hasShift)) {
             // Holding ALT is allowed as well (reference desktop behavior).
 
             // Note on recording user actions: "forward" is recorded regardless of whether it
@@ -181,7 +186,7 @@
             // https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java;l=196;drc=14aab80e079b7db3e85a8302da6d660bafeddfbc
             RecordUserAction.record(reportingTagPrefix + "InNewForegroundTab");
             return mToolbarTabController.forwardInNewTab(/* foregroundNewTab= */ true);
-        } else if (hasControl) {
+        } else if (hasControl || isMiddleClick) {
             RecordUserAction.record(reportingTagPrefix + "InNewBackgroundTab");
             return mToolbarTabController.forwardInNewTab(/* foregroundNewTab= */ false);
         } else if (hasShift) {
diff --git a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java
index fcb3bfe..5134dafc 100644
--- a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java
+++ b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java
@@ -373,7 +373,7 @@
         mBackButtonCoordinator =
                 new BackButtonCoordinator(
                         backButton,
-                        (ignored) -> {
+                        (metaState, buttonState) -> {
                             if (mMediator != null) mMediator.goBack();
                         },
                         mThemeColorProvider,
diff --git a/chrome/browser/ui/ash/app_access/BUILD.gn b/chrome/browser/ui/ash/app_access/BUILD.gn
index fc399f8..ffde60f9 100644
--- a/chrome/browser/ui/ash/app_access/BUILD.gn
+++ b/chrome/browser/ui/ash/app_access/BUILD.gn
@@ -24,6 +24,8 @@
     "//chrome/browser:browser_public_dependencies",
     "//chrome/browser/apps/app_service",
     "//chrome/browser/ash/privacy_hub",
+    "//chromeos/ash/components/browser_context_helper",
+    "//chromeos/ash/experiences/settings_ui",
     "//components/session_manager:base",
   ]
 
diff --git a/chrome/browser/ui/ash/app_access/app_access_notifier.cc b/chrome/browser/ui/ash/app_access/app_access_notifier.cc
index 439ec9a6..e6d4407 100644
--- a/chrome/browser/ui/ash/app_access/app_access_notifier.cc
+++ b/chrome/browser/ui/ash/app_access/app_access_notifier.cc
@@ -9,11 +9,11 @@
 #include <string>
 #include <vector>
 
-#include "app_access_notifier.h"
 #include "ash/constants/ash_features.h"
 #include "ash/system/privacy/privacy_indicators_controller.h"
 #include "ash/system/privacy_hub/camera_privacy_switch_controller.h"
 #include "base/check.h"
+#include "base/check_deref.h"
 #include "base/functional/bind.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/string_util.h"
@@ -23,6 +23,8 @@
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/chrome_pages.h"
+#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
+#include "chromeos/ash/experiences/settings_ui/settings_app_manager.h"
 #include "components/account_id/account_id.h"
 #include "components/services/app_service/public/cpp/app_capability_access_cache.h"
 #include "components/services/app_service/public/cpp/app_capability_access_cache_wrapper.h"
@@ -30,6 +32,7 @@
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/session_manager/session_manager_types.h"
+#include "components/user_manager/user.h"
 
 namespace {
 
@@ -278,9 +281,15 @@
   DCHECK(app_type != apps::AppType::kSystemWeb);
 
   if (app_type == apps::AppType::kWeb) {
-    chrome::ShowAppManagementPage(profile, app_id,
-                                  ash::settings::AppManagementEntryPoint::
-                                      kPrivacyIndicatorsNotificationSettings);
+    const user_manager::User* user =
+        ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile);
+    ash::SettingsAppManager::Get()->Open(
+        CHECK_DEREF(user),
+        ash::SettingsAppManager::OpenParams{
+            .sub_page =
+                ash::SettingsAppManager::CreateAppManagementPagePath(app_id),
+            .entry_point = ash::SettingsAppManager::EntryPoint::
+                kPrivacyIndicatorsNotificationSettings});
   } else {
     apps::AppServiceProxyFactory::GetForProfile(profile)->OpenNativeSettings(
         app_id);
diff --git a/chrome/browser/ui/ash/birch/birch_release_notes_provider.cc b/chrome/browser/ui/ash/birch/birch_release_notes_provider.cc
index c24635a9..4e55c4c1 100644
--- a/chrome/browser/ui/ash/birch/birch_release_notes_provider.cc
+++ b/chrome/browser/ui/ash/birch/birch_release_notes_provider.cc
@@ -15,7 +15,6 @@
 #include "base/command_line.h"
 #include "base/time/time.h"
 #include "chrome/browser/ash/app_list/search/help_app_zero_state_provider.h"
-#include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "ui/base/l10n/l10n_util.h"
 
diff --git a/chrome/browser/ui/ash/login/login_display_host_common.cc b/chrome/browser/ui/ash/login/login_display_host_common.cc
index c6ef67629..3c6f819 100644
--- a/chrome/browser/ui/ash/login/login_display_host_common.cc
+++ b/chrome/browser/ui/ash/login/login_display_host_common.cc
@@ -74,7 +74,6 @@
 #include "chrome/browser/ui/webui/ash/login/terms_of_service_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/user_allowlist_check_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/attestation/attestation_flow_adaptive.h"
 #include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
diff --git a/chrome/browser/ui/ash/login/login_display_host_webui.cc b/chrome/browser/ui/ash/login/login_display_host_webui.cc
index 06c9902..adc47463 100644
--- a/chrome/browser/ui/ash/login/login_display_host_webui.cc
+++ b/chrome/browser/ui/ash/login/login_display_host_webui.cc
@@ -80,7 +80,6 @@
 #include "chrome/browser/ui/webui/ash/login/welcome_screen_handler.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/audio/public/cpp/sounds/sounds_manager.h"
 #include "chromeos/ash/components/audio/sounds.h"
 #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
diff --git a/chrome/browser/ui/ash/login/login_ui_pref_controller.cc b/chrome/browser/ui/ash/login/login_ui_pref_controller.cc
index f49fb01..b745c86e 100644
--- a/chrome/browser/ui/ash/login/login_ui_pref_controller.cc
+++ b/chrome/browser/ui/ash/login/login_ui_pref_controller.cc
@@ -12,7 +12,6 @@
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/system/input_device_settings.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/geolocation/system_location_provider.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ui/ash/login/login_ui_pref_controller_browsertest.cc b/chrome/browser/ui/ash/login/login_ui_pref_controller_browsertest.cc
index 4fc34bd..4364d16 100644
--- a/chrome/browser/ui/ash/login/login_ui_pref_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/login/login_ui_pref_controller_browsertest.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/ash/system/fake_input_device_settings.h"
 #include "chrome/browser/ash/system/input_device_settings.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/common/pref_names.h"
 #include "components/policy/policy_constants.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ui/ash/login/user_adding_screen_browsertest.cc b/chrome/browser/ui/ash/login/user_adding_screen_browsertest.cc
index fb75d12..8c8ee92 100644
--- a/chrome/browser/ui/ash/login/user_adding_screen_browsertest.cc
+++ b/chrome/browser/ui/ash/login/user_adding_screen_browsertest.cc
@@ -22,7 +22,6 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/login/login_display_host.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/prefs/pref_service.h"
 #include "components/session_manager/core/session_manager.h"
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_context_menu_chromeos.cc b/chrome/browser/ui/ash/multi_user/multi_user_context_menu_chromeos.cc
index 2115620e..28e2313c 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_context_menu_chromeos.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_context_menu_chromeos.cc
@@ -16,7 +16,6 @@
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/ash/session/session_controller_client_impl.h"
 #include "chrome/browser/ui/dialogs/browser_dialogs.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/account_id/account_id.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ui/ash/network/network_portal_signin_controller.cc b/chrome/browser/ui/ash/network/network_portal_signin_controller.cc
index 3218f707..2bafa5b 100644
--- a/chrome/browser/ui/ash/network/network_portal_signin_controller.cc
+++ b/chrome/browser/ui/ash/network/network_portal_signin_controller.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/ui/dialogs/browser_dialogs.h"
 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
 #include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/network/network_event_log.h"
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
diff --git a/chrome/browser/ui/ash/network/network_portal_signin_controller_unittest.cc b/chrome/browser/ui/ash/network/network_portal_signin_controller_unittest.cc
index 6d240a22..c2f09f5 100644
--- a/chrome/browser/ui/ash/network/network_portal_signin_controller_unittest.cc
+++ b/chrome/browser/ui/ash/network/network_portal_signin_controller_unittest.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_dialog.h"
 #include "chrome/browser/ui/webui/ash/floating_workspace/floating_workspace_ui.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
diff --git a/chrome/browser/ui/ash/shelf/BUILD.gn b/chrome/browser/ui/ash/shelf/BUILD.gn
index e6e1b32..b455d381 100644
--- a/chrome/browser/ui/ash/shelf/BUILD.gn
+++ b/chrome/browser/ui/ash/shelf/BUILD.gn
@@ -120,7 +120,6 @@
     "//chrome/browser/ui/extensions",
     "//chrome/browser/ui/tabs:tab_enums",
     "//chrome/browser/ui/web_applications",
-    "//chrome/browser/ui/webui/ash/settings/app_management",
     "//chrome/browser/web_applications",
     "//chrome/common",
     "//chromeos/ash/components/browser_context_helper",
@@ -130,6 +129,7 @@
     "//chromeos/ash/experiences/arc:arc_app_constants",
     "//chromeos/ash/experiences/arc:arc_base_utils",
     "//chromeos/ash/experiences/arc:prefs",
+    "//chromeos/ash/experiences/settings_ui",
     "//chromeos/constants",
     "//chromeos/strings:strings_grit",
     "//chromeos/ui/wm",
diff --git a/chrome/browser/ui/ash/shelf/app_service/BUILD.gn b/chrome/browser/ui/ash/shelf/app_service/BUILD.gn
index 37d1b7b..1d7c7a5 100644
--- a/chrome/browser/ui/ash/shelf/app_service/BUILD.gn
+++ b/chrome/browser/ui/ash/shelf/app_service/BUILD.gn
@@ -57,12 +57,13 @@
     "//chrome/browser/ui/ash/multi_user",
     "//chrome/browser/ui/browser_window",
     "//chrome/browser/ui/views/crostini",
-    "//chrome/browser/ui/webui/ash/settings/app_management",
     "//chrome/browser/web_applications",
     "//chromeos/ash/components/borealis",
+    "//chromeos/ash/components/browser_context_helper",
     "//chromeos/ash/experiences/arc",
     "//chromeos/ash/experiences/arc:arc_app_constants",
     "//chromeos/ash/experiences/arc/video_accelerator:protected_native_pixmap_query_client",
+    "//chromeos/ash/experiences/settings_ui",
     "//chromeos/crosapi/cpp:crosapi_constants",
     "//chromeos/ui/base",
     "//components/account_id",
diff --git a/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc
index fcc48a8..9032131a 100644
--- a/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc
@@ -7,6 +7,7 @@
 #include "ash/public/cpp/app_menu_constants.h"
 #include "ash/public/cpp/new_window_delegate.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "base/check_deref.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
@@ -42,10 +43,12 @@
 #include "chrome/browser/ui/ash/shelf/shelf_context_menu.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/views/crostini/crostini_app_restart_dialog.h"
-#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
+#include "chromeos/ash/experiences/settings_ui/settings_app_manager.h"
 #include "components/app_constants/constants.h"
 #include "components/services/app_service/public/cpp/app_types.h"
+#include "components/user_manager/user.h"
 #include "content/public/browser/context_menu_params.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/launch_util.h"
@@ -525,9 +528,16 @@
 
 void AppServiceShelfContextMenu::ShowAppInfo() {
   if (app_type_ == apps::AppType::kArc) {
-    chrome::ShowAppManagementPage(
-        controller()->profile(), item().id.app_id,
-        ash::settings::AppManagementEntryPoint::kShelfContextMenuAppInfoArc);
+    const user_manager::User* user =
+        ash::BrowserContextHelper::Get()->GetUserByBrowserContext(
+            controller()->profile());
+    ash::SettingsAppManager::Get()->Open(
+        CHECK_DEREF(user),
+        ash::SettingsAppManager::OpenParams{
+            .sub_page = ash::SettingsAppManager::CreateAppManagementPagePath(
+                item().id.app_id),
+            .entry_point = ash::SettingsAppManager::EntryPoint::
+                kShelfContextMenuAppInfoArc});
     return;
   }
 
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc
index 7ac3e0b..02718d6 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc
@@ -76,7 +76,6 @@
 #include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h"
 #include "chrome/browser/web_applications/web_app_helpers.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/pref_names.h"
@@ -88,6 +87,7 @@
 #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
 #include "chromeos/ash/experiences/arc/arc_prefs.h"
 #include "chromeos/ash/experiences/arc/arc_util.h"
+#include "chromeos/ash/experiences/settings_ui/settings_app_manager.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "components/account_id/account_id.h"
@@ -921,16 +921,24 @@
     return;
   }
 
+  std::string sub_page;
+  std::optional<ash::SettingsAppManager::EntryPoint> entry_point;
   if (app_type == apps::AppType::kWeb ||
       app_type == apps::AppType::kSystemWeb) {
-    chrome::ShowAppManagementPage(
-        profile_, app_id,
-        ash::settings::AppManagementEntryPoint::kShelfContextMenuAppInfoWebApp);
+    sub_page = ash::SettingsAppManager::CreateAppManagementPagePath(app_id);
+    entry_point =
+        ash::SettingsAppManager::EntryPoint::kShelfContextMenuAppInfoWebApp;
   } else {
-    chrome::ShowAppManagementPage(profile_, app_id,
-                                  ash::settings::AppManagementEntryPoint::
-                                      kShelfContextMenuAppInfoChromeApp);
+    sub_page = ash::SettingsAppManager::CreateAppManagementPagePath(app_id);
+    entry_point =
+        ash::SettingsAppManager::EntryPoint::kShelfContextMenuAppInfoChromeApp;
   }
+
+  const user_manager::User* user =
+      ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile_);
+  ash::SettingsAppManager::Get()->Open(
+      *user, ash::SettingsAppManager::OpenParams{.sub_page = sub_page,
+                                                 .entry_point = entry_point});
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.cc b/chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.cc
index 3653155..6f02c3a 100644
--- a/chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.cc
+++ b/chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.cc
@@ -50,7 +50,6 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/contents_web_view.h"
 #include "chrome/browser/ui/webui/ash/settings/pref_names.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/cryptohome/system_salt_getter.h"
 #include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
diff --git a/chrome/browser/ui/browser_element_identifiers.cc b/chrome/browser/ui/browser_element_identifiers.cc
index d56d771..557b3d16 100644
--- a/chrome/browser/ui/browser_element_identifiers.cc
+++ b/chrome/browser/ui/browser_element_identifiers.cc
@@ -172,7 +172,6 @@
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabDeclutterButtonElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabSearchBubbleElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabSearchButtonElementId);
-DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabSearchContainerElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabStripActionContainerElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabStripComboButtonElementId);
 DEFINE_ELEMENT_IDENTIFIER_VALUE(kTabStripElementId);
diff --git a/chrome/browser/ui/browser_element_identifiers.h b/chrome/browser/ui/browser_element_identifiers.h
index 6db6f6ef..947cd63 100644
--- a/chrome/browser/ui/browser_element_identifiers.h
+++ b/chrome/browser/ui/browser_element_identifiers.h
@@ -215,7 +215,6 @@
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabDeclutterButtonElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabSearchBubbleElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabSearchButtonElementId);
-DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabSearchContainerElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabStripActionContainerElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabStripComboButtonElementId);
 DECLARE_ELEMENT_IDENTIFIER_VALUE(kTabStripElementId);
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index e05fb06..959fe88 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -71,10 +71,8 @@
 #if BUILDFLAG(IS_CHROMEOS)
 #include "ash/constants/ash_features.h"
 #include "ash/constants/webui_url_constants.h"
-#include "ash/webui/settings/public/constants/routes.mojom.h"
 #include "ash/webui/settings/public/constants/routes_util.h"
 #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
-#include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #else
 #include "chrome/browser/ui/signin/signin_view_controller.h"
 #endif
@@ -682,24 +680,6 @@
                    GURL(data_sharing::features::kActivityLogsURL.Get()));
 }
 
-#if BUILDFLAG(IS_CHROMEOS)
-void ShowAppManagementPage(Profile* profile,
-                           const std::string& app_id,
-                           ash::settings::AppManagementEntryPoint entry_point) {
-  // This histogram is also declared and used at chrome/browser/resources/
-  // settings/chrome_os/os_apps_page/app_management_page/constants.js.
-  constexpr char kAppManagementEntryPointsHistogramName[] =
-      "AppManagement.EntryPoints";
-
-  base::UmaHistogramEnumeration(kAppManagementEntryPointsHistogramName,
-                                entry_point);
-  std::string sub_page = base::StrCat(
-      {chromeos::settings::mojom::kAppDetailsSubpagePath, "?id=", app_id});
-  chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(profile,
-                                                               sub_page);
-}
-#endif  // BUILDFLAG(IS_CHROMEOS)
-
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 void ShowWebAppSettingsImpl(Browser* browser,
                             Profile* profile,
diff --git a/chrome/browser/ui/chrome_pages.h b/chrome/browser/ui/chrome_pages.h
index 78cbb21..b2b7d1cc 100644
--- a/chrome/browser/ui/chrome_pages.h
+++ b/chrome/browser/ui/chrome_pages.h
@@ -23,10 +23,6 @@
 #include "chrome/browser/signin/signin_promo.h"
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS)
-#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h"
-#endif
-
 namespace apps {
 enum class LaunchSource;
 }
@@ -158,12 +154,6 @@
 // Shows the enterprise management info page in a browser tab.
 void ShowEnterpriseManagementPageInTabbedBrowser(Browser* browser);
 
-#if BUILDFLAG(IS_CHROMEOS)
-void ShowAppManagementPage(Profile* profile,
-                           const std::string& app_id,
-                           ash::settings::AppManagementEntryPoint entry_point);
-#endif
-
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 // Show chrome://app-settings/<app-id> page.
 void ShowWebAppSettings(Browser* browser,
diff --git a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
index 5896131a..b745a207 100644
--- a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
+++ b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
@@ -291,6 +291,7 @@
   html_source->AddBoolean(
       "enableKeyboardSelection",
       lens::features::IsLensOverlayKeyboardSelectionEnabled());
+  html_source->AddBoolean("enableMultiRegionSelection", false);
 
   LensOverlayController& controller = GetLensOverlayController();
   html_source->AddDouble("invocationTime",
diff --git a/chrome/browser/ui/omnibox/omnibox_context_menu_controller_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_context_menu_controller_browsertest.cc
index a79aa74..56e8d04 100644
--- a/chrome/browser/ui/omnibox/omnibox_context_menu_controller_browsertest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_context_menu_controller_browsertest.cc
@@ -61,6 +61,7 @@
            {omnibox::kShowToolsAndModels.name, "true"}}},
          {omnibox::kWebUIOmniboxPopup, {}}},
         /*disabled_features=*/{omnibox::kAimServerEligibilityEnabled,
+                               omnibox::kAimFuseboxEligibilityCheckEnabled,
                                omnibox::kAimUsePecApi});
   }
 
@@ -291,7 +292,8 @@
            {omnibox::kShowToolsAndModels.name, "true"}}},
          {omnibox::kWebUIOmniboxPopup, {}},
          {omnibox::kAimUsePecApi, {}}},
-        /*disabled_features=*/{omnibox::kAimServerEligibilityEnabled});
+        /*disabled_features=*/{omnibox::kAimServerEligibilityEnabled,
+                               omnibox::kAimFuseboxEligibilityCheckEnabled});
   }
 
   OmniboxContextMenuControllerPecBrowserTest(
diff --git a/chrome/browser/ui/omnibox/omnibox_next_features.cc b/chrome/browser/ui/omnibox/omnibox_next_features.cc
index 106669f..bb6e543 100644
--- a/chrome/browser/ui/omnibox/omnibox_next_features.cc
+++ b/chrome/browser/ui/omnibox/omnibox_next_features.cc
@@ -204,7 +204,7 @@
   }
 
   auto* aim_service = AimEligibilityServiceFactory::GetForProfile(profile);
-  return aim_service && aim_service->IsAimEligible();
+  return aim_service && aim_service->IsFuseboxEligible();
 }
 
 bool IsContentSharingEnabled(
diff --git a/chrome/browser/ui/omnibox/omnibox_next_features_unittest.cc b/chrome/browser/ui/omnibox/omnibox_next_features_unittest.cc
index f207b39a..74c7648 100644
--- a/chrome/browser/ui/omnibox/omnibox_next_features_unittest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_next_features_unittest.cc
@@ -259,6 +259,7 @@
         is_aim_eligible_(is_aim_eligible) {}
 
   bool IsAimEligible() const override { return is_aim_eligible_; }
+  bool IsFuseboxEligible() const override { return is_aim_eligible_; }
   bool IsAimAllowedByDse() const override { return is_aim_eligible_; }
 
  private:
diff --git a/chrome/browser/ui/page_action/page_action_icon_type.cc b/chrome/browser/ui/page_action/page_action_icon_type.cc
index 0f04c36..0d3b242 100644
--- a/chrome/browser/ui/page_action/page_action_icon_type.cc
+++ b/chrome/browser/ui/page_action/page_action_icon_type.cc
@@ -60,6 +60,7 @@
   // Page actions on the new framework that don't have an implementation on the legacy path
   // and don't have a feature param.
   switch (page_action) {
+    case PageActionIconType::kGlic:
     case PageActionIconType::kLensOverlay:
     case PageActionIconType::kMemorySaver:
     case PageActionIconType::kTranslate:
diff --git a/chrome/browser/ui/page_action/page_action_icon_type.h b/chrome/browser/ui/page_action/page_action_icon_type.h
index 57aa911f..8a3c01d6 100644
--- a/chrome/browser/ui/page_action/page_action_icon_type.h
+++ b/chrome/browser/ui/page_action/page_action_icon_type.h
@@ -55,7 +55,8 @@
   kRecordReplay = 39,
   kIndigo = 40,
   kFederation = 41,
-  kMaxValue = kFederation,
+  kGlic = 42,
+  kMaxValue = kGlic,
 };
 // LINT.ThenChange(//tools/metrics/histograms/metadata/page/enums.xml:PageActionIconType)
 
@@ -93,6 +94,7 @@
 static_assert(static_cast<int>(PageActionIconType::kRecordReplay) == 39);
 static_assert(static_cast<int>(PageActionIconType::kIndigo) == 40);
 static_assert(static_cast<int>(PageActionIconType::kFederation) == 41);
+static_assert(static_cast<int>(PageActionIconType::kGlic) == 42);
 
 // Returns a bool indicating whether the given page action type has been
 // migrated to the new framework, which is based on ActionItems instead of
diff --git a/chrome/browser/ui/page_info/BUILD.gn b/chrome/browser/ui/page_info/BUILD.gn
index a33b10bd..dbbd8c3 100644
--- a/chrome/browser/ui/page_info/BUILD.gn
+++ b/chrome/browser/ui/page_info/BUILD.gn
@@ -63,10 +63,7 @@
       "//chrome/browser/web_applications",
     ]
     if (is_chromeos) {
-      deps += [
-        "//chrome/browser/ash/floating_sso",
-        "//chrome/browser/ui/webui/ash/settings/app_management",
-      ]
+      deps += [ "//chrome/browser/ash/floating_sso" ]
     }
   }
   deps += [
diff --git a/chrome/browser/ui/page_info/chrome_page_info_delegate.cc b/chrome/browser/ui/page_info/chrome_page_info_delegate.cc
index eb8590d..56e31e0 100644
--- a/chrome/browser/ui/page_info/chrome_page_info_delegate.cc
+++ b/chrome/browser/ui/page_info/chrome_page_info_delegate.cc
@@ -83,7 +83,6 @@
 #include "chrome/browser/ash/floating_sso/floating_sso_service_factory.h"
 #include "chrome/browser/smart_card/smart_card_permission_context.h"
 #include "chrome/browser/smart_card/smart_card_permission_context_factory.h"
-#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h"
 #endif
 
 namespace {
diff --git a/chrome/browser/ui/settings_window_manager_chromeos.cc b/chrome/browser/ui/settings_window_manager_chromeos.cc
index ae8a573..5b7b3669 100644
--- a/chrome/browser/ui/settings_window_manager_chromeos.cc
+++ b/chrome/browser/ui/settings_window_manager_chromeos.cc
@@ -13,6 +13,7 @@
 #include "ash/webui/settings/public/constants/routes_util.h"
 #include "ash/webui/system_apps/public/system_web_app_type.h"
 #include "ash/wm/window_properties.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
@@ -102,6 +103,11 @@
 
 void SettingsWindowManager::Open(const user_manager::User& user,
                                  OpenParams params) {
+  if (params.entry_point.has_value()) {
+    base::UmaHistogramEnumeration("AppManagement.EntryPoints",
+                                  params.entry_point.value());
+  }
+
   Profile* profile = Profile::FromBrowserContext(
       ash::BrowserContextHelper::Get()->GetBrowserContextByUser(&user));
 
diff --git a/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/BUILD.gn b/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/BUILD.gn
index 4d3592f..f09d8ec 100644
--- a/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/BUILD.gn
+++ b/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/BUILD.gn
@@ -64,6 +64,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "pin_infobar_controller_crash_unittest.cc",
     "pin_infobar_controller_unittest.cc",
     "pin_infobar_delegate_unittest.cc",
     "pin_infobar_prefs_unittest.cc",
diff --git a/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller.cc b/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller.cc
index 423467c..345152f 100644
--- a/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller.cc
+++ b/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller.cc
@@ -1,4 +1,4 @@
-// Copyright 2025 The Chromium Authors
+// Copyright 2026 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -142,8 +142,18 @@
   // Show the pin-to-taskbar infobar.
   content::WebContents* web_contents =
       browser_->GetTabStripModel()->GetActiveWebContents();
+  if (!web_contents) {
+    std::move(done_callback).Run(false);
+    return;
+  }
+
   infobar_manager_ =
       infobars::ContentInfoBarManager::FromWebContents(web_contents);
+  if (!infobar_manager_) {
+    std::move(done_callback).Run(false);
+    return;
+  }
+
   infobar_manager_->AddObserver(this);
   infobar_ = PinInfoBarDelegate::Create(infobar_manager_);
   SetInfoBarShownRecently();
diff --git a/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller_crash_unittest.cc b/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller_crash_unittest.cc
new file mode 100644
index 0000000..beab4e0
--- /dev/null
+++ b/chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller_crash_unittest.cc
@@ -0,0 +1,84 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h"
+#include "chrome/browser/ui/startup/default_browser_prompt/pin_infobar/pin_infobar_controller.h"
+#include "chrome/browser/ui/tabs/tab_model.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
+#include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_renderer_host.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace default_browser {
+
+class PinInfoBarControllerCrashTest : public testing::Test {
+ protected:
+  PinInfoBarControllerCrashTest()
+      : profile_(std::make_unique<TestingProfile>()),
+        delegate_(std::make_unique<TestTabStripModelDelegate>()),
+        tab_strip_model_(
+            std::make_unique<TabStripModel>(delegate_.get(), profile_.get())),
+        browser_window_interface_(
+            std::make_unique<MockBrowserWindowInterface>()) {
+    feature_list_.InitAndEnableFeature(features::kOfferPinToTaskbarInfoBar);
+
+    ON_CALL(*browser_window_interface_, GetTabStripModel())
+        .WillByDefault(::testing::Return(tab_strip_model_.get()));
+    ON_CALL(*browser_window_interface_, GetProfile())
+        .WillByDefault(::testing::Return(profile_.get()));
+    ON_CALL(*browser_window_interface_, GetType())
+        .WillByDefault(
+            ::testing::Return(BrowserWindowInterface::Type::TYPE_NORMAL));
+    delegate_->SetBrowserWindowInterface(browser_window_interface_.get());
+  }
+
+  ~PinInfoBarControllerCrashTest() override {
+    delegate_->SetBrowserWindowInterface(nullptr);
+  }
+
+  MockBrowserWindowInterface* browser_window_interface() {
+    return browser_window_interface_.get();
+  }
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+  base::test::ScopedFeatureList feature_list_;
+  ChromeLayoutProvider layout_provider_;
+  content::RenderViewHostTestEnabler rvh_test_enabler_;
+  const std::unique_ptr<TestingProfile> profile_;
+  const std::unique_ptr<TestTabStripModelDelegate> delegate_;
+  const std::unique_ptr<TabStripModel> tab_strip_model_;
+  const std::unique_ptr<MockBrowserWindowInterface> browser_window_interface_;
+  const tabs::TabModel::PreventFeatureInitializationForTesting prevent_;
+};
+
+// Reproduce the crash in b/491643039: OnShouldOfferToPinResult called when
+// there are no active web contents.
+TEST_F(PinInfoBarControllerCrashTest, ReproduceCrashWithNoWebContents) {
+  PinInfoBarController controller(browser_window_interface());
+
+  base::RunLoop run_loop;
+  bool callback_called = false;
+  controller.OnShouldOfferToPinResult(
+      base::BindLambdaForTesting([&](bool result) {
+        callback_called = true;
+        EXPECT_FALSE(result);
+        run_loop.Quit();
+      }),
+      /*should_offer_to_pin=*/true);
+  run_loop.Run();
+
+  EXPECT_TRUE(callback_called);
+}
+
+}  // namespace default_browser
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc
index ba1f46d2..31ed121 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc
@@ -553,6 +553,7 @@
 
 // Following definitions are equal to content::PrerenderFinalStatus.
 constexpr int kFinalStatusActivated = 0;
+constexpr int kTriggerDestroyed = 16;
 constexpr int kPrerenderFailedDuringPrefetch = 86;
 
 // Following definitions are equal to content::PrefetchStatus.
@@ -974,6 +975,90 @@
       kPrerenderFailedDuringPrefetch, 1);
 }
 
+// Prefetch and prerender triggered, prerender cancelled, and then prerender
+// triggered.
+//
+// Scenario:
+//
+// - mouseenter to a bookmark button.
+// - mousedown
+//   - Prefetch A and prerender A' are triggered.
+//   - A' matched to A.
+// - mouseleave
+//   - Prerender is cancelled.
+// - mouseup outside the button.
+// - mouseenter
+// - mousedown
+//   - Prerender B' is triggered.
+//   - B' matched to A.
+// - mouseup
+//   - Navigate with B' activation.
+IN_PROC_BROWSER_TEST_F(
+    PreloadBookmarkBarPrefetchEnabledPrerenderEnabledNavigationTest,
+    PreloadsTriggered_PrerenderCancelled_PrerenderTriggered) {
+  StartServers();
+  base::HistogramTester histogram_tester;
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), https_test_server()->GetURL("/empty.html")));
+
+  GURL preload_url = https_test_server()->GetURL("/empty.html?preload");
+
+  CreateBookmarkButton(preload_url);
+  views::LabelButton* button = GetBookmarkButton(0);
+
+  gfx::Point center(10, 10);
+
+  // Trigger prefetch and prerender.
+  button->OnMouseEntered(ui::MouseEvent(ui::EventType::kMouseEntered, center,
+                                        center, ui::EventTimeForNow(),
+                                        /*flags=*/ui::EF_NONE,
+                                        /*changed_button_flags=*/ui::EF_NONE));
+  button->OnMousePressed(ui::MouseEvent(
+      ui::EventType::kMousePressed, center, center, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+  content::test::PrerenderTestHelper::WaitForPrerenderLoadCompletion(
+      *GetActiveWebContents(), preload_url);
+  // Cancel prerender.
+  button->OnMouseExited(ui::MouseEvent(ui::EventType::kMouseExited, center,
+                                       center, ui::EventTimeForNow(),
+                                       /*flags=*/ui::EF_NONE,
+                                       /*changed_button_flags=*/ui::EF_NONE));
+  histogram_tester.ExpectUniqueSample(
+      "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_BookmarkBar",
+      kTriggerDestroyed, 1);
+  // mouseup outside button
+
+  // Trigger prerender again.
+  button->OnMouseEntered(ui::MouseEvent(ui::EventType::kMouseEntered, center,
+                                        center, ui::EventTimeForNow(),
+                                        /*flags=*/ui::EF_NONE,
+                                        /*changed_button_flags=*/ui::EF_NONE));
+  content::test::PrerenderHostObserver prerender_observer(
+      *GetActiveWebContents(), preload_url);
+  button->OnMousePressed(ui::MouseEvent(
+      ui::EventType::kMousePressed, center, center, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+  // Navigate.
+  button->OnMouseReleased(ui::MouseEvent(
+      ui::EventType::kMouseReleased, center, center, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+  prerender_observer.WaitForActivation();
+
+  EXPECT_EQ(1, prerender_helper().GetRequestCount(preload_url));
+  histogram_tester.ExpectUniqueSample("Preloading.Prefetch.PrefetchStatus",
+                                      kPrefetchResponseUsed, 1);
+  histogram_tester.ExpectTotalCount(
+      "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_BookmarkBar",
+      2);
+  histogram_tester.ExpectBucketCount(
+      "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_BookmarkBar",
+      kTriggerDestroyed, 1);
+  histogram_tester.ExpectBucketCount(
+      "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_BookmarkBar",
+      kFinalStatusActivated, 1);
+}
+
 namespace {
 
 class BookmarkBarTest : public BookmarkBarTestBase {
diff --git a/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.cc b/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.cc
index f58acb6..7262a66 100644
--- a/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.cc
+++ b/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.cc
@@ -34,7 +34,6 @@
 #include "chrome/browser/ui/views/tabs/shared/tab_strip_flat_edge_button.h"
 #include "chrome/browser/ui/views/tabs/tab_hover_card_controller.h"
 #include "chrome/browser/ui/views/tabs/tab_search_button.h"
-#include "chrome/browser/ui/views/tabs/tab_search_container.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/browser/ui/views/tabs/tab_strip_action_container.h"
 #include "chrome/browser/ui/views/tabs/tab_strip_control_button.h"
@@ -273,8 +272,8 @@
                                  views::LayoutAlignment::kCenter);
   }
 
-  // Add and configure the TabSearchContainer and TabStripComboButton.
-  std::unique_ptr<TabSearchContainer> tab_search_container;
+  // Add and configure the TabSearchButton and TabStripComboButton.
+  std::unique_ptr<TabSearchButton> tab_search_button;
   std::unique_ptr<TabStripActionContainer> tab_strip_action_container;
   if (browser &&
       (browser->GetType() == BrowserWindowInterface::Type::TYPE_NORMAL)) {
@@ -286,22 +285,22 @@
                                               views::LayoutAlignment::kStart);
     } else if (!base::FeatureList::IsEnabled(
                    tabs::kHorizontalTabStripComboButton)) {
-      tab_search_container = std::make_unique<TabSearchContainer>(
-          render_tab_search_before_tab_strip_, this, tab_strip_);
-      tab_search_container->SetProperty(views::kCrossAxisAlignmentKey,
-                                        views::LayoutAlignment::kCenter);
+      tab_search_button =
+          std::make_unique<TabSearchButton>(browser, Edge::kNone, Edge::kNone);
+      tab_search_button->SetProperty(views::kCrossAxisAlignmentKey,
+                                     views::LayoutAlignment::kCenter);
     }
   }
 
-  if (tab_search_container && render_tab_search_before_tab_strip_) {
-    tab_search_container->SetPaintToLayer();
-    tab_search_container->layer()->SetFillsBoundsOpaquely(false);
+  if (tab_search_button && render_tab_search_before_tab_strip_) {
+    tab_search_button->SetPaintToLayer();
+    tab_search_button->layer()->SetFillsBoundsOpaquely(false);
 
-    tab_search_container_ = AddChildView(std::move(tab_search_container));
+    tab_search_button_ = AddChildView(std::move(tab_search_button));
 
     // Inset between the tabsearch and tabstrip should be reduced to account for
     // extra spacing.
-    tab_search_container_->SetProperty(views::kViewIgnoredByLayoutKey, true);
+    tab_search_button_->SetProperty(views::kViewIgnoredByLayoutKey, true);
   }
 
   // Allow the |tab_strip_| to grow into the free space available in
@@ -337,9 +336,9 @@
 
   SetProperty(views::kElementIdentifierKey, kTabStripRegionElementId);
 
-  if (browser && tab_search_container && !render_tab_search_before_tab_strip_) {
-    tab_search_container_ = AddChildView(std::move(tab_search_container));
-    tab_search_container_->SetProperty(
+  if (browser && tab_search_button && !render_tab_search_before_tab_strip_) {
+    tab_search_button_ = AddChildView(std::move(tab_search_button));
+    tab_search_button_->SetProperty(
         views::kMarginsKey,
         gfx::Insets::TLBR(0, 0, 0,
                           GetLayoutConstant(LayoutConstant::kTabStripPadding)));
@@ -365,8 +364,8 @@
   if (unfocus_button_) {
     RemoveChildViewT(std::exchange(unfocus_button_, nullptr));
   }
-  if (tab_search_container_) {
-    RemoveChildViewT(std::exchange(tab_search_container_, nullptr));
+  if (tab_search_button_) {
+    RemoveChildViewT(std::exchange(tab_search_button_, nullptr));
   }
 }
 
@@ -380,8 +379,8 @@
     return false;
   }
 
-  if (render_tab_search_before_tab_strip_ && tab_search_container_ &&
-      IsHitInView(tab_search_container_, point)) {
+  if (render_tab_search_before_tab_strip_ && tab_search_button_ &&
+      IsHitInView(tab_search_button_, point)) {
     return false;
   }
 
@@ -431,8 +430,8 @@
     children.emplace_back(unfocus_button_.get());
   }
 
-  if (tab_search_container_) {
-    children.emplace_back(tab_search_container_.get());
+  if (tab_search_button_) {
+    children.emplace_back(tab_search_button_.get());
   }
 
   if (tab_strip_action_container_) {
@@ -454,9 +453,9 @@
     return;
   }
 
-  const bool tab_search_container_before_tab_strip =
-      tab_search_container_ && render_tab_search_before_tab_strip_;
-  if (tab_search_container_before_tab_strip ||
+  const bool tab_search_button_before_tab_strip =
+      tab_search_button_ && render_tab_search_before_tab_strip_;
+  if (tab_search_button_before_tab_strip ||
       (unfocus_button_ && unfocus_button_->GetVisible()) || combo_button_) {
     UpdateTabStripMargin();
   }
@@ -464,9 +463,9 @@
   LayoutSuperclass<views::AccessiblePaneView>(this);
 
   int leading_offset = 0;
-  if (tab_search_container_before_tab_strip) {
-    AdjustViewBoundsRect(tab_search_container_, leading_offset);
-    leading_offset += tab_search_container_->GetPreferredSize().width() +
+  if (tab_search_button_before_tab_strip) {
+    AdjustViewBoundsRect(tab_search_button_, leading_offset);
+    leading_offset += tab_search_button_->GetPreferredSize().width() +
                       GetLayoutConstant(LayoutConstant::kTabStripPadding);
   }
 
@@ -568,11 +567,11 @@
                          : AccessiblePaneView::GetDefaultFocusableChild();
 }
 
-TabStripFlatEdgeButton* HorizontalTabStripRegionView::GetTabSearchButton() {
+views::Button* HorizontalTabStripRegionView::GetTabSearchButton() {
   if (combo_button_) {
     return combo_button_->end_button();
   }
-  return nullptr;
+  return tab_search_button_;
 }
 
 views::LabelButton* HorizontalTabStripRegionView::GetGlicButton() {
@@ -687,8 +686,8 @@
   if (unfocus_button_ && unfocus_button_->GetVisible()) {
     return true;
   }
-  if (tab_search_container_ && render_tab_search_before_tab_strip_ &&
-      tab_search_container_->GetVisible()) {
+  if (tab_search_button_ && render_tab_search_before_tab_strip_ &&
+      tab_search_button_->GetVisible()) {
     return true;
   }
   return false;
@@ -733,14 +732,8 @@
   if (unfocus_button_) {
     UpdateBorderInsetsIfNeeded(unfocus_button_, border_insets);
   }
-  if (tab_search_container_) {
-    UpdateBorderInsetsIfNeeded(tab_search_container_->tab_search_button(),
-                               border_insets);
-
-    if (tab_search_container_->auto_tab_group_button()) {
-      UpdateBorderInsetsIfNeeded(tab_search_container_->auto_tab_group_button(),
-                                 border_insets);
-    }
+  if (tab_search_button_) {
+    UpdateBorderInsetsIfNeeded(tab_search_button_, border_insets);
   }
 }
 
@@ -778,11 +771,11 @@
   std::optional<int> tab_strip_left_margin;
   int current_leading_width = 0;
 
-  if (tab_search_container_ && render_tab_search_before_tab_strip_) {
-    // The `tab_search_container_` is being laid out manually.
-    CHECK(tab_search_container_->GetProperty(views::kViewIgnoredByLayoutKey));
+  if (tab_search_button_ && render_tab_search_before_tab_strip_) {
+    // The `tab_search_button_` is being laid out manually.
+    CHECK(tab_search_button_->GetProperty(views::kViewIgnoredByLayoutKey));
     current_leading_width +=
-        tab_search_container_->GetPreferredSize().width() +
+        tab_search_button_->GetPreferredSize().width() +
         GetLayoutConstant(LayoutConstant::kTabStripPadding);
   }
 
diff --git a/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.h b/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.h
index d109873..239e34a7 100644
--- a/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.h
+++ b/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.h
@@ -9,7 +9,6 @@
 #include "build/buildflag.h"
 #include "chrome/browser/ui/tabs/tab_data.h"
 #include "chrome/browser/ui/views/frame/tab_strip_region_view.h"
-#include "chrome/browser/ui/views/tabs/tab_search_container.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/common/buildflags.h"
 #include "ui/base/metadata/metadata_header_macros.h"
@@ -31,7 +30,6 @@
 class TabStripScrollContainer;
 class TabSearchPositionMetricsLogger;
 class TabStripControlButton;
-class TabStripFlatEdgeButton;
 
 // Container for the tabstrip and the other views sharing space with it -
 // with the exception of the caption buttons.
@@ -85,7 +83,7 @@
 
   TabStrip* tab_strip() { return tab_strip_; }
 
-  TabStripFlatEdgeButton* GetTabSearchButton();
+  views::Button* GetTabSearchButton();
   TabStripComboButton* GetComboButton() { return combo_button_; }
 
   views::LabelButton* GetGlicButton();
@@ -128,12 +126,12 @@
 
  private:
   // Updates the border padding for `new_tab_button_` and
-  // `tab_search_container_`, if present.  This should be called whenever any
+  // `tab_search_button_`, if present.  This should be called whenever any
   // input of the computation of the border's sizing changes.
   void UpdateButtonBorders();
 
   // Updates the left and right margins for the tab strip. This should be
-  // called whenever `tab_search_container_` changes size, if
+  // called whenever `tab_search_button_` changes size, if
   // `render_tab_search_before_tab_strip_` is true.
   void UpdateTabStripMargin();
 
@@ -151,7 +149,7 @@
   raw_ptr<TabStripScrollContainer> tab_strip_scroll_container_ = nullptr;
   raw_ptr<TabStripComboButton> combo_button_ = nullptr;
   raw_ptr<views::Button> new_tab_button_ = nullptr;
-  raw_ptr<TabSearchContainer> tab_search_container_ = nullptr;
+  raw_ptr<TabSearchButton> tab_search_button_ = nullptr;
   raw_ptr<TabStripControlButton> unfocus_button_ = nullptr;
 
   // On some platforms for Chrome Refresh, the TabSearchButton should be
diff --git a/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view_interactive_uitest.cc b/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view_interactive_uitest.cc
index afe31d90..590e787 100644
--- a/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view_interactive_uitest.cc
@@ -50,11 +50,6 @@
 
   TabStrip* tab_strip() { return tab_strip_region_view()->tab_strip(); }
 
-  TabSearchContainer* tab_search_container() {
-    return BrowserElementsViews::From(browser())->GetViewAs<TabSearchContainer>(
-        kTabSearchContainerElementId);
-  }
-
   TabSearchButton* tab_search_button() {
     return BrowserElementsViews::From(browser())->GetViewAs<TabSearchButton>(
         kTabSearchButtonElementId);
@@ -249,25 +244,25 @@
 }
 
 IN_PROC_BROWSER_TEST_F(HorizontalTabStripRegionViewBrowserTest,
-                       DefaultTestSearchContainerIsEndAligned) {
+                       DefaultTabSearchButtonIsEndAligned) {
   if (tabs::GetTabSearchPosition(browser()) ==
       tabs::TabSearchPosition::kLeadingHorizontalTabstrip) {
-    // The TabSearchContainer is calculated as controls padding away from the
+    // The TabSearchButton is calculated as controls padding away from the
     // first tab (not including bottom corner radius)
-    const int tab_search_container_expected_end =
+    const int tab_search_button_expected_end =
         tab_strip_region_view()->tab_strip()->x() +
         TabStyle::Get()->GetBottomCornerRadius() -
         GetLayoutConstant(LayoutConstant::kTabStripPadding);
 
-    EXPECT_EQ(tab_search_container()->bounds().right(),
-              tab_search_container_expected_end);
+    EXPECT_EQ(tab_search_button()->bounds().right(),
+              tab_search_button_expected_end);
   } else if (tabs::GetTabSearchPosition(browser()) !=
              tabs::TabSearchPosition::kToolbarButton) {
-    const int tab_search_container_expected_end =
+    const int tab_search_button_expected_end =
         tab_strip_region_view()->GetLocalBounds().right() -
         GetLayoutConstant(LayoutConstant::kTabStripPadding);
-    EXPECT_EQ(tab_search_container()->bounds().right(),
-              tab_search_container_expected_end);
+    EXPECT_EQ(tab_search_button()->bounds().right(),
+              tab_search_button_expected_end);
   }
 }
 
diff --git a/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl.cc b/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl.cc
index 3bcc8646..9dd2915 100644
--- a/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl.cc
+++ b/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl.cc
@@ -268,8 +268,8 @@
       layout.force_top_container_to_top =
           remainder < min_toolbar_height_side_panel_width;
 
-      // If still allowing toolbar height, clamp the sidepanel based on what the
-      // toolbar actually supports.
+      // If still allowing toolbar height, clamp the side panel based on what
+      // the toolbar actually supports.
       if (!layout.force_top_container_to_top) {
         preferred_toolbar_height_side_panel_width =
             std::min(preferred_toolbar_height_side_panel_width, remainder);
@@ -402,7 +402,8 @@
 gfx::Size BrowserViewTabbedLayoutImpl::GetMinimumMainAreaSize(
     const BrowserLayoutParams& params) const {
   gfx::Size toolbar_size = views().toolbar->GetMinimumSize();
-  if (GetTabStripType() == TabStripType::kVertical) {
+  const auto tab_strip_type = GetTabStripType();
+  if (tab_strip_type == TabStripType::kVertical) {
     toolbar_size.Enlarge(GetExclusionWidth(params), 0);
   }
   const gfx::Size bookmark_bar_size =
@@ -418,14 +419,23 @@
           ? views().contents_height_side_panel->GetMinimumSize()
           : gfx::Size();
 
-  const int width = std::max({toolbar_size.width(), bookmark_bar_size.width(),
-                              infobar_container_size.width(),
-                              contents_height_side_panel_size.width() +
-                                  kContentsContainerMinimumWidth});
+  int width = std::max({toolbar_size.width(), bookmark_bar_size.width(),
+                        infobar_container_size.width(),
+                        contents_height_side_panel_size.width() +
+                            kContentsContainerMinimumWidth});
   const int height = toolbar_size.height() + bookmark_bar_size.height() +
                      infobar_container_size.height() +
                      std::max(contents_size.height(),
                               contents_height_side_panel_size.height());
+
+  if (tab_strip_type == TabStripType::kHorizontal &&
+      IsParentedToAndVisible(views().horizontal_tab_strip_region_view,
+                             views().browser_view)) {
+    // When toolbar-height side panel is present, ensure that the shadow box
+    // padding is included in the size calculation.
+    width += GetLayoutConstant(LayoutConstant::kToolbarHeightSidePanelInset);
+  }
+
   return gfx::Size(width, height);
 }
 
diff --git a/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl_interactive_uitest.cc b/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl_interactive_uitest.cc
index 77a2c4c..bdff164 100644
--- a/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl_interactive_uitest.cc
+++ b/chrome/browser/ui/views/frame/layout/browser_view_tabbed_layout_impl_interactive_uitest.cc
@@ -60,7 +60,10 @@
   static constexpr char kBaselineCl[] = "7635098";
 
   explicit BrowserViewTabbedLayoutImplUiTest(bool disable_animations = true)
-      : disable_animations_(disable_animations) {}
+      : render_mode_resetter_(gfx::AnimationTestApi::SetRichAnimationRenderMode(
+            disable_animations
+                ? gfx::Animation::RichAnimationRenderMode::FORCE_DISABLED
+                : gfx::Animation::RichAnimationRenderMode::FORCE_ENABLED)) {}
 
   ~BrowserViewTabbedLayoutImplUiTest() override = default;
 
@@ -70,7 +73,6 @@
     feature_list_.InitWithFeatures(
         {tabs::kVerticalTabs, features::kSidePanelFlyoverAnimation}, {});
     set_open_about_blank_on_browser_launch(true);
-    gfx::Animation::SetPrefersReducedMotionForTesting(disable_animations_);
     InteractiveBrowserTest::SetUp();
   }
 
@@ -84,7 +86,9 @@
     chrome::AddTabAt(browser(), GURL("about:blank"), -1, true);
   }
 
-  auto ReplaceAndShowSidePanel(SidePanelEntry::PanelType type) {
+  auto ReplaceAndShowSidePanel(
+      SidePanelEntry::PanelType type,
+      std::optional<int> force_preferred_width = std::nullopt) {
     return Steps(
         Do([this, type]() {
           // Note: there is a registry at browser level and one per tab; we want
@@ -111,8 +115,71 @@
               SidePanelEntry::Id::kCustomizeChrome);
         }),
         WaitForShow(kSidePanelElementId),
+        If(
+            [force_preferred_width] {
+              return force_preferred_width.has_value();
+            },
+            Then(WithView(kSidePanelElementId,
+                          [force_preferred_width](SidePanel* side_panel) {
+                            side_panel->SetPanelWidth(*force_preferred_width);
+                            side_panel->InvalidateLayout();
+                          }),
+                 Do([this] { RunScheduledLayouts(); }))),
         // Wait for the indicator to show.
-        WaitForShow(kSidePanelTestElement));
+        WaitForShow(kSidePanelTestElement),
+        // Ensure that the browser gets a full layout.
+        Do([this] { RunScheduledLayouts(); }));
+  }
+
+  using Bounds = base::RefCountedData<gfx::Rect>;
+
+  auto VerifyLayout() {
+    auto browser_bounds = base::MakeRefCounted<Bounds>();
+    auto side_panel_bounds = base::MakeRefCounted<Bounds>();
+    auto content_bounds = base::MakeRefCounted<Bounds>();
+    auto toolbar_bounds = base::MakeRefCounted<Bounds>();
+    return Steps(
+        WithElement(kBrowserViewElementId,
+                    [browser_bounds](ui::TrackedElement* el) {
+                      browser_bounds->data = el->GetScreenBounds();
+                    }),
+        WithElement(kSidePanelElementId,
+                    [side_panel_bounds](ui::TrackedElement* el) {
+                      side_panel_bounds->data = el->GetScreenBounds();
+                    }),
+        WithElement(kMultiContentsViewElementId,
+                    [content_bounds](ui::TrackedElement* el) {
+                      content_bounds->data = el->GetScreenBounds();
+                    }),
+        WithElement(ToolbarView::kToolbarElementId,
+                    [toolbar_bounds](ui::TrackedElement* el) {
+                      toolbar_bounds->data = el->GetScreenBounds();
+                    }),
+        Check(
+            [=] {
+              return browser_bounds->data.Contains(side_panel_bounds->data);
+            },
+            "Side Panel in browser"),
+        Check(
+            [=] {
+              return !side_panel_bounds->data.Intersects(toolbar_bounds->data);
+            },
+            "Side Panel does not overlap Toolbar"),
+        Check(
+            [=] { return browser_bounds->data.Contains(toolbar_bounds->data); },
+            "Toolbar in browser"),
+        Check(
+            [=] { return browser_bounds->data.Contains(content_bounds->data); },
+            "Content in browser"),
+        CheckView(
+            ToolbarView::kToolbarElementId,
+            [=](ToolbarView* toolbar) {
+              LOG(INFO) << "Toolbar minimum size is "
+                        << toolbar->GetMinimumSize().ToString();
+              return toolbar_bounds->data.width() >=
+                     toolbar->GetMinimumSize().width();
+            },
+            "Toolbar is at least minimum size"));
   }
 
   auto CloseSidePanel() {
@@ -125,8 +192,6 @@
     return GetLayoutConstant(LayoutConstant::kToolbarCornerRadius);
   }
 
-  using Bounds = base::RefCountedData<gfx::Rect>;
-
   template <typename F>
   auto ScreenshotSubregion(ui::ElementSpecifier spec,
                            const std::string& name,
@@ -253,9 +318,22 @@
     });
   }
 
+  auto ResizeToMinimumWidth() {
+    auto steps = Steps(
+        WithView(kBrowserViewElementId,
+                 [=](BrowserView* browser_view) {
+                   auto* const widget = browser_view->GetWidget();
+                   widget->SetSize(gfx::Size(widget->GetMinimumSize().width(),
+                                             widget->GetSize().height()));
+                 }),
+        Do([this]() { RunScheduledLayouts(); }));
+    AddDescriptionPrefix(steps, "ResizeToMinimumWidth");
+    return steps;
+  }
+
  private:
-  const bool disable_animations_;
   base::test::ScopedFeatureList feature_list_;
+  const gfx::AnimationTestApi::RenderModeResetter render_mode_resetter_;
 };
 
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(BrowserViewTabbedLayoutImplUiTest,
@@ -430,6 +508,84 @@
       ScreenshotBottom(kSidePanelElementId, "side_panel_bottom", 8));
 }
 
+// Regression tests to ensure that when the side panel is open and the browser
+// is at its minimum size, it doesn't go into content-height mode.
+// Content-height mode is only for cases where the browser is already too small
+// when the side panel is opened.
+//
+// See https://crbug.com/491484034 for rationale.
+
+IN_PROC_BROWSER_TEST_F(BrowserViewTabbedLayoutImplUiTest,
+                       OpenSidePanelAtSmallWidthCollapsesLayout) {
+  gfx::Rect toolbar_bounds_in_screen;
+  RunTestSequence(ResizeToMinimumWidth(), SelectTab(kBrowserViewElementId, 0),
+                  ReplaceAndShowSidePanel(SidePanelEntry::PanelType::kToolbar),
+                  WithView(ToolbarView::kToolbarElementId,
+                           [&](views::View* toolbar) {
+                             toolbar_bounds_in_screen =
+                                 toolbar->GetBoundsInScreen();
+                           }),
+                  CheckView(kSidePanelElementId, [&](views::View* side_panel) {
+                    return side_panel->GetBoundsInScreen().y() >
+                           toolbar_bounds_in_screen.bottom();
+                  }));
+}
+
+IN_PROC_BROWSER_TEST_F(BrowserViewTabbedLayoutImplUiTest,
+                       TestMinimumWindowSizeWhenSidePanelIsOpen) {
+  gfx::Rect toolbar_bounds_in_screen;
+  RunTestSequence(SelectTab(kBrowserViewElementId, 0),
+                  ReplaceAndShowSidePanel(SidePanelEntry::PanelType::kToolbar),
+                  ResizeToMinimumWidth(),
+                  WithView(ToolbarView::kToolbarElementId,
+                           [&](views::View* toolbar) {
+                             toolbar_bounds_in_screen =
+                                 toolbar->GetBoundsInScreen();
+                           }),
+                  CheckView(kSidePanelElementId, [&](views::View* side_panel) {
+                    return side_panel->GetBoundsInScreen().y() <
+                           toolbar_bounds_in_screen.bottom();
+                  }));
+}
+
+// Regression tests for cases where preferred width for side panel is
+// unreasonably large or small.
+
+IN_PROC_BROWSER_TEST_F(BrowserViewTabbedLayoutImplUiTest, NormalSidePanelSize) {
+  RunScheduledLayouts();
+  RunTestSequence(SelectTab(kBrowserViewElementId, 0),
+                  ReplaceAndShowSidePanel(SidePanelEntry::PanelType::kToolbar),
+                  VerifyLayout());
+}
+
+IN_PROC_BROWSER_TEST_F(BrowserViewTabbedLayoutImplUiTest, SmallSidePanelSize) {
+  RunScheduledLayouts();
+  RunTestSequence(
+      SelectTab(kBrowserViewElementId, 0),
+      ReplaceAndShowSidePanel(SidePanelEntry::PanelType::kToolbar, 100),
+      VerifyLayout());
+}
+
+IN_PROC_BROWSER_TEST_F(BrowserViewTabbedLayoutImplUiTest,
+                       LargeSidePanelPreferredSize) {
+  RunScheduledLayouts();
+  const int large_width = browser()->GetBrowserView().width() - 20;
+  RunTestSequence(
+      SelectTab(kBrowserViewElementId, 0),
+      ReplaceAndShowSidePanel(SidePanelEntry::PanelType::kToolbar, large_width),
+      VerifyLayout());
+}
+
+IN_PROC_BROWSER_TEST_F(BrowserViewTabbedLayoutImplUiTest,
+                       VeryLargeSidePanelPreferredSize) {
+  RunScheduledLayouts();
+  const int large_width = browser()->GetBrowserView().width() + 100;
+  RunTestSequence(
+      SelectTab(kBrowserViewElementId, 0),
+      ReplaceAndShowSidePanel(SidePanelEntry::PanelType::kToolbar, large_width),
+      VerifyLayout());
+}
+
 // Regression test for limiting the amount of WebContents resizing when the
 // "flyover" animation flag is enabled.
 class BrowserViewTabbedLayoutImplContentLayoutUiTest
diff --git a/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.cc b/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.cc
index f55e33a..13ade0e 100644
--- a/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.cc
+++ b/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.cc
@@ -6,6 +6,7 @@
 
 #include <optional>
 
+#include "base/functional/bind.h"
 #include "base/i18n/rtl.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
@@ -13,12 +14,12 @@
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/favicon/favicon_utils.h"
 #include "chrome/browser/search/search.h"
+#include "chrome/browser/ui/tab_ui_helper.h"
 #include "chrome/browser/ui/tabs/alert/tab_alert.h"
 #include "chrome/browser/ui/tabs/alert/tab_alert_controller.h"
 #include "chrome/browser/ui/tabs/alert/tab_alert_icon.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/browser/ui/tabs/split_tab_menu_model.h"
-#include "chrome/browser/ui/tabs/tab_data.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/contents_container_outline.h"
@@ -134,12 +135,6 @@
           .WithOrder(1));
   views::InstallCircleHighlightPathGenerator(image_button_);
 
-  // Update minitoolbar contents.
-  std::optional<tabs::TabData> tab_data = GetTabData();
-  if (tab_data.has_value()) {
-    UpdateContents(tab_data.value());
-  }
-
   web_contents_attached_subscription_ =
       web_view->AddWebContentsAttachedCallback(
           base::BindRepeating(&MultiContentsViewMiniToolbar::UpdateWebContents,
@@ -149,14 +144,10 @@
           base::BindRepeating(&MultiContentsViewMiniToolbar::ClearWebContents,
                               base::Unretained(this)));
 
-  RegisterTabAlertSubscription();
-
-  browser_view_->browser()->tab_strip_model()->AddObserver(this);
+  RegisterTabSubscriptions();
 }
 
-MultiContentsViewMiniToolbar::~MultiContentsViewMiniToolbar() {
-  browser_view_->browser()->tab_strip_model()->RemoveObserver(this);
-}
+MultiContentsViewMiniToolbar::~MultiContentsViewMiniToolbar() = default;
 
 void MultiContentsViewMiniToolbar::UpdateState(bool is_active,
                                                bool is_highlighted) {
@@ -188,20 +179,53 @@
 }
 
 void MultiContentsViewMiniToolbar::UpdateContents() {
-  std::optional<tabs::TabData> tab_data = GetTabData();
-  if (tab_data.has_value()) {
-    UpdateContents(tab_data.value());
+  auto* const interface = GetTabInterface(web_contents_);
+  if (!interface) {
+    return;
   }
+
+  TabUIHelper* const tab_ui_helper = TabUIHelper::From(interface);
+
+  GURL domain_url = tab_ui_helper->GetVisibleURL();
+  if (tab_ui_helper->GetLastCommittedURL().is_valid()) {
+    domain_url = tab_ui_helper->GetLastCommittedURL();
+  }
+
+  // Create the formatted domain, this will match the hover card domain.
+  std::u16string domain;
+  if (IsNTP(domain_url)) {
+    domain = u"";
+  } else if (domain_url.SchemeIsFile()) {
+    domain = l10n_util::GetStringUTF16(IDS_HOVER_CARD_FILE_URL_SOURCE);
+  } else if (domain_url.SchemeIsBlob()) {
+    domain = l10n_util::GetStringUTF16(IDS_HOVER_CARD_BLOB_URL_SOURCE);
+  } else if (domain_url.SchemeIs(url::kViewSourceScheme)) {
+    domain = l10n_util::GetStringUTF16(IDS_HOVER_CARD_VIEW_SOURCE_URL_SOURCE);
+  } else if (tab_ui_helper->ShouldDisplayURL()) {
+    domain = url_formatter::FormatUrl(
+        domain_url,
+        url_formatter::kFormatUrlOmitDefaults |
+            url_formatter::kFormatUrlOmitTrivialSubdomains |
+            url_formatter::kFormatUrlOmitHTTPS |
+            url_formatter::kFormatUrlTrimAfterHost,
+        base::UnescapeRule::SPACES, nullptr, nullptr, nullptr);
+  }
+  domain_label_->SetText(domain);
+  domain_label_->SetElideBehavior(domain_url.IsStandard() &&
+                                          !domain_url.SchemeIsFile() &&
+                                          !domain_url.SchemeIsFileSystem()
+                                      ? gfx::ELIDE_HEAD
+                                      : gfx::ELIDE_TAIL);
+  domain_label_->SetCustomTooltipText(base::ASCIIToUTF16(domain_url.spec()));
+
+  UpdateFavicon(tab_ui_helper);
 }
 
 void MultiContentsViewMiniToolbar::UpdateWebContents(views::WebView* web_view) {
   tab_alert_status_subscription_.reset();
+  tab_ui_updated_subscription_.reset();
   web_contents_ = web_view->web_contents();
-  RegisterTabAlertSubscription();
-  std::optional<tabs::TabData> tab_data = GetTabData();
-  if (tab_data.has_value()) {
-    UpdateContents(tab_data.value());
-  }
+  RegisterTabSubscriptions();
 }
 
 void MultiContentsViewMiniToolbar::ClearWebContents(views::WebView*) {
@@ -210,15 +234,6 @@
   web_contents_ = nullptr;
 }
 
-void MultiContentsViewMiniToolbar::OnTabChangedAt(tabs::TabInterface* tab,
-                                                  int index,
-                                                  TabChangeType change_type) {
-  if (!web_contents_ || tab->GetContents() != web_contents_) {
-    return;
-  }
-  UpdateContents(tabs::TabData::FromTabInterface(tab));
-}
-
 void MultiContentsViewMiniToolbar::OnPaint(gfx::Canvas* canvas) {
   // Paint the mini toolbar background to match the toolbar.
   ThemedBackground::PaintBackground(canvas, this, browser_view_);
@@ -226,19 +241,20 @@
 
 void MultiContentsViewMiniToolbar::OnThemeChanged() {
   views::View::OnThemeChanged();
-  std::optional<tabs::TabData> tab_data = GetTabData();
-  if (tab_data.has_value()) {
-    UpdateFavicon(tab_data.value());
-  }
   if (auto* interface = GetTabInterface(web_contents_)) {
     auto* tab_alert_controller = tabs::TabAlertController::From(interface);
     if (tab_alert_controller) {
       OnAlertStatusIndicatorChanged(tab_alert_controller->GetAlertToShow());
     }
+
+    TabUIHelper* const tab_ui_helper = TabUIHelper::From(interface);
+    if (tab_ui_helper) {
+      UpdateFavicon(tab_ui_helper);
+    }
   }
 }
 
-void MultiContentsViewMiniToolbar::RegisterTabAlertSubscription() {
+void MultiContentsViewMiniToolbar::RegisterTabSubscriptions() {
   if (auto* interface = GetTabInterface(web_contents_)) {
     auto* tab_alert_controller = tabs::TabAlertController::From(interface);
     OnAlertStatusIndicatorChanged(tab_alert_controller->GetAlertToShow());
@@ -246,6 +262,11 @@
         tab_alert_controller->AddAlertToShowChangedCallback(base::BindRepeating(
             &MultiContentsViewMiniToolbar::OnAlertStatusIndicatorChanged,
             base::Unretained(this)));
+    tab_ui_updated_subscription_ =
+        TabUIHelper::From(interface)->AddTabUIChangeCallback(
+            base::BindRepeating(&MultiContentsViewMiniToolbar::UpdateContents,
+                                base::Unretained(this)));
+    UpdateContents();
   }
 }
 
@@ -265,62 +286,10 @@
   }
 }
 
-std::optional<tabs::TabData> MultiContentsViewMiniToolbar::GetTabData() {
-  if (!web_contents_) {
-    return std::nullopt;
-  }
-
-  TabStripModel* const model = browser_view_->browser()->tab_strip_model();
-  const int tab_index = model->GetIndexOfWebContents(web_contents_);
-  if (tab_index == TabStripModel::kNoTab) {
-    return std::nullopt;
-  }
-
-  tabs::TabInterface* const tab_interface = GetTabInterface(web_contents_);
-  return tab_interface ? std::make_optional(
-                             tabs::TabData::FromTabInterface(tab_interface))
-                       : std::nullopt;
-}
-
-void MultiContentsViewMiniToolbar::UpdateContents(tabs::TabData tab_data) {
-  GURL domain_url = tab_data.visible_url;
-  if (tab_data.last_committed_url.is_valid()) {
-    domain_url = tab_data.last_committed_url;
-  }
-  // Create the formatted domain, this will match the hover card domain.
-  std::u16string domain;
-  if (IsNTP(domain_url)) {
-    domain = u"";
-  } else if (domain_url.SchemeIsFile()) {
-    domain = l10n_util::GetStringUTF16(IDS_HOVER_CARD_FILE_URL_SOURCE);
-  } else if (domain_url.SchemeIsBlob()) {
-    domain = l10n_util::GetStringUTF16(IDS_HOVER_CARD_BLOB_URL_SOURCE);
-  } else if (domain_url.SchemeIs(url::kViewSourceScheme)) {
-    domain = l10n_util::GetStringUTF16(IDS_HOVER_CARD_VIEW_SOURCE_URL_SOURCE);
-  } else if (tab_data.should_display_url) {
-    domain = url_formatter::FormatUrl(
-        domain_url,
-        url_formatter::kFormatUrlOmitDefaults |
-            url_formatter::kFormatUrlOmitTrivialSubdomains |
-            url_formatter::kFormatUrlOmitHTTPS |
-            url_formatter::kFormatUrlTrimAfterHost,
-        base::UnescapeRule::SPACES, nullptr, nullptr, nullptr);
-  }
-  domain_label_->SetText(domain);
-  domain_label_->SetElideBehavior(domain_url.IsStandard() &&
-                                          !domain_url.SchemeIsFile() &&
-                                          !domain_url.SchemeIsFileSystem()
-                                      ? gfx::ELIDE_HEAD
-                                      : gfx::ELIDE_TAIL);
-  domain_label_->SetCustomTooltipText(base::ASCIIToUTF16(domain_url.spec()));
-
-  UpdateFavicon(tab_data);
-}
-
-void MultiContentsViewMiniToolbar::UpdateFavicon(tabs::TabData tab_data) {
+void MultiContentsViewMiniToolbar::UpdateFavicon(TabUIHelper* tab_ui_helper) {
   // Theme the favicon similar to how favicons are themed in the bookmarks bar.
-  ui::ImageModel favicon = tab_data.favicon;
-  bool themify_favicon = tab_data.should_themify_favicon;
+  ui::ImageModel favicon = tab_ui_helper->GetFavicon();
+  bool themify_favicon = tab_ui_helper->ShouldThemifyFavicon();
   if (favicon.IsEmpty()) {
     favicon = favicon::GetDefaultFaviconModel(kColorBookmarkBarBackground);
     themify_favicon = true;
diff --git a/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.h b/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.h
index 8402967..1eb06b0 100644
--- a/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.h
+++ b/chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.h
@@ -9,13 +9,14 @@
 
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
-#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/view.h"
 
 class ContentsWebView;
-namespace tabs {
-struct TabData;
+class TabUIHelper;
+
+namespace content {
+class WebContents;
 }
 
 namespace tabs {
@@ -36,8 +37,7 @@
 
 // MultiContentsViewMiniToolbar is shown for the inactive side of a split and
 // displays the favicon, domain, tab alert state, and a menu button.
-class MultiContentsViewMiniToolbar : public views::View,
-                                     public TabStripModelObserver {
+class MultiContentsViewMiniToolbar : public views::View {
   METADATA_HEADER(MultiContentsViewMiniToolbar, views::View)
 
  public:
@@ -54,11 +54,6 @@
   views::ImageButton* image_button_for_testing() { return image_button_; }
 
  private:
-  // TabStripModelObserver:
-  void OnTabChangedAt(tabs::TabInterface* tab,
-                      int index,
-                      TabChangeType change_type) override;
-
   // View:
   void OnPaint(gfx::Canvas* canvas) override;
   void OnThemeChanged() override;
@@ -66,13 +61,10 @@
   void UpdateWebContents(views::WebView* web_view);
   void ClearWebContents(views::WebView*);
 
-  void RegisterTabAlertSubscription();
+  void RegisterTabSubscriptions();
   void OnAlertStatusIndicatorChanged(std::optional<tabs::TabAlert> new_alert);
 
-  std::optional<tabs::TabData> GetTabData();
-  // Updates the favicon and domain based on the provided |tab_data|.
-  void UpdateContents(tabs::TabData tab_data);
-  void UpdateFavicon(tabs::TabData tab_data);
+  void UpdateFavicon(TabUIHelper* tab_ui_helper);
 
   void OpenSplitViewMenu();
   void CloseCurrentView();
@@ -91,6 +83,7 @@
   base::CallbackListSubscription web_contents_attached_subscription_;
   base::CallbackListSubscription web_contents_detached_subscription_;
   std::optional<base::CallbackListSubscription> tab_alert_status_subscription_;
+  std::optional<base::CallbackListSubscription> tab_ui_updated_subscription_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_FRAME_MULTI_CONTENTS_VIEW_MINI_TOOLBAR_H_
diff --git a/chrome/browser/ui/views/frame/vertical_tab_strip_region_view.cc b/chrome/browser/ui/views/frame/vertical_tab_strip_region_view.cc
index 503b8d5f..af75394f 100644
--- a/chrome/browser/ui/views/frame/vertical_tab_strip_region_view.cc
+++ b/chrome/browser/ui/views/frame/vertical_tab_strip_region_view.cc
@@ -919,22 +919,19 @@
 
   // If the rect doesn't fit on the monitor, push the arrow to the other side.
   display::Screen* screen = display::Screen::Get();
-  display::Display display =
-      screen->GetDisplayNearestView(GetWidget()->GetNativeView());
+  gfx::Rect display_bounds =
+      screen->GetDisplayNearestView(GetWidget()->GetNativeView()).bounds();
 
-  if (!display.bounds().Contains(drop_bounds)) {
-    // Only left/right arrows should be outside the display bounds.
-    CHECK_NE(*direction, DropArrow::Direction::kDown);
+  DropArrow::MaybeAdjustDisplayBounds(display_bounds);
 
-    const gfx::Size drop_arrow_size = DropArrow::GetSize();
-    if (base::i18n::IsRTL()) {
+  if (!display_bounds.Contains(drop_bounds)) {
+    if (base::i18n::IsRTL() && *direction == DropArrow::Direction::kLeft) {
       *direction = DropArrow::Direction::kRight;
-      drop_bounds.Offset(
-          -GetBoundsInScreen().width() - drop_arrow_size.height(), 0);
-    } else {
+      drop_bounds.Offset(-GetBoundsInScreen().width() - DropArrow::kSize, 0);
+    } else if (base::i18n::IsRTL() &&
+               *direction == DropArrow::Direction::kRight) {
       *direction = DropArrow::Direction::kLeft;
-      drop_bounds.Offset(GetBoundsInScreen().width() + drop_arrow_size.height(),
-                         0);
+      drop_bounds.Offset(GetBoundsInScreen().width() + DropArrow::kSize, 0);
     }
   }
 
@@ -1016,18 +1013,15 @@
 gfx::Rect VerticalTabStripRegionView::GetLinkDropBoundsFromPosition(
     gfx::Point position,
     DropArrow::Direction direction) {
-  gfx::Size drop_arrow_size = DropArrow::GetSize();
   if (direction == DropArrow::Direction::kRight) {
-    drop_arrow_size.Transpose();
-    position.Offset(-drop_arrow_size.width(), -drop_arrow_size.height() / 2);
+    position.Offset(-DropArrow::kSize, -DropArrow::kSize / 2);
   } else if (direction == DropArrow::Direction::kLeft) {
-    drop_arrow_size.Transpose();
-    position.Offset(drop_arrow_size.width(), -drop_arrow_size.height() / 2);
+    position.Offset(DropArrow::kSize, -DropArrow::kSize / 2);
   } else {
-    position.Offset(-drop_arrow_size.width() / 2, -drop_arrow_size.height());
+    position.Offset(-DropArrow::kSize / 2, -DropArrow::kSize);
   }
 
-  return gfx::Rect(position, drop_arrow_size);
+  return gfx::Rect(position, gfx::Size(DropArrow::kSize, DropArrow::kSize));
 }
 
 BEGIN_METADATA(VerticalTabStripRegionView)
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view_browsertest.cc b/chrome/browser/ui/views/location_bar/location_bar_view_browsertest.cc
index cc6374d..bf93b7b 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view_browsertest.cc
@@ -622,7 +622,8 @@
           {{omnibox::kWebUIOmniboxAimPopupAddContextButtonVariantParam.name,
             "inline"}}},
          {omnibox::kWebUIOmniboxPopup, {}}},
-        /*disabled_features=*/{omnibox::kAimServerEligibilityEnabled});
+        /*disabled_features=*/{omnibox::kAimServerEligibilityEnabled,
+                               omnibox::kAimFuseboxEligibilityCheckEnabled});
   }
   ~LocationBarViewAddContextButtonBrowserTest() override = default;
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui.cc b/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui.cc
index e01aa99f..61943ec 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_popup_view_webui.cc
@@ -111,11 +111,11 @@
       }
     }
 
-    if (auto* handler = presenter()
-                            ->GetWebUIContent()
-                            ->contents_wrapper()
-                            ->GetWebUIController()
-                            ->omnibox_handler()) {
+    auto* controller = presenter()
+                           ->GetWebUIContent()
+                           ->contents_wrapper()
+                           ->GetWebUIController();
+    if (auto* handler = controller ? controller->omnibox_handler() : nullptr) {
       handler->SetAimButtonVisible(omnibox_view_->AimButtonVisible());
     }
   }
@@ -137,11 +137,9 @@
 void OmniboxPopupViewWebUI::StepSelection(
     OmniboxPopupSelection::Direction direction,
     OmniboxPopupSelection::Step step) {
-  if (auto* handler = presenter()
-                          ->GetWebUIContent()
-                          ->contents_wrapper()
-                          ->GetWebUIController()
-                          ->omnibox_handler()) {
+  auto* controller =
+      presenter()->GetWebUIContent()->contents_wrapper()->GetWebUIController();
+  if (auto* handler = controller ? controller->omnibox_handler() : nullptr) {
     handler->SetAimButtonVisible(omnibox_view_->AimButtonVisible());
     handler->StepSelection(direction, step);
   }
@@ -149,11 +147,9 @@
 
 void OmniboxPopupViewWebUI::OpenCurrentSelection(
     WindowOpenDisposition disposition) {
-  if (auto* handler = presenter()
-                          ->GetWebUIContent()
-                          ->contents_wrapper()
-                          ->GetWebUIController()
-                          ->omnibox_handler()) {
+  auto* controller =
+      presenter()->GetWebUIContent()->contents_wrapper()->GetWebUIController();
+  if (auto* handler = controller ? controller->omnibox_handler() : nullptr) {
     handler->OpenCurrentSelection(disposition);
   }
 }
diff --git a/chrome/browser/ui/views/page_action/action_ids.h b/chrome/browser/ui/views/page_action/action_ids.h
index b282a07..a5f6c92 100644
--- a/chrome/browser/ui/views/page_action/action_ids.h
+++ b/chrome/browser/ui/views/page_action/action_ids.h
@@ -41,6 +41,7 @@
     kActionFilledCardInformation,
     kActionShowPaymentsBubbleOrPage,
     kActionSidePanelShowContextualTasks,
+    kActionGlicContextualCueing,
     kActionBookmarkThisTab,
     kActionFederation,
 });
diff --git a/chrome/browser/ui/views/page_action/page_action_properties_provider.cc b/chrome/browser/ui/views/page_action/page_action_properties_provider.cc
index bf5ab45d..84f5e20 100644
--- a/chrome/browser/ui/views/page_action/page_action_properties_provider.cc
+++ b/chrome/browser/ui/views/page_action/page_action_properties_provider.cc
@@ -246,6 +246,13 @@
                 .element_identifier = kFederationElementId,
             },
         },
+        {
+            kActionGlicContextualCueing,
+            {
+                .histogram_name = "Glic",
+                .type = PageActionIconType::kGlic,
+            },
+        },
     });
 
 constexpr bool CheckIgnoreFlagUsage() {
diff --git a/chrome/browser/ui/views/tabs/projects/BUILD.gn b/chrome/browser/ui/views/tabs/projects/BUILD.gn
index b5821fb..e901fdfcd 100644
--- a/chrome/browser/ui/views/tabs/projects/BUILD.gn
+++ b/chrome/browser/ui/views/tabs/projects/BUILD.gn
@@ -8,6 +8,7 @@
     "projects_panel_controller.h",
     "projects_panel_controls_view.h",
     "projects_panel_no_tab_groups_view.h",
+    "projects_panel_recent_threads_expand_button.h",
     "projects_panel_recent_threads_view.h",
     "projects_panel_tab_groups_drag_scroll_handler.h",
     "projects_panel_tab_groups_item_view.h",
@@ -34,6 +35,7 @@
     "projects_panel_controller.cc",
     "projects_panel_controls_view.cc",
     "projects_panel_no_tab_groups_view.cc",
+    "projects_panel_recent_threads_expand_button.cc",
     "projects_panel_recent_threads_view.cc",
     "projects_panel_tab_groups_drag_scroll_handler.cc",
     "projects_panel_tab_groups_item_view.cc",
@@ -75,6 +77,7 @@
   testonly = true
   sources = [
     "projects_panel_controller_unittest.cc",
+    "projects_panel_recent_threads_expand_button_unittest.cc",
     "projects_panel_recent_threads_view_unittest.cc",
     "projects_panel_tab_groups_drag_scroll_handler_unittest.cc",
     "projects_panel_tab_groups_item_view_unittest.cc",
diff --git a/chrome/browser/ui/views/tabs/projects/layout_constants.h b/chrome/browser/ui/views/tabs/projects/layout_constants.h
index 99cc5b7..67576d0 100644
--- a/chrome/browser/ui/views/tabs/projects/layout_constants.h
+++ b/chrome/browser/ui/views/tabs/projects/layout_constants.h
@@ -55,6 +55,9 @@
 // any affected histograms.
 inline constexpr size_t kMaxNumberOfRecentThreads = 300;
 
+// Number of threads visible when the threads section when collapsed.
+inline constexpr int kNumThreadsVisibleWhenCollapsed = 3;
+
 // Minimum width of the projects panel.
 inline constexpr int kProjectsPanelMinWidth = 240;
 
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button.cc b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button.cc
new file mode 100644
index 0000000..802a023a
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button.cc
@@ -0,0 +1,77 @@
+// Copyright 2026 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/tabs/projects/projects_panel_recent_threads_expand_button.h"
+
+#include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/ui/color/chrome_color_id.h"
+#include "chrome/browser/ui/views/tabs/projects/layout_constants.h"
+#include "chrome/browser/ui/views/tabs/projects/projects_panel_utils.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/base/models/image_model.h"
+#include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/animation/ink_drop.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/flex_layout.h"
+
+namespace {
+constexpr int kExpandIconSize = 20;
+constexpr gfx::Insets kExpandIconMargins = gfx::Insets::TLBR(0, 2, 0, 2);
+}  // namespace
+
+ProjectsPanelRecentThreadsExpandButton::ProjectsPanelRecentThreadsExpandButton(
+    base::RepeatingClosure callback)
+    : views::Button(std::move(callback)) {
+  SetLayoutManager(std::make_unique<views::FlexLayout>())
+      ->SetInteriorMargin(projects_panel::kListItemMargins)
+      .SetOrientation(views::LayoutOrientation::kHorizontal)
+      .SetCrossAxisAlignment(views::LayoutAlignment::kCenter);
+
+  icon_ = AddChildView(std::make_unique<views::ImageView>());
+  icon_->SetProperty(views::kMarginsKey, kExpandIconMargins);
+
+  title_ = AddChildView(std::make_unique<views::Label>());
+  title_->SetTextStyle(views::style::STYLE_BODY_3_MEDIUM);
+  title_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD);
+  title_->SetBackgroundColor(SK_ColorTRANSPARENT);
+  title_->SetProperty(views::kMarginsKey,
+                      projects_panel::kListItemTitleMargins);
+  title_->SetProperty(
+      views::kFlexBehaviorKey,
+      views::FlexSpecification(views::LayoutOrientation::kHorizontal,
+                               views::MinimumFlexSizeRule::kScaleToMinimum,
+                               views::MaximumFlexSizeRule::kUnbounded));
+
+  projects_panel::ConfigureInkDropForButton(this);
+
+  SetHideInkDropWhenShowingContextMenu(true);
+}
+
+ProjectsPanelRecentThreadsExpandButton::
+    ~ProjectsPanelRecentThreadsExpandButton() = default;
+
+void ProjectsPanelRecentThreadsExpandButton::SetExpanded(bool expanded) {
+  const int string_id =
+      expanded ? IDS_THREADS_SHOW_LESS : IDS_THREADS_SHOW_MORE;
+  const gfx::VectorIcon& icon = expanded ? kKeyboardArrowUpChromeRefreshIcon
+                                         : kKeyboardArrowDownChromeRefreshIcon;
+
+  icon_->SetImage(ui::ImageModel::FromVectorIcon(
+      icon, kColorProjectsPanelButtonIcon, kExpandIconSize));
+  title_->SetText(l10n_util::GetStringUTF16(string_id));
+  GetViewAccessibility().SetName(l10n_util::GetStringUTF16(string_id));
+
+  // Reset the button and ink drop state to ensure it doesn't get stuck in the
+  // 'activated' or 'hovered' state if the button moves during the animation.
+  SetState(STATE_NORMAL);
+  auto* ink_drop = views::InkDrop::Get(this)->GetInkDrop();
+  ink_drop->AnimateToState(views::InkDropState::HIDDEN);
+  ink_drop->SetHovered(false);
+}
+
+BEGIN_METADATA(ProjectsPanelRecentThreadsExpandButton)
+END_METADATA
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button.h b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button.h
new file mode 100644
index 0000000..00bc890
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button.h
@@ -0,0 +1,41 @@
+// Copyright 2026 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_TABS_PROJECTS_PROJECTS_PANEL_RECENT_THREADS_EXPAND_BUTTON_H_
+#define CHROME_BROWSER_UI_VIEWS_TABS_PROJECTS_PROJECTS_PANEL_RECENT_THREADS_EXPAND_BUTTON_H_
+
+#include "base/memory/raw_ptr.h"
+#include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/views/controls/button/button.h"
+
+namespace views {
+class ImageView;
+class Label;
+}  // namespace views
+
+class ProjectsPanelRecentThreadsExpandButton : public views::Button {
+  METADATA_HEADER(ProjectsPanelRecentThreadsExpandButton, views::Button)
+
+ public:
+  explicit ProjectsPanelRecentThreadsExpandButton(
+      base::RepeatingClosure callback);
+  ProjectsPanelRecentThreadsExpandButton(
+      const ProjectsPanelRecentThreadsExpandButton&) = delete;
+  ProjectsPanelRecentThreadsExpandButton& operator=(
+      const ProjectsPanelRecentThreadsExpandButton&) = delete;
+  ~ProjectsPanelRecentThreadsExpandButton() override;
+
+  // Sets whether the list this button corresponds to is expanded, updating the
+  // title and icon.
+  void SetExpanded(bool expanded);
+
+  views::Label* title_label_for_testing() { return title_; }
+  views::ImageView* icon_view_for_testing() { return icon_; }
+
+ private:
+  raw_ptr<views::ImageView> icon_ = nullptr;
+  raw_ptr<views::Label> title_ = nullptr;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_TABS_PROJECTS_PROJECTS_PANEL_RECENT_THREADS_EXPAND_BUTTON_H_
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button_unittest.cc b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button_unittest.cc
new file mode 100644
index 0000000..9eb291c
--- /dev/null
+++ b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button_unittest.cc
@@ -0,0 +1,55 @@
+// Copyright 2026 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/tabs/projects/projects_panel_recent_threads_expand_button.h"
+
+#include "base/functional/callback_helpers.h"
+#include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/image_model.h"
+#include "ui/gfx/vector_icon_types.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/test/views_test_base.h"
+
+class ProjectsPanelRecentThreadsExpandButtonTest : public views::ViewsTestBase {
+ public:
+  ProjectsPanelRecentThreadsExpandButtonTest() = default;
+};
+
+TEST_F(ProjectsPanelRecentThreadsExpandButtonTest, ShowsCorrectTextAndIcon) {
+  auto button = std::make_unique<ProjectsPanelRecentThreadsExpandButton>(
+      base::DoNothing());
+
+  // Initially the button should be in the collapsed state.
+  button->SetExpanded(false);
+  EXPECT_EQ(button->title_label_for_testing()->GetText(),
+            l10n_util::GetStringUTF16(IDS_THREADS_SHOW_MORE));
+
+  ui::ImageModel icon_model = button->icon_view_for_testing()->GetImageModel();
+  EXPECT_TRUE(icon_model.IsVectorIcon());
+  EXPECT_EQ(icon_model.GetVectorIcon().vector_icon()->name,
+            kKeyboardArrowDownChromeRefreshIcon.name);
+
+  // Switch to expanded.
+  button->SetExpanded(true);
+  EXPECT_EQ(button->title_label_for_testing()->GetText(),
+            l10n_util::GetStringUTF16(IDS_THREADS_SHOW_LESS));
+
+  icon_model = button->icon_view_for_testing()->GetImageModel();
+  EXPECT_TRUE(icon_model.IsVectorIcon());
+  EXPECT_EQ(icon_model.GetVectorIcon().vector_icon()->name,
+            kKeyboardArrowUpChromeRefreshIcon.name);
+
+  // Switch back to collapsed.
+  button->SetExpanded(false);
+  EXPECT_EQ(button->title_label_for_testing()->GetText(),
+            l10n_util::GetStringUTF16(IDS_THREADS_SHOW_MORE));
+
+  icon_model = button->icon_view_for_testing()->GetImageModel();
+  EXPECT_TRUE(icon_model.IsVectorIcon());
+  EXPECT_EQ(icon_model.GetVectorIcon().vector_icon()->name,
+            kKeyboardArrowDownChromeRefreshIcon.name);
+}
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.cc b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.cc
index 87a564f..2f0ece6f 100644
--- a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.cc
+++ b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.cc
@@ -8,33 +8,97 @@
 #include "chrome/browser/ui/views/tabs/projects/projects_panel_thread_item_view.h"
 #include "components/contextual_tasks/public/contextual_task.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
-#include "ui/views/controls/label.h"
+#include "ui/gfx/animation/slide_animation.h"
+#include "ui/gfx/animation/tween.h"
 #include "ui/views/layout/box_layout.h"
 
+namespace {
+constexpr base::TimeDelta kExpandAnimationTime = base::Milliseconds(250);
+
+static bool disable_animations_for_testing_ = false;
+}  // namespace
+
 ProjectsPanelRecentThreadsView::ProjectsPanelRecentThreadsView(
     ThreadPressedCallback thread_button_callback)
     : thread_button_callback_(std::move(thread_button_callback)) {
   SetLayoutManager(std::make_unique<views::BoxLayout>())
       ->SetOrientation(views::BoxLayout::Orientation::kVertical);
+
+  expansion_animation_.SetTweenType(gfx::Tween::Type::EASE_IN_OUT_EMPHASIZED);
 }
 
 ProjectsPanelRecentThreadsView::~ProjectsPanelRecentThreadsView() = default;
 
 void ProjectsPanelRecentThreadsView::SetThreads(
     const std::vector<contextual_tasks::Thread>& threads) {
+  threads_ = threads;
+  UpdateDisplayedThreads();
+}
+
+void ProjectsPanelRecentThreadsView::SetExpanded(bool expanded) {
+  if (expanded_ == expanded) {
+    return;
+  }
+
+  // Compute the start and end heights after changing the number of threads
+  // displayed.
+  start_height_ = GetPreferredSize().height();
+  expanded_ = expanded;
+  UpdateDisplayedThreads();
+  target_height_ = GetPreferredSize().height();
+
+  if (disable_animations_for_testing_) {
+    PreferredSizeChanged();
+    return;
+  }
+
+  expansion_animation_.Reset(0.0);
+  expansion_animation_.SetSlideDuration(kExpandAnimationTime);
+  expansion_animation_.Show();
+}
+
+void ProjectsPanelRecentThreadsView::UpdateDisplayedThreads() {
   item_views_.clear();
   RemoveAllChildViews();
 
   size_t count = 0;
-  for (const auto& thread : threads) {
+  for (const auto& thread : threads_) {
+    if (count >= projects_panel::kMaxNumberOfRecentThreads ||
+        (!expanded_ &&
+         count >= projects_panel::kNumThreadsVisibleWhenCollapsed)) {
+      break;
+    }
     item_views_.push_back(
         AddChildView(std::make_unique<ProjectsPanelThreadItemView>(
             thread, thread_button_callback_)));
-    if (++count >= projects_panel::kMaxNumberOfRecentThreads) {
-      return;
-    }
+    count++;
   }
 }
 
+gfx::Size ProjectsPanelRecentThreadsView::CalculatePreferredSize(
+    const views::SizeBounds& available_size) const {
+  gfx::Size size = views::View::CalculatePreferredSize(available_size);
+  if (expansion_animation_.is_animating()) {
+    size.set_height(gfx::Tween::IntValueBetween(
+        expansion_animation_.GetCurrentValue(), start_height_, target_height_));
+  }
+  return size;
+}
+
+void ProjectsPanelRecentThreadsView::AnimationProgressed(
+    const gfx::Animation* animation) {
+  PreferredSizeChanged();
+}
+
+void ProjectsPanelRecentThreadsView::AnimationEnded(
+    const gfx::Animation* animation) {
+  PreferredSizeChanged();
+}
+
+// static
+void ProjectsPanelRecentThreadsView::disable_animations_for_testing() {
+  disable_animations_for_testing_ = true;
+}
+
 BEGIN_METADATA(ProjectsPanelRecentThreadsView)
 END_METADATA
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.h b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.h
index 9710abd..3c57e856 100644
--- a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.h
+++ b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.h
@@ -7,13 +7,16 @@
 
 #include "base/functional/callback_helpers.h"
 #include "chrome/browser/ui/views/tabs/projects/projects_panel_thread_item_view.h"
+#include "ui/gfx/animation/animation_delegate.h"
+#include "ui/gfx/animation/slide_animation.h"
 #include "ui/views/view.h"
 
 namespace contextual_tasks {
 struct Thread;
 }
 
-class ProjectsPanelRecentThreadsView : public views::View {
+class ProjectsPanelRecentThreadsView : public views::View,
+                                       public gfx::AnimationDelegate {
   METADATA_HEADER(ProjectsPanelRecentThreadsView, views::View)
 
  public:
@@ -31,13 +34,35 @@
   // Updates the threads shown in the list.
   void SetThreads(const std::vector<contextual_tasks::Thread>& threads);
 
+  // Sets whether the list is expanded to show all threads.
+  void SetExpanded(bool expanded);
+  bool expanded() { return expanded_; }
+
+  // views::View:
+  gfx::Size CalculatePreferredSize(
+      const views::SizeBounds& available_size) const override;
+
+  // gfx::AnimationDelegate:
+  void AnimationProgressed(const gfx::Animation* animation) override;
+  void AnimationEnded(const gfx::Animation* animation) override;
+
   const std::vector<ProjectsPanelThreadItemView*> item_views_for_testing() {
     return item_views_;
   }
 
+  static void disable_animations_for_testing();
+
  private:
+  void UpdateDisplayedThreads();
+
+  std::vector<contextual_tasks::Thread> threads_;
+  bool expanded_ = false;
   std::vector<ProjectsPanelThreadItemView*> item_views_;
   ThreadPressedCallback thread_button_callback_;
+
+  gfx::SlideAnimation expansion_animation_{this};
+  int start_height_ = 0;
+  int target_height_ = 0;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_TABS_PROJECTS_PROJECTS_PANEL_RECENT_THREADS_VIEW_H_
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view_unittest.cc b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view_unittest.cc
index c000340..b629411 100644
--- a/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view_unittest.cc
+++ b/chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view_unittest.cc
@@ -46,6 +46,18 @@
   return *thread;
 }
 
+const contextual_tasks::Thread& GetThread4() {
+  static const base::NoDestructor<contextual_tasks::Thread> thread(
+      CreateThread("Thread 4", "id4"));
+  return *thread;
+}
+
+const std::vector<contextual_tasks::Thread>& GetManyThreads() {
+  static const base::NoDestructor<std::vector<contextual_tasks::Thread>>
+      threads({GetThread1(), GetThread2(), GetThread3(), GetThread4()});
+  return *threads;
+}
+
 MATCHER_P(IsForThread, expected_thread, "") {
   const views::Label* label = arg->title_for_testing();
   return base::UTF8ToUTF16(expected_thread.title) == label->GetText();
@@ -128,7 +140,45 @@
   auto recent_threads_view = std::make_unique<ProjectsPanelRecentThreadsView>();
   recent_threads_view->SetThreads(threads);
 
+  recent_threads_view->SetExpanded(true);
+
   // Ensure only the max number of threads are shown.
   EXPECT_EQ(projects_panel::kMaxNumberOfRecentThreads,
             recent_threads_view->item_views_for_testing().size());
 }
+
+TEST_F(ProjectsPanelRecentThreadsViewTest, CollapsesThreadsInitially) {
+  auto recent_threads_view = std::make_unique<ProjectsPanelRecentThreadsView>();
+  recent_threads_view->SetThreads(GetManyThreads());
+
+  // Only 3 thread should be visible.
+  EXPECT_EQ(3u, recent_threads_view->children().size());
+  EXPECT_EQ(3u, recent_threads_view->item_views_for_testing().size());
+}
+
+TEST_F(ProjectsPanelRecentThreadsViewTest, ExpandList) {
+  auto recent_threads_view = std::make_unique<ProjectsPanelRecentThreadsView>();
+  recent_threads_view->SetThreads(GetManyThreads());
+
+  recent_threads_view->SetExpanded(true);
+
+  // All 4 threads should be visible after expanding.
+  EXPECT_EQ(4u, recent_threads_view->children().size());
+  EXPECT_EQ(4u, recent_threads_view->item_views_for_testing().size());
+  EXPECT_TRUE(recent_threads_view->expanded());
+}
+
+TEST_F(ProjectsPanelRecentThreadsViewTest, CollapseList) {
+  auto recent_threads_view = std::make_unique<ProjectsPanelRecentThreadsView>();
+  recent_threads_view->SetThreads(GetManyThreads());
+  recent_threads_view->SetExpanded(true);
+
+  EXPECT_EQ(4u, recent_threads_view->children().size());
+
+  recent_threads_view->SetExpanded(false);
+
+  // Only 3 threads should be visible after collapsing.
+  EXPECT_EQ(3u, recent_threads_view->children().size());
+  EXPECT_EQ(3u, recent_threads_view->item_views_for_testing().size());
+  EXPECT_FALSE(recent_threads_view->expanded());
+}
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_view.cc b/chrome/browser/ui/views/tabs/projects/projects_panel_view.cc
index 6e613ec..9c98d81 100644
--- a/chrome/browser/ui/views/tabs/projects/projects_panel_view.cc
+++ b/chrome/browser/ui/views/tabs/projects/projects_panel_view.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/ui/views/tabs/projects/layout_constants.h"
 #include "chrome/browser/ui/views/tabs/projects/projects_panel_controller.h"
 #include "chrome/browser/ui/views/tabs/projects/projects_panel_controls_view.h"
+#include "chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_expand_button.h"
 #include "chrome/browser/ui/views/tabs/projects/projects_panel_recent_threads_view.h"
 #include "chrome/browser/ui/views/tabs/projects/projects_panel_tab_groups_view.h"
 #include "chrome/browser/ui/views/tabs/projects/projects_panel_utils.h"
@@ -51,6 +52,7 @@
 #include "ui/gfx/text_constants.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/actions/action_view_controller.h"
+#include "ui/views/animation/ink_drop.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/image_button_factory.h"
@@ -83,7 +85,7 @@
 constexpr int kListHeaderHeight = 28;
 constexpr int kCreateNewTabGroupIconSize = 20;
 constexpr gfx::Insets kCreateNewTabGroupIconMargins =
-    gfx::Insets::TLBR(0, 4, 0, 0);
+    gfx::Insets::TLBR(0, 2, 0, 2);
 
 constexpr base::TimeDelta kPanelShowAnimationDuration = base::Milliseconds(250);
 constexpr base::TimeDelta kPanelHideAnimationDuration = base::Milliseconds(200);
@@ -329,8 +331,16 @@
         std::make_unique<ProjectsPanelRecentThreadsView>(base::BindRepeating(
             &ProjectsPanelView::OnThreadButtonPressed, base::Unretained(this)));
     threads_view_ = threads_scroll_view->SetContents(std::move(threads_view));
+    if (disable_animations_for_testing_) {
+      threads_view_->disable_animations_for_testing();  // IN-TEST
+    }
     SetScrollViewProperties(*threads_scroll_view);
 
+    threads_expand_button_ = threads_container_->AddChildView(
+        std::make_unique<ProjectsPanelRecentThreadsExpandButton>(
+            base::BindRepeating(&ProjectsPanelView::OnThreadExpandButtonPressed,
+                                base::Unretained(this))));
+
     threads_activity_menu_model_ = std::make_unique<ui::SimpleMenuModel>(this);
     threads_activity_menu_model_->AddItemWithIcon(
         kGeminiActivity,
@@ -429,6 +439,12 @@
       const bool show_threads = show_threads_for_testing_ || threads.size() > 0;
       threads_container_->SetVisible(show_threads);
       separator_->SetVisible(show_threads);
+
+      if (show_threads) {
+        threads_expand_button_->SetExpanded(threads_view_->expanded());
+        threads_expand_button_->SetVisible(
+            threads.size() > projects_panel::kNumThreadsVisibleWhenCollapsed);
+      }
     }
 
     base::UmaHistogramCounts100(
@@ -718,6 +734,12 @@
   ClosePanel();
 }
 
+void ProjectsPanelView::OnThreadExpandButtonPressed() {
+  const bool expanded = !threads_view_->expanded();
+  threads_view_->SetExpanded(expanded);
+  threads_expand_button_->SetExpanded(expanded);
+}
+
 void ProjectsPanelView::OnThreadsActivityMenuButtonPressed() {
   threads_activity_menu_runner_->RunMenuAt(
       GetWidget(), threads_activity_menu_button_->button_controller(),
diff --git a/chrome/browser/ui/views/tabs/projects/projects_panel_view.h b/chrome/browser/ui/views/tabs/projects/projects_panel_view.h
index bd020ea..dd69fda3 100644
--- a/chrome/browser/ui/views/tabs/projects/projects_panel_view.h
+++ b/chrome/browser/ui/views/tabs/projects/projects_panel_view.h
@@ -43,6 +43,7 @@
 class BrowserWindowInterface;
 
 class ProjectsPanelController;
+class ProjectsPanelRecentThreadsExpandButton;
 class ProjectsPanelRecentThreadsView;
 class ProjectsPanelStateController;
 class ProjectsPanelTabGroupsView;
@@ -163,6 +164,7 @@
                              contextual_tasks::ThreadType thread_type);
   void OnTabGroupDragUpdated(const gfx::Point& location);
   void OnTabGroupDragExited();
+  void OnThreadExpandButtonPressed();
 
   const raw_ptr<BrowserWindowInterface> browser_;
   raw_ptr<actions::ActionItem> root_action_item_ = nullptr;
@@ -174,6 +176,8 @@
   raw_ptr<ProjectsPanelTabGroupsView> tab_groups_view_ = nullptr;
   raw_ptr<views::View> threads_container_ = nullptr;
   raw_ptr<ProjectsPanelRecentThreadsView> threads_view_ = nullptr;
+  raw_ptr<ProjectsPanelRecentThreadsExpandButton> threads_expand_button_ =
+      nullptr;
   raw_ptr<views::Separator> separator_ = nullptr;
   raw_ptr<views::MenuButton> threads_activity_menu_button_ = nullptr;
   raw_ptr<views::Button> create_new_tab_group_button_ = nullptr;
diff --git a/chrome/browser/ui/views/tabs/shared/drop_arrow.cc b/chrome/browser/ui/views/tabs/shared/drop_arrow.cc
index 39f48ce..1a1e7f8 100644
--- a/chrome/browser/ui/views/tabs/shared/drop_arrow.cc
+++ b/chrome/browser/ui/views/tabs/shared/drop_arrow.cc
@@ -7,25 +7,36 @@
 #include <utility>
 
 #include "base/notreached.h"
-#include "chrome/grit/theme_resources.h"
+#include "build/build_config.h"
+#include "chrome/app/vector_icons/vector_icons.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/models/image_model.h"
-#include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/shadow_value.h"
 #include "ui/views/controls/image_view.h"
+#include "ui/views/layout/layout_provider.h"
 #include "ui/views/widget/widget.h"
 
 namespace {
 
-int GetDropArrowImageResourceId(DropArrow::Direction direction) {
+gfx::ShadowValues GetShadowValues() {
+  constexpr int kShadowBlur = 3;
+  return {gfx::ShadowValue(gfx::Vector2d(0, 0), kShadowBlur,
+                           SkColorSetA(SK_ColorBLACK, 0xFF))};
+}
+
+const gfx::VectorIcon& GetDropArrowIcon(DropArrow::Direction direction) {
   switch (direction) {
     case DropArrow::Direction::kUp:
-      return IDR_TAB_DROP_UP;
+      return kArrowUpwardIcon;
     case DropArrow::Direction::kDown:
-      return IDR_TAB_DROP_DOWN;
+      return kArrowDownwardIcon;
     case DropArrow::Direction::kLeft:
-      return IDR_TAB_DROP_LEFT;
+      return kArrowBackIcon;
     case DropArrow::Direction::kRight:
-      return IDR_TAB_DROP_RIGHT;
+      return kArrowForwardIcon;
     default:
       NOTREACHED();
   }
@@ -44,7 +55,7 @@
   params.z_order = ui::ZOrderLevel::kFloatingUIElement;
   params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
   params.accept_events = false;
-  params.bounds = gfx::Rect(GetSize());
+  params.bounds = gfx::Rect(kSize, kSize);
   params.context = context;
   arrow_widget_->Init(std::move(params));
   arrow_view_ =
@@ -57,22 +68,24 @@
 }
 
 DropArrow::~DropArrow() {
-  // Close eventually deletes the window, which deletes arrow_view too.
+  // Close eventually deletes the window, which deletes arrow_view_ too.
   if (arrow_widget_) {
     arrow_widget_->Close();
   }
 }
 
 // static
-gfx::Size DropArrow::GetSize() {
-  static const gfx::Size size = []() {
-    // Direction doesn't matter, both images are the same size.
-    const gfx::ImageSkia* drop_image =
-        ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
-            GetDropArrowImageResourceId(Direction::kDown));
-    return gfx::Size(drop_image->width(), drop_image->height());
-  }();
-  return size;
+void DropArrow::MaybeAdjustDisplayBounds(gfx::Rect& display_bounds) {
+#if BUILDFLAG(IS_LINUX)
+  // On Linux, `GetBoundsInScreen` returns coordinates relative to the browser
+  // window (plus shadow elevation outsets) rather than the screen. To handle
+  // this, we adjust the display bounds by the difference between the drop arrow
+  // size and the window elevation. Note that this only works on the first
+  // display.
+  const int elevation = views::LayoutProvider::Get()->GetShadowElevationMetric(
+      views::Emphasis::kHigh);
+  display_bounds.Outset(DropArrow::kSize - elevation);
+#endif
 }
 
 void DropArrow::SetIndex(const BrowserRootView::DropIndex& index) {
@@ -80,23 +93,26 @@
   UpdateBounds();
 }
 
+void DropArrow::OnWidgetDestroying(views::Widget* widget) {
+  DCHECK(scoped_observation_.IsObservingSource(arrow_widget_.get()));
+  scoped_observation_.Reset();
+  arrow_view_ = nullptr;
+  arrow_widget_ = nullptr;
+}
+
 void DropArrow::UpdateBounds() {
   Direction direction;
   gfx::Rect drop_bounds = bounds_callback_.Run(index_, &direction);
 
   if (!direction_.has_value() || direction_ != direction) {
     direction_ = direction;
-    arrow_view_->SetImage(ui::ImageModel::FromResourceId(
-        GetDropArrowImageResourceId(*direction_)));
+    gfx::ImageSkia icon = gfx::CreateVectorIcon(GetDropArrowIcon(*direction_),
+                                                kSize, SK_ColorWHITE);
+    arrow_view_->SetImage(ui::ImageModel::FromImageSkia(
+        gfx::ImageSkiaOperations::CreateImageWithDropShadow(
+            icon, GetShadowValues())));
     arrow_view_->SchedulePaint();
   }
 
   arrow_widget_->SetBounds(drop_bounds);
 }
-
-void DropArrow::OnWidgetDestroying(views::Widget* widget) {
-  DCHECK(scoped_observation_.IsObservingSource(arrow_widget_.get()));
-  scoped_observation_.Reset();
-  arrow_view_ = nullptr;
-  arrow_widget_ = nullptr;
-}
diff --git a/chrome/browser/ui/views/tabs/shared/drop_arrow.h b/chrome/browser/ui/views/tabs/shared/drop_arrow.h
index 9c5a81ca..bf451448 100644
--- a/chrome/browser/ui/views/tabs/shared/drop_arrow.h
+++ b/chrome/browser/ui/views/tabs/shared/drop_arrow.h
@@ -45,9 +45,12 @@
   DropArrow& operator=(const DropArrow&) = delete;
   ~DropArrow() override;
 
-  // Returns the size of the arrow image. Height represents the length of the
-  // arrow in the direction it points and width is the opposite dimension.
-  static gfx::Size GetSize();
+  // Represents the size of the arrow image, excluding the drop shadow.
+  static constexpr int kSize = 20;
+
+  // Potentially adjusts `display_bounds` based on platform-specific
+  // requirements.
+  static void MaybeAdjustDisplayBounds(gfx::Rect& display_bounds);
 
   void SetIndex(const BrowserRootView::DropIndex& index);
   BrowserRootView::DropIndex index() const { return index_; }
diff --git a/chrome/browser/ui/views/tabs/tab_container_impl.cc b/chrome/browser/ui/views/tabs/tab_container_impl.cc
index 5872d41..c164a34d 100644
--- a/chrome/browser/ui/views/tabs/tab_container_impl.cc
+++ b/chrome/browser/ui/views/tabs/tab_container_impl.cc
@@ -41,6 +41,7 @@
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/controls/image_view.h"
+#include "ui/views/layout/layout_provider.h"
 #include "ui/views/mouse_watcher_view_host.h"
 #include "ui/views/rect_based_targeting_utils.h"
 #include "ui/views/view_utils.h"
@@ -1642,18 +1643,19 @@
   center_x = GetMirroredXInView(center_x);
 
   // Determine the screen bounds.
-  const gfx::Size drop_arrow_size = DropArrow::GetSize();
-  gfx::Point drop_loc(center_x - drop_arrow_size.width() / 2,
-                      -drop_arrow_size.height());
+  gfx::Point drop_loc(center_x - DropArrow::kSize / 2, -DropArrow::kSize);
   ConvertPointToScreen(this, &drop_loc);
-  gfx::Rect drop_bounds(drop_loc.x(), drop_loc.y(), drop_arrow_size.width(),
-                        drop_arrow_size.height());
+  gfx::Rect drop_bounds(drop_loc.x(), drop_loc.y(), DropArrow::kSize,
+                        DropArrow::kSize);
 
   // If the rect doesn't fit on the monitor, push the arrow to the bottom.
   display::Screen* screen = display::Screen::Get();
-  display::Display display =
-      screen->GetDisplayNearestView(GetWidget()->GetNativeView());
-  const bool is_beneath = !display.bounds().Contains(drop_bounds);
+  gfx::Rect display_bounds =
+      screen->GetDisplayNearestView(GetWidget()->GetNativeView()).bounds();
+
+  DropArrow::MaybeAdjustDisplayBounds(display_bounds);
+
+  const bool is_beneath = drop_bounds.y() < display_bounds.y();
   *direction =
       is_beneath ? DropArrow::Direction::kUp : DropArrow::Direction::kDown;
   if (is_beneath) {
diff --git a/chrome/browser/ui/views/tabs/tab_group_style.cc b/chrome/browser/ui/views/tabs/tab_group_style.cc
index 6d70878..217849b 100644
--- a/chrome/browser/ui/views/tabs/tab_group_style.cc
+++ b/chrome/browser/ui/views/tabs/tab_group_style.cc
@@ -35,6 +35,7 @@
 constexpr int kEmptyChipSize = 20;
 constexpr int kCornerRadius = 6;
 constexpr int kDetachedTabsCornerRadius = 100;
+constexpr int kDetachedTabsHorizontalInsets = 8;
 constexpr int kTabGroupOverlapAdjustment = 2;
 
 }  // namespace
@@ -113,10 +114,9 @@
 
 gfx::Insets TabGroupStyle::GetInsetsForHeaderChip() const {
   if (base::FeatureList::IsEnabled(features::kDetachedTabs)) {
-    return gfx::Insets::TLBR(0, kCornerRadius, 0, kCornerRadius);
+    return gfx::Insets::VH(0, kDetachedTabsHorizontalInsets);
   }
-  return gfx::Insets::TLBR(kHeaderChipVerticalInset, kCornerRadius,
-                           kHeaderChipVerticalInset, kCornerRadius);
+  return gfx::Insets::VH(kHeaderChipVerticalInset, kCornerRadius);
 }
 
 int TabGroupStyle::GetTitleAdjustmentToTabGroupHeaderDesiredWidth(
diff --git a/chrome/browser/ui/views/tabs/tab_search_container.cc b/chrome/browser/ui/views/tabs/tab_search_container.cc
deleted file mode 100644
index 688b6704..0000000
--- a/chrome/browser/ui/views/tabs/tab_search_container.cc
+++ /dev/null
@@ -1,446 +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.
-
-#include "chrome/browser/ui/views/tabs/tab_search_container.h"
-
-#include <memory>
-
-#include "base/i18n/rtl.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/time/time.h"
-#include "base/types/pass_key.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_element_identifiers.h"
-#include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
-#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
-#include "chrome/browser/ui/tabs/organization/tab_organization_service.h"
-#include "chrome/browser/ui/tabs/organization/tab_organization_service_factory.h"
-#include "chrome/browser/ui/tabs/organization/tab_organization_utils.h"
-#include "chrome/browser/ui/ui_features.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/tabs/tab_search_button.h"
-#include "chrome/browser/ui/views/tabs/tab_strip.h"
-#include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
-#include "chrome/browser/ui/views/tabs/tab_strip_nudge_button.h"
-#include "chrome/browser/ui/webui/tab_search/tab_search.mojom.h"
-#include "chrome/common/chrome_features.h"
-#include "chrome/grit/generated_resources.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/metadata/metadata_impl_macros.h"
-#include "ui/gfx/animation/tween.h"
-#include "ui/gfx/vector_icon_types.h"
-#include "ui/views/animation/animation_delegate_views.h"
-#include "ui/views/layout/flex_layout.h"
-#include "ui/views/mouse_watcher.h"
-#include "ui/views/mouse_watcher_view_host.h"
-#include "ui/views/view_class_properties.h"
-
-namespace {
-
-constexpr base::TimeDelta kExpansionInDuration = base::Milliseconds(500);
-constexpr base::TimeDelta kExpansionOutDuration = base::Milliseconds(250);
-constexpr base::TimeDelta kFlatEdgeInDuration = base::Milliseconds(400);
-constexpr base::TimeDelta kFlatEdgeOutDuration = base::Milliseconds(250);
-constexpr base::TimeDelta kOpacityInDuration = base::Milliseconds(300);
-constexpr base::TimeDelta kOpacityOutDuration = base::Milliseconds(100);
-constexpr base::TimeDelta kOpacityDelay = base::Milliseconds(100);
-constexpr base::TimeDelta kShowDuration = base::Seconds(16);
-constexpr int kSpaceBetweenButtons = 2;
-
-Edge GetFlatEdge(bool is_search_button, bool tab_search_before_chips) {
-  const bool is_rtl = base::i18n::IsRTL();
-  if ((!is_search_button && tab_search_before_chips) ||
-      (is_search_button && !tab_search_before_chips)) {
-    return is_rtl ? Edge::kRight : Edge::kLeft;
-  }
-  return is_rtl ? Edge::kLeft : Edge::kRight;
-}
-
-}  // namespace
-
-TabSearchContainer::TabOrganizationAnimationSession::
-    TabOrganizationAnimationSession(
-        TabStripNudgeButton* button,
-        TabSearchContainer* container,
-        AnimationSessionType session_type,
-        base::OnceCallback<void()> on_animation_ended)
-    : button_(button),
-      container_(container),
-      expansion_animation_(container),
-      flat_edge_animation_(container),
-      opacity_animation_(container),
-      session_type_(session_type),
-      on_animation_ended_(std::move(on_animation_ended)) {
-  if (session_type_ == AnimationSessionType::HIDE) {
-    expansion_animation_.Reset(1);
-    flat_edge_animation_.Reset(1);
-    opacity_animation_.Reset(1);
-  }
-}
-
-TabSearchContainer::TabOrganizationAnimationSession::
-    ~TabOrganizationAnimationSession() = default;
-
-void TabSearchContainer::TabOrganizationAnimationSession::ApplyAnimationValue(
-    const gfx::Animation* animation) {
-  float value = animation->GetCurrentValue();
-  if (animation == &expansion_animation_) {
-    button_->SetWidthFactor(value);
-  } else if (animation == &flat_edge_animation_) {
-    container_->tab_search_button()->SetFlatEdgeFactor(1 - value);
-    button_->SetFlatEdgeFactor(1 - value);
-  } else if (animation == &opacity_animation_) {
-    button_->SetOpacity(value);
-  }
-}
-
-void TabSearchContainer::TabOrganizationAnimationSession::MarkAnimationDone(
-    const gfx::Animation* animation) {
-  if (animation == &expansion_animation_) {
-    expansion_animation_done_ = true;
-  } else if (animation == &flat_edge_animation_) {
-    flat_edge_animation_done_ = true;
-  } else {
-    opacity_animation_done_ = true;
-  }
-
-  if (expansion_animation_done_ && flat_edge_animation_done_ &&
-      opacity_animation_done_) {
-    if (on_animation_ended_) {
-      std::move(on_animation_ended_).Run();
-    }
-  }
-}
-
-void TabSearchContainer::TabOrganizationAnimationSession::Start() {
-  if (session_type_ ==
-      TabOrganizationAnimationSession::AnimationSessionType::SHOW) {
-    Show();
-  } else {
-    Hide();
-  }
-}
-
-void TabSearchContainer::TabOrganizationAnimationSession::
-    ResetExpansionAnimationForTesting(double value) {
-  expansion_animation_.Reset(value);
-}
-
-void TabSearchContainer::TabOrganizationAnimationSession::
-    ResetOpacityAnimationForTesting(double value) {
-  if (opacity_animation_delay_timer_.IsRunning()) {
-    opacity_animation_delay_timer_.FireNow();
-  }
-
-  opacity_animation_.Reset(value);
-}
-
-void TabSearchContainer::TabOrganizationAnimationSession::
-    ResetFlatEdgeAnimationForTesting(double value) {
-  flat_edge_animation_.Reset(value);
-}
-
-void TabSearchContainer::TabOrganizationAnimationSession::Hide() {
-  // Animate and hide existing chip.
-  if (session_type_ ==
-      TabOrganizationAnimationSession::AnimationSessionType::SHOW) {
-    if (opacity_animation_delay_timer_.IsRunning()) {
-      opacity_animation_delay_timer_.FireNow();
-    }
-    session_type_ = TabOrganizationAnimationSession::AnimationSessionType::HIDE;
-  }
-
-  expansion_animation_.SetTweenType(gfx::Tween::Type::ACCEL_20_DECEL_100);
-  opacity_animation_.SetTweenType(gfx::Tween::Type::LINEAR);
-  flat_edge_animation_.SetTweenType(gfx::Tween::Type::ACCEL_20_DECEL_100);
-
-  expansion_animation_.SetSlideDuration(
-      gfx::Animation::RichAnimationDuration(kExpansionOutDuration));
-
-  flat_edge_animation_.SetSlideDuration(
-      gfx::Animation::RichAnimationDuration(kFlatEdgeOutDuration));
-
-  opacity_animation_.SetSlideDuration(
-      gfx::Animation::RichAnimationDuration(kOpacityOutDuration));
-
-  expansion_animation_.Hide();
-  flat_edge_animation_.Hide();
-  opacity_animation_.Hide();
-}
-
-void TabSearchContainer::TabOrganizationAnimationSession::
-    ShowOpacityAnimation() {
-  opacity_animation_.Show();
-}
-
-void TabSearchContainer::TabOrganizationAnimationSession::Show() {
-  expansion_animation_.SetTweenType(gfx::Tween::Type::ACCEL_20_DECEL_100);
-  opacity_animation_.SetTweenType(gfx::Tween::Type::LINEAR);
-  flat_edge_animation_.SetTweenType(gfx::Tween::Type::LINEAR);
-
-  expansion_animation_.SetSlideDuration(
-      gfx::Animation::RichAnimationDuration(kExpansionInDuration));
-  flat_edge_animation_.SetSlideDuration(
-      gfx::Animation::RichAnimationDuration(kFlatEdgeInDuration));
-  opacity_animation_.SetSlideDuration(
-      gfx::Animation::RichAnimationDuration(kOpacityInDuration));
-
-  expansion_animation_.Show();
-  flat_edge_animation_.Show();
-
-  const base::TimeDelta delay =
-      gfx::Animation::RichAnimationDuration(kOpacityDelay);
-  opacity_animation_delay_timer_.Start(
-      FROM_HERE, delay, this,
-      &TabSearchContainer::TabOrganizationAnimationSession::
-          ShowOpacityAnimation);
-}
-
-TabSearchContainer::TabSearchContainer(bool tab_search_before_chips,
-                                       View* locked_expansion_view,
-                                       TabStrip* tab_strip)
-    : AnimationDelegateViews(this),
-      locked_expansion_view_(locked_expansion_view) {
-  TabStripController* const tab_strip_controller = tab_strip->controller();
-  browser_window_interface_ = tab_strip_controller->GetBrowserWindowInterface();
-  SetProperty(views::kElementIdentifierKey, kTabSearchContainerElementId);
-  mouse_watcher_ = std::make_unique<views::MouseWatcher>(
-      std::make_unique<views::MouseWatcherViewHost>(locked_expansion_view,
-                                                    gfx::Insets()),
-      this);
-
-  tab_organization_service_ = TabOrganizationServiceFactory::GetForProfile(
-      browser_window_interface_->GetProfile());
-  if (tab_organization_service_) {
-    tab_organization_observation_.Observe(tab_organization_service_);
-  }
-
-  // Edge adjacent to new tab button should be rounded and opposite edge
-  // should animate to flat on chip show.
-  std::unique_ptr<TabSearchButton> tab_search_button =
-      std::make_unique<TabSearchButton>(
-          browser_window_interface_, Edge::kNone,
-          GetFlatEdge(true, tab_search_before_chips));
-  tab_search_button->SetProperty(views::kCrossAxisAlignmentKey,
-                                 views::LayoutAlignment::kCenter);
-  tab_search_button_ = AddChildView(std::move(tab_search_button));
-
-  int tab_search_button_index = GetIndexOf(tab_search_button_).value();
-  int index = tab_search_before_chips ? tab_search_button_index + 1
-                                      : tab_search_button_index;
-  // TODO(crbug.com/40925230): Consider hiding the button when the request has
-  // started, vs. when the button as clicked.
-  auto_tab_group_button_ =
-      AddChildViewAt(CreateAutoTabGroupButton(tab_search_before_chips), index);
-
-  SetupButtonProperties(auto_tab_group_button_, tab_search_before_chips);
-
-  SetLayoutManager(std::make_unique<views::FlexLayout>());
-}
-
-TabSearchContainer::~TabSearchContainer() {
-  if (scoped_tab_strip_modal_ui_) {
-    scoped_tab_strip_modal_ui_.reset();
-  }
-}
-
-void TabSearchContainer::ShowTabOrganization(TabStripNudgeButton* button) {
-  if (locked_expansion_view_->IsMouseHovered()) {
-    SetLockedExpansionMode(LockedExpansionMode::kWillShow, button);
-  }
-  if (locked_expansion_mode_ == LockedExpansionMode::kNone) {
-    ExecuteShowTabOrganization(button);
-  }
-}
-
-void TabSearchContainer::HideTabOrganization(TabStripNudgeButton* button) {
-  if (locked_expansion_view_->IsMouseHovered()) {
-    SetLockedExpansionMode(LockedExpansionMode::kWillHide, button);
-  }
-  if (locked_expansion_mode_ == LockedExpansionMode::kNone) {
-    ExecuteHideTabOrganization(button);
-  }
-}
-
-void TabSearchContainer::SetLockedExpansionModeForTesting(
-    LockedExpansionMode mode,
-    TabStripNudgeButton* button) {
-  SetLockedExpansionMode(mode, button);
-}
-
-void TabSearchContainer::OnAutoTabGroupButtonClicked() {
-  tab_organization_service_->OnActionUIAccepted(
-      browser_window_interface_->GetBrowserForMigrationOnly());
-
-  // Force hide the button when pressed, bypassing locked expansion mode.
-  ExecuteHideTabOrganization(auto_tab_group_button_);
-}
-
-void TabSearchContainer::OnAutoTabGroupButtonDismissed() {
-  tab_organization_service_->OnActionUIDismissed(
-      browser_window_interface_->GetBrowserForMigrationOnly());
-
-  // Force hide the button when pressed, bypassing locked expansion mode.
-  ExecuteHideTabOrganization(auto_tab_group_button_);
-}
-
-void TabSearchContainer::OnOrganizeButtonTimeout(TabStripNudgeButton* button) {
-  // Hide the button if not pressed. Use locked expansion mode to avoid
-  // disrupting the user.
-  HideTabOrganization(button);
-}
-
-void TabSearchContainer::MouseMovedOutOfHost() {
-  SetLockedExpansionMode(LockedExpansionMode::kNone, nullptr);
-}
-
-void TabSearchContainer::AnimationCanceled(const gfx::Animation* animation) {
-  AnimationEnded(animation);
-}
-
-void TabSearchContainer::AnimationEnded(const gfx::Animation* animation) {
-  animation_session_->ApplyAnimationValue(animation);
-  animation_session_->MarkAnimationDone(animation);
-}
-
-void TabSearchContainer::AnimationProgressed(const gfx::Animation* animation) {
-  animation_session_->ApplyAnimationValue(animation);
-}
-
-void TabSearchContainer::OnToggleActionUIState(const Browser* browser,
-                                               bool should_show) {
-  CHECK(tab_organization_service_);
-
-  if (locked_expansion_mode_ != LockedExpansionMode::kNone) {
-    return;
-  }
-
-  if (should_show && browser_window_interface_ == browser) {
-    ShowTabOrganization(auto_tab_group_button_);
-  } else {
-    HideTabOrganization(auto_tab_group_button_);
-  }
-}
-
-void TabSearchContainer::SetLockedExpansionMode(LockedExpansionMode mode,
-                                                TabStripNudgeButton* button) {
-  if (mode == LockedExpansionMode::kNone) {
-    if (locked_expansion_mode_ == LockedExpansionMode::kWillShow) {
-      ExecuteShowTabOrganization(locked_expansion_button_);
-    } else if (locked_expansion_mode_ == LockedExpansionMode::kWillHide) {
-      ExecuteHideTabOrganization(locked_expansion_button_);
-    }
-    locked_expansion_button_ = nullptr;
-  } else {
-    locked_expansion_button_ = button;
-    mouse_watcher_->Start(GetWidget()->GetNativeWindow());
-  }
-  locked_expansion_mode_ = mode;
-}
-
-void TabSearchContainer::ExecuteShowTabOrganization(
-    TabStripNudgeButton* button) {
-  if (browser_window_interface_ && (button == auto_tab_group_button_) &&
-      !TabOrganizationUtils::GetInstance()->IsEnabled(
-          browser_window_interface_->GetProfile())) {
-    return;
-  }
-
-  TabStripModel* const tab_strip_model =
-      browser_window_interface_->GetTabStripModel();
-  // If the tab strip already has a modal UI showing, exit early.
-  if (!tab_strip_model->CanShowModalUI()) {
-    return;
-  }
-
-  scoped_tab_strip_modal_ui_ = tab_strip_model->ShowModalUI();
-
-  animation_session_ = std::make_unique<TabOrganizationAnimationSession>(
-      button, this, TabOrganizationAnimationSession::AnimationSessionType::SHOW,
-      base::BindOnce(&TabSearchContainer::OnAnimationSessionEnded,
-                     base::Unretained(this)));
-  animation_session_->Start();
-
-  hide_tab_organization_timer_.Start(
-      FROM_HERE, kShowDuration,
-      base::BindOnce(&TabSearchContainer::OnOrganizeButtonTimeout,
-                     base::Unretained(this), button));
-}
-
-void TabSearchContainer::ExecuteHideTabOrganization(
-    TabStripNudgeButton* button) {
-  // Hide the current animation if the shown button is the same button. Do not
-  // create a new animation session.
-  if (animation_session_ &&
-      animation_session_->session_type() ==
-          TabOrganizationAnimationSession::AnimationSessionType::SHOW &&
-      animation_session_->button() == button) {
-    hide_tab_organization_timer_.Stop();
-    animation_session_->Hide();
-    return;
-  }
-
-  if (!button->GetVisible()) {
-    return;
-  }
-
-  // Stop the timer since the chip might be getting hidden on user actions like
-  // dismissal or click and not timeout.
-  hide_tab_organization_timer_.Stop();
-  animation_session_ = std::make_unique<TabOrganizationAnimationSession>(
-      button, this, TabOrganizationAnimationSession::AnimationSessionType::HIDE,
-      base::BindOnce(&TabSearchContainer::OnAnimationSessionEnded,
-                     base::Unretained(this)));
-  animation_session_->Start();
-}
-
-void TabSearchContainer::OnAnimationSessionEnded() {
-  // If the button went from shown -> hidden, unblock the tab strip from
-  // showing other modal UIs.
-  if (animation_session_->session_type() ==
-      TabOrganizationAnimationSession::AnimationSessionType::HIDE) {
-    scoped_tab_strip_modal_ui_.reset();
-  }
-
-  animation_session_.reset();
-}
-
-std::unique_ptr<TabStripNudgeButton>
-TabSearchContainer::CreateAutoTabGroupButton(bool tab_search_before_chips) {
-  auto button = std::make_unique<TabStripNudgeButton>(
-      browser_window_interface_,
-      base::BindRepeating(&TabSearchContainer::OnAutoTabGroupButtonClicked,
-                          base::Unretained(this)),
-      base::BindRepeating(&TabSearchContainer::OnAutoTabGroupButtonDismissed,
-                          base::Unretained(this)),
-      l10n_util::GetStringUTF16(IDS_TAB_ORGANIZE), kAutoTabGroupButtonElementId,
-      GetFlatEdge(false, tab_search_before_chips), gfx::VectorIcon::EmptyIcon(),
-      /*show_close_button=*/true);
-  button->SetTooltipText(l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_ORGANIZE));
-  button->GetViewAccessibility().SetName(
-      l10n_util::GetStringUTF16(IDS_ACCNAME_TAB_ORGANIZE));
-  button->SetProperty(views::kCrossAxisAlignmentKey,
-                      views::LayoutAlignment::kCenter);
-  return button;
-}
-
-void TabSearchContainer::SetupButtonProperties(TabStripNudgeButton* button,
-                                               bool tab_search_before_chips) {
-  // Set the margins for the button
-  gfx::Insets margin;
-  if (tab_search_before_chips) {
-    margin.set_left(kSpaceBetweenButtons);
-  } else {
-    margin.set_right(kSpaceBetweenButtons);
-  }
-  button->SetProperty(views::kMarginsKey, margin);
-
-  // Set opacity for the button
-  button->SetOpacity(0);
-}
-
-BEGIN_METADATA(TabSearchContainer)
-END_METADATA
diff --git a/chrome/browser/ui/views/tabs/tab_search_container.h b/chrome/browser/ui/views/tabs/tab_search_container.h
deleted file mode 100644
index 20b5cf4..0000000
--- a/chrome/browser/ui/views/tabs/tab_search_container.h
+++ /dev/null
@@ -1,178 +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.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_TABS_TAB_SEARCH_CONTAINER_H_
-#define CHROME_BROWSER_UI_VIEWS_TABS_TAB_SEARCH_CONTAINER_H_
-
-#include <memory>
-
-#include "base/memory/raw_ptr.h"
-#include "base/timer/timer.h"
-#include "chrome/browser/ui/tabs/organization/tab_organization_observer.h"
-#include "ui/base/metadata/metadata_header_macros.h"
-#include "ui/gfx/animation/animation.h"
-#include "ui/gfx/animation/slide_animation.h"
-#include "ui/views/animation/animation_delegate_views.h"
-#include "ui/views/mouse_watcher.h"
-#include "ui/views/view.h"
-
-enum class Edge;
-class BrowserWindowInterface;
-class TabStripNudgeButton;
-class TabOrganizationService;
-class TabSearchButton;
-class TabStrip;
-class ScopedTabStripModalUI;
-
-enum class LockedExpansionMode {
-  kNone = 0,
-  kWillShow,
-  kWillHide,
-};
-
-class TabSearchContainer : public views::View,
-                           public views::AnimationDelegateViews,
-                           public TabOrganizationObserver,
-                           public views::MouseWatcherListener {
-  METADATA_HEADER(TabSearchContainer, views::View)
-
- public:
-  class TabOrganizationAnimationSession {
-   public:
-    enum class AnimationSessionType { SHOW, HIDE };
-
-    TabOrganizationAnimationSession(
-        TabStripNudgeButton* button,
-        TabSearchContainer* container,
-        AnimationSessionType session_type,
-        base::OnceCallback<void()> on_animation_ended);
-    ~TabOrganizationAnimationSession();
-    void ApplyAnimationValue(const gfx::Animation* animation);
-    void MarkAnimationDone(const gfx::Animation* animation);
-    void Start();
-    AnimationSessionType session_type() { return session_type_; }
-
-    gfx::SlideAnimation* expansion_animation() { return &expansion_animation_; }
-    void ResetExpansionAnimationForTesting(double value);
-    void ResetOpacityAnimationForTesting(double value);
-    void ResetFlatEdgeAnimationForTesting(double value);
-
-    void Hide();
-    TabStripNudgeButton* button() { return button_; }
-
-   private:
-    void ShowOpacityAnimation();
-    void Show();
-    raw_ptr<TabStripNudgeButton> button_;
-    raw_ptr<TabSearchContainer> container_;
-
-    gfx::SlideAnimation expansion_animation_;
-    gfx::SlideAnimation flat_edge_animation_;
-    gfx::SlideAnimation opacity_animation_;
-
-    bool expansion_animation_done_ = false;
-    bool flat_edge_animation_done_ = false;
-    bool opacity_animation_done_ = false;
-    AnimationSessionType session_type_;
-
-    // Timer for initiating the opacity animation during show.
-    base::OneShotTimer opacity_animation_delay_timer_;
-
-    // Callback to container after animation has ended.
-    base::OnceCallback<void()> on_animation_ended_;
-  };
-
-  // TODO(382097906): Pull tabslotcontroller out of tabstrip and pass
-  // that instead.
-  TabSearchContainer(bool tab_search_before_chips,
-                     View* locked_expansion_view,
-                     TabStrip* tab_strip);
-  TabSearchContainer(const TabSearchContainer&) = delete;
-  TabSearchContainer& operator=(const TabSearchContainer&) = delete;
-  ~TabSearchContainer() override;
-
-  TabStripNudgeButton* auto_tab_group_button() {
-    return auto_tab_group_button_;
-  }
-
-  TabSearchButton* tab_search_button() { return tab_search_button_; }
-
-  TabOrganizationAnimationSession* animation_session_for_testing() {
-    return animation_session_.get();
-  }
-
-  TabOrganizationService* tab_organization_service_for_testing() {
-    return tab_organization_service_;
-  }
-
-  void ShowTabOrganization(TabStripNudgeButton* button);
-  void HideTabOrganization(TabStripNudgeButton* button);
-  void SetLockedExpansionModeForTesting(LockedExpansionMode mode,
-                                        TabStripNudgeButton* button);
-
-  void OnAutoTabGroupButtonClicked();
-  void OnAutoTabGroupButtonDismissed();
-
-  void OnOrganizeButtonTimeout(TabStripNudgeButton* button);
-
-  // views::MouseWatcherListener:
-  void MouseMovedOutOfHost() override;
-
-  // views::AnimationDelegateViews
-  void AnimationCanceled(const gfx::Animation* animation) override;
-  void AnimationEnded(const gfx::Animation* animation) override;
-  void AnimationProgressed(const gfx::Animation* animation) override;
-
-  // TabOrganizationObserver
-  void OnToggleActionUIState(const Browser* browser, bool should_show) override;
-
- private:
-  void SetLockedExpansionMode(LockedExpansionMode mode,
-                              TabStripNudgeButton* button);
-  void ExecuteShowTabOrganization(TabStripNudgeButton* button);
-  void ExecuteHideTabOrganization(TabStripNudgeButton* button);
-
-  void OnAnimationSessionEnded();
-
-  std::unique_ptr<TabStripNudgeButton> CreateAutoTabGroupButton(
-      bool tab_search_before_chips);
-  void SetupButtonProperties(TabStripNudgeButton* button,
-                             bool tab_search_before_chips);
-
-  // View where, if the mouse is currently over its bounds, the expansion state
-  // will not change. Changes will be staged until after the mouse exits the
-  // bounds of this View.
-  raw_ptr<View, DanglingUntriaged> locked_expansion_view_;
-
-  // The button currently holding the lock to be shown/hidden.
-  raw_ptr<TabStripNudgeButton> locked_expansion_button_ = nullptr;
-  raw_ptr<TabStripNudgeButton, DanglingUntriaged> auto_tab_group_button_ =
-      nullptr;
-  raw_ptr<TabSearchButton, DanglingUntriaged> tab_search_button_ = nullptr;
-  raw_ptr<TabOrganizationService, DanglingUntriaged> tab_organization_service_ =
-      nullptr;
-
-  raw_ptr<BrowserWindowInterface> browser_window_interface_;
-
-  // Timer for hiding tab_organization_button_ after show.
-  base::OneShotTimer hide_tab_organization_timer_;
-
-  // When locked, the container is unable to change its expanded state. Changes
-  // will be staged until after this is unlocked.
-  LockedExpansionMode locked_expansion_mode_ = LockedExpansionMode::kNone;
-
-  // MouseWatcher is used to lock and unlock the expansion state of this
-  // container.
-  std::unique_ptr<views::MouseWatcher> mouse_watcher_;
-
-  base::ScopedObservation<TabOrganizationService, TabOrganizationObserver>
-      tab_organization_observation_{this};
-
-  // Prevents other features from showing tabstrip-modal UI.
-  std::unique_ptr<ScopedTabStripModalUI> scoped_tab_strip_modal_ui_;
-
-  std::unique_ptr<TabOrganizationAnimationSession> animation_session_;
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_TABS_TAB_SEARCH_CONTAINER_H_
diff --git a/chrome/browser/ui/views/tabs/tab_search_container_browsertest.cc b/chrome/browser/ui/views/tabs/tab_search_container_browsertest.cc
deleted file mode 100644
index 313417c6..0000000
--- a/chrome/browser/ui/views/tabs/tab_search_container_browsertest.cc
+++ /dev/null
@@ -1,471 +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.
-
-#include "chrome/browser/ui/views/tabs/tab_search_container.h"
-
-#include "base/feature_list.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/optimization_guide/browser_test_util.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_element_identifiers.h"
-#include "chrome/browser/ui/tabs/organization/tab_organization_service.h"
-#include "chrome/browser/ui/tabs/organization/tab_organization_utils.h"
-#include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/ui_features.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/interaction/browser_elements_views.h"
-#include "chrome/browser/ui/views/tabs/tab_search_button.h"
-#include "chrome/common/chrome_features.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/views/chrome_views_test_base.h"
-#include "components/optimization_guide/core/model_execution/model_execution_features.h"
-#include "components/optimization_guide/core/optimization_guide_features.h"
-#include "components/signin/public/identity_manager/identity_test_utils.h"
-#include "content/public/test/browser_test.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/ui_base_features.h"
-#include "ui/events/test/event_generator.h"
-#include "ui/gfx/animation/slide_animation.h"
-#include "ui/views/test/views_test_utils.h"
-#include "ui/views/view_utils.h"
-
-namespace {
-
-using ::testing::AssertionFailure;
-using ::testing::AssertionResult;
-using ::testing::AssertionSuccess;
-
-class TabSearchContainerBrowserTest : public InProcessBrowserTest {
- public:
-  TabSearchContainerBrowserTest() {
-    feature_list_.InitWithFeatures(
-        /*enabled_features=*/{features::kTabOrganization},
-        /*disabled_features=*/{features::kGlic});
-    TabOrganizationUtils::GetInstance()->SetIgnoreOptGuideForTesting(true);
-  }
-
-  TabStripModel* tab_strip_model() { return browser()->tab_strip_model(); }
-
-  BrowserView* browser_view() {
-    return BrowserView::GetBrowserViewForBrowser(browser());
-  }
-
-  TabSearchContainer* tab_search_container() {
-    return BrowserElementsViews::From(browser())->GetViewAs<TabSearchContainer>(
-        kTabSearchContainerElementId);
-  }
-
-  // Returns an assertion result that the expansion animation is closing.
-  AssertionResult ExpansionAnimationIsClosing() {
-    if (!tab_search_container()) {
-      return AssertionFailure() << "tab_search_container is null.";
-    }
-    if (!tab_search_container()->animation_session_for_testing()) {
-      return AssertionFailure() << "animation_session_for_testing is null.";
-    }
-    if (!tab_search_container()
-             ->animation_session_for_testing()
-             ->expansion_animation()) {
-      return AssertionFailure() << "expansion_animation is null.";
-    }
-    return tab_search_container()
-                   ->animation_session_for_testing()
-                   ->expansion_animation()
-                   ->IsClosing()
-               ? AssertionSuccess()
-               : AssertionFailure() << "expansion_animation is not closing.";
-  }
-
- protected:
-  void ResetAnimation(int value) {
-    if (tab_search_container()->animation_session_for_testing()) {
-      tab_search_container()
-          ->animation_session_for_testing()
-          ->ResetOpacityAnimationForTesting(value);
-    }
-    if (tab_search_container()->animation_session_for_testing()) {
-      tab_search_container()
-          ->animation_session_for_testing()
-          ->ResetExpansionAnimationForTesting(value);
-    }
-    if (tab_search_container()->animation_session_for_testing()) {
-      tab_search_container()
-          ->animation_session_for_testing()
-          ->ResetFlatEdgeAnimationForTesting(value);
-    }
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-// TODO(crbug.com/413441658): Flaky on Windows 10 builds.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_TogglesActionUIState DISABLED_TogglesActionUIState
-#else
-#define MAYBE_TogglesActionUIState TogglesActionUIState
-#endif
-IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
-                       MAYBE_TogglesActionUIState) {
-  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
-
-  TabOrganizationService* service =
-      tab_search_container()->tab_organization_service_for_testing();
-
-  tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kNone, nullptr);
-  tab_strip_model()->ForceShowingModalUIForTesting(false);
-  service->OnTriggerOccured(browser());
-
-  ASSERT_TRUE(tab_search_container()
-                  ->animation_session_for_testing()
-                  ->expansion_animation()
-                  ->IsShowing());
-}
-
-// TODO(crbug.com/413441658): Flaky on Windows 10 builds.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_TogglesActionUIStateOnlyInCorrectBrowser \
-  DISABLED_TogglesActionUIStateOnlyInCorrectBrowser
-#else
-#define MAYBE_TogglesActionUIStateOnlyInCorrectBrowser \
-  TogglesActionUIStateOnlyInCorrectBrowser
-#endif
-IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
-                       MAYBE_TogglesActionUIStateOnlyInCorrectBrowser) {
-  Browser* const second_browser = CreateBrowser(browser()->profile());
-  TabSearchContainer* const second_search_container =
-      BrowserElementsViews::From(second_browser)
-          ->GetViewAs<TabSearchContainer>(kTabSearchContainerElementId);
-
-  ASSERT_FALSE(second_search_container->animation_session_for_testing());
-
-  TabOrganizationService* service =
-      tab_search_container()->tab_organization_service_for_testing();
-  // Same profile -> same service.
-  ASSERT_EQ(service,
-            second_search_container->tab_organization_service_for_testing());
-
-  tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kNone, nullptr);
-  tab_strip_model()->ForceShowingModalUIForTesting(false);
-  second_search_container->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kNone, nullptr);
-  second_browser->tab_strip_model()->ForceShowingModalUIForTesting(false);
-  service->OnTriggerOccured(browser());
-
-  EXPECT_TRUE(tab_search_container()
-                  ->animation_session_for_testing()
-                  ->expansion_animation()
-                  ->IsShowing());
-  EXPECT_FALSE(second_search_container->animation_session_for_testing());
-}
-
-// TODO(crbug.com/413441658): Flaky on Windows 10 builds.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_DoesntShowIfTabStripModalUIExists \
-  DISABLED_DoesntShowIfTabStripModalUIExists
-#else
-#define MAYBE_DoesntShowIfTabStripModalUIExists \
-  DoesntShowIfTabStripModalUIExists
-#endif
-IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
-                       MAYBE_DoesntShowIfTabStripModalUIExists) {
-  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
-
-  tab_strip_model()->ForceShowingModalUIForTesting(true);
-  tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kNone, nullptr);
-  tab_search_container()->ShowTabOrganization(
-      tab_search_container()->auto_tab_group_button());
-
-  EXPECT_FALSE(tab_search_container()->animation_session_for_testing());
-
-  tab_strip_model()->ForceShowingModalUIForTesting(false);
-  tab_search_container()->ShowTabOrganization(
-      tab_search_container()->auto_tab_group_button());
-
-  EXPECT_TRUE(tab_search_container()
-                  ->animation_session_for_testing()
-                  ->expansion_animation()
-                  ->IsShowing());
-}
-
-// TODO(crbug.com/413441658): Flaky on Windows 10 builds.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_BlocksTabStripModalUIWhileShown \
-  DISABLED_BlocksTabStripModalUIWhileShown
-#else
-#define MAYBE_BlocksTabStripModalUIWhileShown BlocksTabStripModalUIWhileShown
-#endif
-IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
-                       MAYBE_BlocksTabStripModalUIWhileShown) {
-  ASSERT_TRUE(browser()->tab_strip_model()->CanShowModalUI());
-
-  tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kNone, nullptr);
-  tab_search_container()->ShowTabOrganization(
-      tab_search_container()->auto_tab_group_button());
-
-  EXPECT_FALSE(browser()->tab_strip_model()->CanShowModalUI());
-
-  ResetAnimation(1);
-
-  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
-
-  EXPECT_FALSE(browser()->tab_strip_model()->CanShowModalUI());
-
-  tab_search_container()->HideTabOrganization(
-      tab_search_container()->auto_tab_group_button());
-
-  EXPECT_FALSE(browser()->tab_strip_model()->CanShowModalUI());
-
-  ResetAnimation(0);
-
-  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
-
-  EXPECT_TRUE(browser()->tab_strip_model()->CanShowModalUI());
-}
-
-IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, DelaysShow) {
-  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
-
-  tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kWillShow,
-      tab_search_container()->auto_tab_group_button());
-  tab_search_container()->ShowTabOrganization(
-      tab_search_container()->auto_tab_group_button());
-
-  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
-
-  tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kNone, nullptr);
-
-  ASSERT_TRUE(tab_search_container()
-                  ->animation_session_for_testing()
-                  ->expansion_animation()
-                  ->IsShowing());
-}
-
-// TODO(crbug.com/413441658): Flaky on Windows 10 builds.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_DelaysHide DISABLED_DelaysHide
-#else
-#define MAYBE_DelaysHide DelaysHide
-#endif
-IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest, MAYBE_DelaysHide) {
-  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
-
-  tab_search_container()->ShowTabOrganization(
-      tab_search_container()->auto_tab_group_button());
-  ResetAnimation(1);
-  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
-
-  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
-
-  tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kWillHide,
-      tab_search_container()->auto_tab_group_button());
-  tab_search_container()->HideTabOrganization(
-      tab_search_container()->auto_tab_group_button());
-
-  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
-
-  tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kNone, nullptr);
-
-  EXPECT_TRUE(ExpansionAnimationIsClosing());
-}
-
-// TODO(crbug.com/413441658): Flaky on Windows 10 builds.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_ImmediatelyHidesWhenOrganizeButtonClicked \
-  DISABLED_ImmediatelyHidesWhenOrganizeButtonClicked
-#else
-#define MAYBE_ImmediatelyHidesWhenOrganizeButtonClicked \
-  ImmediatelyHidesWhenOrganizeButtonClicked
-#endif
-IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
-                       MAYBE_ImmediatelyHidesWhenOrganizeButtonClicked) {
-  tab_search_container()->ShowTabOrganization(
-      tab_search_container()->auto_tab_group_button());
-  ResetAnimation(1);
-  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
-
-  tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kWillHide,
-      tab_search_container()->auto_tab_group_button());
-
-  tab_search_container()->OnAutoTabGroupButtonClicked();
-
-  EXPECT_TRUE(ExpansionAnimationIsClosing());
-}
-
-// TODO(crbug.com/413441658): Flaky on Windows 10 builds.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_ImmediatelyHidesWhenOrganizeButtonDismissed \
-  DISABLED_ImmediatelyHidesWhenOrganizeButtonDismissed
-#else
-#define MAYBE_ImmediatelyHidesWhenOrganizeButtonDismissed \
-  ImmediatelyHidesWhenOrganizeButtonDismissed
-#endif
-IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
-                       MAYBE_ImmediatelyHidesWhenOrganizeButtonDismissed) {
-  tab_search_container()->ShowTabOrganization(
-      tab_search_container()->auto_tab_group_button());
-  ResetAnimation(1);
-  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
-
-  tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kWillHide,
-      tab_search_container()->auto_tab_group_button());
-
-  tab_search_container()->OnAutoTabGroupButtonDismissed();
-
-  EXPECT_TRUE(ExpansionAnimationIsClosing());
-}
-
-// TODO(crbug.com/414839512): Fix flaky test.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_DelayedHidesWhenOrganizeButtonTimesOut \
-  DISABLED_DelayedHidesWhenOrganizeButtonTimesOut
-#else
-#define MAYBE_DelayedHidesWhenOrganizeButtonTimesOut \
-  DelayedHidesWhenOrganizeButtonTimesOut
-#endif
-IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
-                       MAYBE_DelayedHidesWhenOrganizeButtonTimesOut) {
-  // RunScheduledLayout() is needed due to widget auto-resize.
-  views::test::RunScheduledLayout(tab_search_container());
-
-  tab_search_container()->ShowTabOrganization(
-      tab_search_container()->auto_tab_group_button());
-  ResetAnimation(1);
-  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
-
-  tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kWillHide,
-      tab_search_container()->auto_tab_group_button());
-
-  tab_search_container()->OnOrganizeButtonTimeout(
-      tab_search_container()->auto_tab_group_button());
-
-  EXPECT_FALSE(tab_search_container()->animation_session_for_testing());
-
-  tab_search_container()->SetLockedExpansionModeForTesting(
-      LockedExpansionMode::kNone,
-      tab_search_container()->auto_tab_group_button());
-
-  EXPECT_TRUE(ExpansionAnimationIsClosing());
-}
-
-IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
-                       LogsSuccessWhenAutoTabGroupsButtonClicked) {
-  base::HistogramTester histogram_tester;
-
-  tab_search_container()->ShowTabOrganization(
-      tab_search_container()->auto_tab_group_button());
-  ResetAnimation(1);
-  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
-
-  TabOrganizationService* service =
-      tab_search_container()->tab_organization_service_for_testing();
-
-  service->OnTriggerOccured(browser());
-
-  tab_search_container()->OnAutoTabGroupButtonClicked();
-}
-
-IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
-                       LogsFailureWhenAutoTabGroupsButtonDismissed) {
-  base::HistogramTester histogram_tester;
-
-  tab_search_container()->ShowTabOrganization(
-      tab_search_container()->auto_tab_group_button());
-  ResetAnimation(1);
-  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
-
-  TabOrganizationService* service =
-      tab_search_container()->tab_organization_service_for_testing();
-
-  service->OnTriggerOccured(browser());
-
-  tab_search_container()->OnAutoTabGroupButtonDismissed();
-}
-
-// TODO(crbug.com/413441658): Flaky on Windows 10 builds.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_LogsFailureWhenAutoTabGroupsButtonTimeout \
-  DISABLED_LogsFailureWhenAutoTabGroupsButtonTimeout
-#else
-#define MAYBE_LogsFailureWhenAutoTabGroupsButtonTimeout \
-  LogsFailureWhenAutoTabGroupsButtonTimeout
-#endif
-IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
-                       MAYBE_LogsFailureWhenAutoTabGroupsButtonTimeout) {
-  base::HistogramTester histogram_tester;
-
-  tab_search_container()->ShowTabOrganization(
-      tab_search_container()->auto_tab_group_button());
-  ResetAnimation(1);
-  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
-
-  TabOrganizationService* service =
-      tab_search_container()->tab_organization_service_for_testing();
-
-  service->OnTriggerOccured(browser());
-
-  tab_search_container()->OnOrganizeButtonTimeout(
-      tab_search_container()->auto_tab_group_button());
-}
-
-// TODO(crbug.com/413441658): Flaky on Windows 10 builds.
-#if BUILDFLAG(IS_WIN)
-#define MAYBE_HidesAutoTabGroupButtonFromHalfway \
-  DISABLED_HidesAutoTabGroupButtonFromHalfway
-#else
-#define MAYBE_HidesAutoTabGroupButtonFromHalfway \
-  HidesAutoTabGroupButtonFromHalfway
-#endif
-IN_PROC_BROWSER_TEST_F(TabSearchContainerBrowserTest,
-                       MAYBE_HidesAutoTabGroupButtonFromHalfway) {
-  ASSERT_FALSE(tab_search_container()->animation_session_for_testing());
-
-  tab_search_container()->ShowTabOrganization(
-      tab_search_container()->auto_tab_group_button());
-
-  ASSERT_TRUE(tab_search_container()
-                  ->animation_session_for_testing()
-                  ->expansion_animation()
-                  ->IsShowing());
-
-  gfx::SlideAnimation* expansion_animation =
-      tab_search_container()
-          ->animation_session_for_testing()
-          ->expansion_animation();
-
-  gfx::AnimationTestApi animation_api(expansion_animation);
-  base::TimeTicks now = base::TimeTicks::Now();
-  animation_api.SetStartTime(now);
-  animation_api.Step(now + (expansion_animation->GetSlideDuration() / 2));
-
-  double expanded_value = expansion_animation->GetCurrentValue();
-  tab_search_container()->GetWidget()->LayoutRootViewIfNecessary();
-
-  tab_search_container()->HideTabOrganization(
-      tab_search_container()->auto_tab_group_button());
-
-  EXPECT_TRUE(ExpansionAnimationIsClosing());
-
-  EXPECT_EQ(tab_search_container()
-                ->animation_session_for_testing()
-                ->expansion_animation()
-                ->GetCurrentValue(),
-            expanded_value);
-}
-
-}  // namespace
diff --git a/chrome/browser/ui/views/tabs/tab_search_container_unittest.cc b/chrome/browser/ui/views/tabs/tab_search_container_unittest.cc
deleted file mode 100644
index 69ee145..0000000
--- a/chrome/browser/ui/views/tabs/tab_search_container_unittest.cc
+++ /dev/null
@@ -1,167 +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.
-
-#include "chrome/browser/ui/views/tabs/tab_search_container.h"
-
-#include <memory>
-
-#include "base/feature_list.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
-#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
-#include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h"
-#include "chrome/browser/ui/tabs/organization/tab_organization_service.h"
-#include "chrome/browser/ui/tabs/organization/tab_organization_utils.h"
-#include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
-#include "chrome/browser/ui/ui_features.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/tabs/fake_tab_slot_controller.h"
-#include "chrome/browser/ui/views/tabs/tab_hover_card_controller.h"
-#include "chrome/browser/ui/views/tabs/tab_search_button.h"
-#include "chrome/browser/ui/views/tabs/tab_strip_nudge_button.h"
-#include "chrome/common/chrome_features.h"
-#include "chrome/test/base/testing_profile.h"
-#include "chrome/test/views/chrome_views_test_base.h"
-#include "components/optimization_guide/core/model_execution/model_execution_features.h"
-#include "components/optimization_guide/core/optimization_guide_features.h"
-#include "fake_base_tab_strip_controller.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/ui_base_features.h"
-#include "ui/base/unowned_user_data/user_data_factory.h"
-#include "ui/gfx/animation/animation_test_api.h"
-
-using ::testing::NiceMock;
-
-class FakeBaseTabStripControllerWithBWI : public FakeBaseTabStripController {
- public:
-  explicit FakeBaseTabStripControllerWithBWI(
-      BrowserWindowInterface* browser_window_interface)
-      : browser_window_interface_(browser_window_interface) {}
-
-  BrowserWindowInterface* GetBrowserWindowInterface() override {
-    return browser_window_interface_;
-  }
-
- private:
-  raw_ptr<BrowserWindowInterface> browser_window_interface_;
-};
-
-class TabSearchContainerTest : public ChromeViewsTestBase {
- public:
-  void SetUp() override {
-    ChromeViewsTestBase::SetUp();
-
-    TabOrganizationUtils::GetInstance()->SetIgnoreOptGuideForTesting(true);
-    scoped_feature_list_.InitAndEnableFeature(features::kTabOrganization);
-
-    tab_strip_model_ = std::make_unique<TabStripModel>(
-        &tab_strip_model_delegate_, profile_.get());
-
-    browser_window_interface_ = std::make_unique<MockBrowserWindowInterface>();
-    ON_CALL(*browser_window_interface_, GetTabStripModel())
-        .WillByDefault(::testing::Return(tab_strip_model_.get()));
-    ON_CALL(*browser_window_interface_, GetProfile())
-        .WillByDefault(::testing::Return(profile_.get()));
-    ON_CALL(*browser_window_interface_, GetFeatures())
-        .WillByDefault(testing::ReturnRef(browser_window_features_));
-    ON_CALL(testing::Const(*browser_window_interface_), GetFeatures())
-        .WillByDefault(testing::ReturnRef(browser_window_features_));
-    ON_CALL(*browser_window_interface_, GetUnownedUserDataHost())
-        .WillByDefault(testing::ReturnRef(user_data_host_));
-
-    tab_strip_ = std::make_unique<TabStrip>(
-        std::make_unique<FakeBaseTabStripControllerWithBWI>(
-            browser_window_interface_.get()),
-        std::unique_ptr<NiceMock<TabHoverCardController>>());
-
-    locked_expansion_view_ = std::make_unique<views::View>();
-    container_before_tab_strip_ = std::make_unique<TabSearchContainer>(
-        true, locked_expansion_view_.get(), tab_strip_.get());
-    container_after_tab_strip_ = std::make_unique<TabSearchContainer>(
-        false, locked_expansion_view_.get(), tab_strip_.get());
-  }
-
- protected:
-  void ResetAnimation(int value) {
-    if (container_before_tab_strip_->animation_session_for_testing()) {
-      container_before_tab_strip_->animation_session_for_testing()
-          ->ResetOpacityAnimationForTesting(value);
-    }
-    if (container_before_tab_strip_->animation_session_for_testing()) {
-      container_before_tab_strip_->animation_session_for_testing()
-          ->ResetExpansionAnimationForTesting(value);
-    }
-    if (container_before_tab_strip_->animation_session_for_testing()) {
-      container_before_tab_strip_->animation_session_for_testing()
-          ->ResetFlatEdgeAnimationForTesting(value);
-    }
-  }
-
-  std::unique_ptr<TestingProfile> profile_ = std::make_unique<TestingProfile>();
-  base::test::ScopedFeatureList scoped_feature_list_;
-  ui::UnownedUserDataHost user_data_host_;
-  std::unique_ptr<TabStripModel> tab_strip_model_;
-  TestTabStripModelDelegate tab_strip_model_delegate_;
-  std::unique_ptr<MockBrowserWindowInterface> browser_window_interface_;
-  std::unique_ptr<TabStrip> tab_strip_;
-  std::unique_ptr<views::View> locked_expansion_view_;
-  std::unique_ptr<TabSearchContainer> container_before_tab_strip_;
-  std::unique_ptr<TabSearchContainer> container_after_tab_strip_;
-
-  BrowserWindowFeatures browser_window_features_;
-
-  // Some of these tests rely on animation being enabled. This forces
-  // animation on even if it's turned off in the OS.
-  gfx::AnimationTestApi::RenderModeResetter animation_mode_reset_{
-      gfx::AnimationTestApi::SetRichAnimationRenderMode(
-          gfx::Animation::RichAnimationRenderMode::FORCE_ENABLED)};
-};
-
-TEST_F(TabSearchContainerTest, OrdersButtonsCorrectly) {
-  ASSERT_EQ(container_before_tab_strip_->tab_search_button(),
-            container_before_tab_strip_->children()[0]);
-  ASSERT_EQ(container_before_tab_strip_->auto_tab_group_button(),
-            container_before_tab_strip_->children()[1]);
-
-  ASSERT_EQ(container_after_tab_strip_->auto_tab_group_button(),
-            container_after_tab_strip_->children()[0]);
-  ASSERT_EQ(container_after_tab_strip_->tab_search_button(),
-            container_after_tab_strip_->children()[1]);
-}
-
-TEST_F(TabSearchContainerTest, ButtonsHaveFlatEdges) {
-  ASSERT_EQ(
-      Edge::kRight,
-      container_before_tab_strip_->tab_search_button()->animated_flat_edge());
-  ASSERT_EQ(Edge::kLeft, container_before_tab_strip_->auto_tab_group_button()
-                             ->animated_flat_edge());
-
-  ASSERT_EQ(
-      Edge::kLeft,
-      container_after_tab_strip_->tab_search_button()->animated_flat_edge());
-  ASSERT_EQ(Edge::kRight, container_after_tab_strip_->auto_tab_group_button()
-                              ->animated_flat_edge());
-}
-
-TEST_F(TabSearchContainerTest, AnimatesToExpanded) {
-  // Should be collapsed by default
-  ASSERT_EQ(nullptr,
-            container_before_tab_strip_->animation_session_for_testing());
-
-  ASSERT_EQ(0, container_before_tab_strip_->auto_tab_group_button()
-                   ->width_factor_for_testing());
-
-  container_before_tab_strip_->ShowTabOrganization(
-      container_before_tab_strip_->auto_tab_group_button());
-
-  ASSERT_TRUE(container_before_tab_strip_->animation_session_for_testing()
-                  ->expansion_animation()
-                  ->IsShowing());
-
-  ResetAnimation(1);
-
-  ASSERT_EQ(1, container_before_tab_strip_->auto_tab_group_button()
-                   ->width_factor_for_testing());
-}
diff --git a/chrome/browser/ui/views/tabs/tab_strip_action_container.h b/chrome/browser/ui/views/tabs/tab_strip_action_container.h
index bc069d7..973ff11 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_action_container.h
+++ b/chrome/browser/ui/views/tabs/tab_strip_action_container.h
@@ -12,7 +12,6 @@
 #include "chrome/browser/ui/views/glic/glic_button_interface.h"
 #include "chrome/browser/ui/views/tabs/glic/tab_strip_glic_actor_task_icon.h"
 #include "chrome/browser/ui/views/tabs/glic/tab_strip_glic_button.h"
-#include "chrome/browser/ui/views/tabs/tab_search_container.h"
 #include "chrome/common/buildflags.h"
 #include "ui/gfx/animation/animation.h"
 #include "ui/gfx/animation/slide_animation.h"
@@ -31,6 +30,12 @@
 class BrowserWindowInterface;
 class GlicAndActorButtonsContainer;
 
+enum class LockedExpansionMode {
+  kNone = 0,
+  kWillShow,
+  kWillHide,
+};
+
 class TabStripActionContainer : public views::View,
                                 public views::AnimationDelegateViews,
                                 public views::MouseWatcherListener,
diff --git a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller_interactive_uitest.cc
index 30ab389..8212f62 100644
--- a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller_interactive_uitest.cc
@@ -512,29 +512,6 @@
       WaitForHide(TabHoverCardBubbleView::kHoverCardBubbleElementId));
 }
 
-IN_PROC_BROWSER_TEST_F(VerticalTabStripControllerInteractiveUiTest,
-                       ScrollingUnpinnedContainerClosesTabGroupEditorBubble) {
-  RunTestSequence(
-      WaitForShow(kVerticalTabStripBottomContainerElementId), Do([this]() {
-        browser()->tab_strip_model()->ExecuteContextMenuCommand(
-            browser()->tab_strip_model()->active_index(),
-            TabStripModel::ContextMenuCommand::
-                CommandAddToNewGroupFromMenuItem);
-      }),
-      WaitForShow(kTabGroupHeaderElementId),
-      WaitForShow(kTabGroupEditorBubbleId), Do([this]() {
-        views::View* tab_strip_view =
-            BrowserView::GetBrowserViewForBrowser(browser())
-                ->vertical_tab_strip_region_view_for_testing()
-                ->GetTabStripView();
-        VerticalTabStripView* vertical_tab_strip_view =
-            views::AsViewClass<VerticalTabStripView>(tab_strip_view);
-        vertical_tab_strip_view->unpinned_tabs_scroll_view_for_testing()
-            ->ScrollByOffset({0, -100});
-      }),
-      WaitForHide(kTabGroupEditorBubbleId));
-}
-
 #if BUILDFLAG(IS_WIN)
 #define MAYBE_MousePressHidesHoverCard DISABLED_MousePressHidesHoverCard
 #else
diff --git a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.cc b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.cc
index 7d5932f6..762711a 100644
--- a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.cc
+++ b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h"
 #include "chrome/browser/ui/views/tabs/tab_hover_card_controller.h"
 #include "chrome/browser/ui/views/tabs/vertical/tab_collection_node.h"
 #include "chrome/browser/ui/views/tabs/vertical/vertical_pinned_tab_container_view.h"
@@ -28,7 +27,6 @@
 #include "ui/views/background.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/separator.h"
-#include "ui/views/interaction/element_tracker_views.h"
 #include "ui/views/layout/delegating_layout_manager.h"
 #include "ui/views/layout/layout_types.h"
 #include "ui/views/layout/proposed_layout.h"
@@ -377,8 +375,8 @@
   scroll_view->SetVerticalScrollBar(std::make_unique<VerticalTabStripScrollBar>(
       collection_node_->GetController()->GetStateController()));
   callback_subscriptions_.emplace_back(scroll_view->AddContentsScrolledCallback(
-      base::BindRepeating(&VerticalTabStripView::OnContentsScrolled,
-                          base::Unretained(this), scroll_view)));
+      base::BindRepeating(&VerticalTabStripView::HideHoverCardOnScroll,
+                          base::Unretained(this))));
 }
 
 void VerticalTabStripView::ResetCollectionNode() {
@@ -437,30 +435,17 @@
   return GetWidget() ? GetWidget()->ShouldPaintAsActive() : true;
 }
 
-void VerticalTabStripView::OnContentsScrolled(views::ScrollView* scroll_view) {
+void VerticalTabStripView::HideHoverCardOnScroll() {
   if (!collection_node_) {
     return;
   }
 
-  // Hide the hover card when the user scrolls the tab strip.
   if (TabHoverCardController* hover_card_controller =
           collection_node_->GetController()->GetHoverCardController();
       hover_card_controller && hover_card_controller->IsHoverCardVisible()) {
     hover_card_controller->UpdateHoverCard(
         nullptr, TabSlotController::HoverCardUpdateType::kAnimating);
   }
-
-  // If the scroll event was from the unpinned container, also close any group
-  // editor bubble.
-  if (scroll_view == unpinned_tabs_scroll_view_) {
-    auto* const bubble_view =
-        views::ElementTrackerViews::GetInstance()->GetFirstMatchingView(
-            TabGroupEditorBubbleView::kTabGroupEditorBubbleViewId,
-            views::ElementTrackerViews::GetContextForView(this));
-    if (bubble_view) {
-      bubble_view->GetWidget()->Close();
-    }
-  }
 }
 
 BEGIN_METADATA(VerticalTabStripView)
diff --git a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.h b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.h
index 779e292..c28f99e 100644
--- a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.h
+++ b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.h
@@ -77,7 +77,7 @@
 
   bool IsFrameActive() const;
 
-  void OnContentsScrolled(views::ScrollView* scroll_view);
+  void HideHoverCardOnScroll();
 
   raw_ptr<TabCollectionNode> collection_node_ = nullptr;
   raw_ptr<views::ScrollView> pinned_tabs_scroll_view_ = nullptr;
diff --git a/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc b/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc
index 3201f73e..ada5a79 100644
--- a/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc
@@ -500,4 +500,32 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 }
 
+// Creates a view for the detailed install dialog that contains
+// WebAppIconNameAndOriginView and ImageCarouselView.
+std::unique_ptr<views::View> CreateDetailedInstallDialogView(
+    gfx::ImageSkia icon_image,
+    const std::u16string& title,
+    const GURL& start_url,
+    bool is_maskable,
+    base::WeakPtr<WebAppScreenshotFetcher> fetcher,
+    const std::u16string& description) {
+  auto detailed_view = std::make_unique<views::BoxLayoutView>();
+  detailed_view->SetOrientation(views::BoxLayout::Orientation::kVertical);
+  detailed_view->SetBetweenChildSpacing(
+      views::LayoutProvider::Get()->GetDistanceMetric(
+          views::DISTANCE_RELATED_CONTROL_VERTICAL));
+
+  detailed_view->AddChildView(WebAppIconNameAndOriginView::Create(
+      icon_image, title, start_url, is_maskable));
+
+  auto* description_label =
+      detailed_view->AddChildView(std::make_unique<views::Label>(description));
+  description_label->SetMultiLine(true);
+  description_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  description_label->SetTextStyle(views::style::STYLE_SECONDARY);
+
+  detailed_view->AddChildView(std::make_unique<ImageCarouselView>(fetcher));
+  return detailed_view;
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc b/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc
index 9464268c..c2e135a 100644
--- a/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc
@@ -184,4 +184,19 @@
   g_auto_accept_diy_dialog_for_testing = auto_accept;
 }
 
+// Creates a view for the DIY install dialog that contains the
+// input dialog.
+std::unique_ptr<views::View> CreateDiyInstallDialogView(
+    gfx::ImageSkia icon_image,
+    const std::u16string& title,
+    const GURL& start_url,
+    content::WebContents* web_contents,
+    base::RepeatingCallback<void(const std::u16string&)>
+        on_textfield_changed_callback) {
+  return std::make_unique<SiteIconTextAndOriginView>(
+      icon_image, title,
+      l10n_util::GetStringUTF16(IDS_DIY_APP_AX_BUBBLE_NAME_LABEL), start_url,
+      web_contents, std::move(on_textfield_changed_callback));
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.cc b/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.cc
index 7c5a1866..cd8ded1 100644
--- a/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.cc
@@ -7,13 +7,17 @@
 #include <memory>
 
 #include "base/check_deref.h"
+#include "base/feature_list.h"
 #include "base/functional/callback_helpers.h"
+#include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/observer_list.h"
 #include "base/strings/string_util.h"
 #include "base/task/sequenced_task_runner.h"
+#include "chrome/browser/feature_engagement/tracker_factory.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/actions/chrome_action_id.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/page_action/page_action_icon_type.h"
@@ -34,6 +38,7 @@
 #include "content/public/browser/page.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/interaction/element_tracker.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/interaction/element_tracker_views.h"
@@ -164,6 +169,11 @@
 
 WebAppInstallDialogDelegate::~WebAppInstallDialogDelegate() = default;
 
+bool WebAppInstallDialogDelegate::OnOkButtonClicked() {
+  OnAccept();
+  return true;
+}
+
 void WebAppInstallDialogDelegate::OnAccept() {
   MeasureAcceptUserActionsForInstallDialog();
   if (iph_state_ == PwaInProductHelpState::kShown) {
@@ -258,8 +268,19 @@
 void WebAppInstallDialogDelegate::OnTextFieldChangedMaybeUpdateButton(
     const std::u16string& text_field_contents) {
   text_field_contents_ = text_field_contents;
-  ui::DialogModel::Button* ok_button =
-      dialog_model()->GetButtonByUniqueId(kDiyAppsDialogOkButtonId);
+  if (!dialog_model() || !dialog_model()->host()) {
+    return;
+  }
+
+  ui::DialogModel::Button* ok_button = nullptr;
+  if (dialog_model()->HasField(kDiyAppsDialogOkButtonId)) {
+    ok_button = dialog_model()->GetButtonByUniqueId(kDiyAppsDialogOkButtonId);
+  } else if (dialog_model()->HasField(kPwaInstallDialogInstallButton)) {
+    // Use the kPwaInstallDialogInstallButton id for for the Flow view.
+    ok_button =
+        dialog_model()->GetButtonByUniqueId(kPwaInstallDialogInstallButton);
+  }
+
   CHECK(ok_button);
   dialog_model()->SetButtonEnabled(ok_button,
                                    /*enabled=*/!text_field_contents.empty());
diff --git a/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h b/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h
index 0723c106c..a058e45 100644
--- a/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h
+++ b/chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h
@@ -88,10 +88,12 @@
 
   ~WebAppInstallDialogDelegate() override;
 
-  void OnAccept();
+  virtual void OnAccept();
   void OnCancel();
   void OnClose();
 
+  virtual bool OnOkButtonClicked();
+
   // This is called when the dialog has been either accepted, cancelled, closed
   // or destroyed without an user-action.
   void OnDestroyed();
@@ -112,12 +114,14 @@
   // WebAppModalDialogDelegate overrides:
   void CloseDialogAsIgnored() override;
 
+ protected:
+  std::unique_ptr<WebAppInstallInfo> install_info_;
+
  private:
   void MeasureIphOnDialogClose();
   void MeasureAcceptUserActionsForInstallDialog();
   void MeasureCancelUserActionsForInstallDialog();
 
-  std::unique_ptr<WebAppInstallInfo> install_info_;
   std::unique_ptr<webapps::MlInstallOperationTracker> install_tracker_;
   AppInstallationAcceptanceCallback callback_;
   PwaInProductHelpState iph_state_;
@@ -136,7 +140,6 @@
 
   base::WeakPtrFactory<WebAppInstallDialogDelegate> weak_ptr_factory_{this};
 };
-
 }  // namespace web_app
 
 #endif  // CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_INSTALL_DIALOG_DELEGATE_H_
diff --git a/chrome/browser/ui/views/web_apps/web_app_install_dialog_flow_view.cc b/chrome/browser/ui/views/web_apps/web_app_install_dialog_flow_view.cc
new file mode 100644
index 0000000..5992205
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/web_app_install_dialog_flow_view.cc
@@ -0,0 +1,81 @@
+// Copyright 2026 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/web_apps/web_app_install_dialog_flow_view.h"
+
+#include <memory>
+
+#include "chrome/browser/ui/views/web_apps/web_app_icon_name_and_origin_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+
+namespace web_app {
+
+// A generic view that represents the installation flow.
+WebAppInstallFlowView::WebAppInstallFlowView(const gfx::ImageSkia& icon_image,
+                                             const std::u16string& app_name,
+                                             const GURL& start_url,
+                                             bool is_maskable) {
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical));
+
+  // kInstallDialog
+  auto* install_dialog_view = AddChildView(WebAppIconNameAndOriginView::Create(
+      icon_image, app_name, start_url, is_maskable));
+  install_step_to_view_[InstallDialogStep::kInstallDialog] =
+      install_dialog_view;
+
+  // kInstallerOptions
+  auto* options =
+      AddChildView(std::make_unique<views::Label>(u"Installer Options View"));
+  options->SetVisible(false);
+  install_step_to_view_[InstallDialogStep::kInstallerOptions] = options;
+
+  // kProgress
+  auto* progress =
+      AddChildView(std::make_unique<views::Label>(u"Progress View"));
+  progress->SetVisible(false);
+  install_step_to_view_[InstallDialogStep::kProgress] = progress;
+
+  // kSuccessful launch app button
+  auto* successful =
+      AddChildView(std::make_unique<views::Label>(u"Successful View"));
+  successful->SetVisible(false);
+  install_step_to_view_[InstallDialogStep::kSuccessful] = successful;
+}
+
+WebAppInstallFlowView::~WebAppInstallFlowView() = default;
+
+// Assigns a view to the provided InstallDialogStep in the
+// WebAppInstallFlowView.
+void WebAppInstallFlowView::SetStepView(InstallDialogStep step,
+                                        std::unique_ptr<views::View> view) {
+  auto pair = install_step_to_view_.find(step);
+  bool was_visible = true;
+  if (pair != install_step_to_view_.end()) {
+    views::View* old_view = pair->second;
+    was_visible = old_view->GetVisible();
+    pair->second = nullptr;
+    RemoveChildViewT(old_view);
+  }
+  view->SetVisible(was_visible);
+  install_step_to_view_[step] = AddChildView(std::move(view));
+}
+
+// Ensures visibility is appropriately updated for each views.
+void WebAppInstallFlowView::UpdateStepVisibility(
+    InstallDialogStep current_step) {
+  for (auto const& [step, view] : install_step_to_view_) {
+    if (view) {
+      view->SetVisible(step == current_step);
+    }
+  }
+  PreferredSizeChanged();
+}
+
+base::WeakPtr<WebAppInstallFlowView> WebAppInstallFlowView::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/web_app_install_dialog_flow_view.h b/chrome/browser/ui/views/web_apps/web_app_install_dialog_flow_view.h
new file mode 100644
index 0000000..b572bb3f
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/web_app_install_dialog_flow_view.h
@@ -0,0 +1,42 @@
+// Copyright 2026 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_WEB_APPS_WEB_APP_INSTALL_DIALOG_FLOW_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_INSTALL_DIALOG_FLOW_VIEW_H_
+
+#include <map>
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h"
+#include "chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.h"
+#include "ui/views/view.h"
+
+namespace web_app {
+
+// A generic view that represents the flow of the web app installation.
+// It contains different views for each step of the installation process.
+class WebAppInstallFlowView : public views::View {
+ public:
+  WebAppInstallFlowView(const gfx::ImageSkia& icon_image,
+                        const std::u16string& app_name,
+                        const GURL& start_url,
+                        bool is_maskable);
+  ~WebAppInstallFlowView() override;
+
+  base::WeakPtr<WebAppInstallFlowView> GetWeakPtr();
+
+  void SetStepView(InstallDialogStep step, std::unique_ptr<views::View> view);
+
+  void UpdateStepVisibility(InstallDialogStep current_step);
+
+ private:
+  std::map<InstallDialogStep, raw_ptr<views::View>> install_step_to_view_;
+
+  base::WeakPtrFactory<WebAppInstallFlowView> weak_ptr_factory_{this};
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_INSTALL_DIALOG_FLOW_VIEW_H_
diff --git a/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.cc b/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.cc
new file mode 100644
index 0000000..0ca4e14d5
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.cc
@@ -0,0 +1,239 @@
+// Copyright 2026 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/web_apps/web_app_install_flow_dialog_delegate.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/functional/bind.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/feature_engagement/tracker_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/url_identity.h"
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/controls/site_icon_text_and_origin_view.h"
+#include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
+#include "chrome/browser/ui/views/web_apps/web_app_icon_name_and_origin_view.h"
+#include "chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h"
+#include "chrome/browser/ui/views/web_apps/web_app_install_dialog_flow_view.h"
+#include "chrome/browser/ui/web_applications/web_app_dialogs.h"
+#include "chrome/browser/ui/web_applications/web_app_info_image_source.h"
+#include "chrome/browser/web_applications/web_app_constants.h"
+#include "chrome/browser/web_applications/web_app_helpers.h"
+#include "chrome/browser/web_applications/web_app_install_info.h"
+#include "chrome/browser/web_applications/web_app_screenshot_fetcher.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/constrained_window/constrained_window_views.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/url_formatter/elide_url.h"
+#include "components/webapps/browser/installable/ml_install_operation_tracker.h"
+#include "components/webapps/common/constants.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/dialog_model.h"
+#include "ui/base/mojom/dialog_button.mojom.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/text_elider.h"
+#include "ui/views/bubble/bubble_dialog_model_host.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/layout/box_layout_view.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+#include "url/origin.h"
+
+namespace web_app {
+
+WebAppInstallFlowDialogDelegate::WebAppInstallFlowDialogDelegate(
+    content::WebContents* web_contents,
+    std::unique_ptr<WebAppInstallInfo> install_info,
+    std::unique_ptr<webapps::MlInstallOperationTracker> install_tracker,
+    AppInstallationAcceptanceCallback callback,
+    PwaInProductHelpState iph_state,
+    PrefService* prefs,
+    feature_engagement::Tracker* tracker,
+    InstallDialogType dialog_type)
+    : WebAppInstallDialogDelegate(web_contents,
+                                  std::move(install_info),
+                                  std::move(install_tracker),
+                                  std::move(callback),
+                                  iph_state,
+                                  prefs,
+                                  tracker,
+                                  dialog_type) {}
+
+WebAppInstallFlowDialogDelegate::~WebAppInstallFlowDialogDelegate() = default;
+
+bool WebAppInstallFlowDialogDelegate::OnOkButtonClicked() {
+  if (current_step_ == InstallDialogStep::kSuccessful) {
+    OnAccept();
+    return true;
+  }
+
+  // Update install dialog step.
+  if (current_step_ == InstallDialogStep::kInstallDialog) {
+    current_step_ = InstallDialogStep::kInstallerOptions;
+  } else if (current_step_ == InstallDialogStep::kInstallerOptions) {
+    current_step_ = InstallDialogStep::kProgress;
+  } else if (current_step_ == InstallDialogStep::kProgress) {
+    current_step_ = InstallDialogStep::kSuccessful;
+  }
+
+  if (flow_view_) {
+    flow_view_->UpdateStepVisibility(current_step_);
+  }
+
+  // Last dialog to show the install button.
+  // TODO(crbug.com/380497638): Trigger the installation earlier in the flow.
+  if (current_step_ == InstallDialogStep::kSuccessful && dialog_model()) {
+    ui::DialogModel::Button* ok_button =
+        dialog_model()->GetButtonByUniqueId(kPwaInstallDialogInstallButton);
+    if (ok_button) {
+      dialog_model()->SetButtonLabel(ok_button,
+                                     l10n_util::GetStringUTF16(IDS_DONE));
+    }
+  }
+
+  return false;
+}
+
+// Builds and shows an install dialog flow according to the install_type.
+void WebAppInstallFlowDialogDelegate::Show(
+    content::WebContents* web_contents,
+    std::unique_ptr<WebAppInstallInfo> install_info,
+    std::unique_ptr<webapps::MlInstallOperationTracker> install_tracker,
+    AppInstallationAcceptanceCallback callback,
+    PwaInProductHelpState iph_state,
+    base::WeakPtr<WebAppScreenshotFetcher> screenshot_fetcher,
+    bool show_initiating_origin,
+    InstallDialogType install_type) {
+  auto* browser_context = web_contents->GetBrowserContext();
+  Profile* profile = Profile::FromBrowserContext(browser_context);
+  PrefService* prefs = profile->GetPrefs();
+  feature_engagement::Tracker* tracker =
+      feature_engagement::TrackerFactory::GetForBrowserContext(browser_context);
+
+  DialogImageInfo dialog_image_info =
+      install_info->GetIconBitmapsForSecureSurfaces();
+  gfx::ImageSkia icon_image(
+      std::make_unique<WebAppInfoImageSource>(
+          kIconSize, std::move(dialog_image_info.bitmaps)),
+      gfx::Size(kIconSize, kIconSize));
+
+  std::u16string title = install_info->title.value();
+  GURL start_url = install_info->start_url();
+
+  auto flow_view = std::make_unique<WebAppInstallFlowView>(
+      icon_image, title, start_url, dialog_image_info.is_maskable);
+  auto flow_view_weak_ptr = flow_view->GetWeakPtr();
+
+  auto install_info_description = install_info->description.value();
+
+  auto delegate = std::make_unique<WebAppInstallFlowDialogDelegate>(
+      web_contents, std::move(install_info), std::move(install_tracker),
+      std::move(callback), std::move(iph_state), prefs, tracker, install_type);
+  auto* delegate_ptr = delegate.get();
+  auto delegate_weak_ptr = delegate_ptr->AsWeakPtr();
+
+  delegate_ptr->SetFlowView(flow_view_weak_ptr);
+
+  views::View* focusable_view = nullptr;
+  std::unique_ptr<views::View> step_view;
+
+  switch (install_type) {
+    case InstallDialogType::kDetailed: {
+      const std::u16string description = gfx::TruncateString(
+          install_info_description, webapps::kMaximumDescriptionLength,
+          gfx::CHARACTER_BREAK);
+      step_view = CreateDetailedInstallDialogView(
+          icon_image, title, start_url, dialog_image_info.is_maskable,
+          std::move(screenshot_fetcher), description);
+      break;
+    }
+    case InstallDialogType::kDiy: {
+      if (title.empty()) {
+        title = UrlIdentity::CreateFromUrl(profile, start_url,
+                                           {UrlIdentity::Type::kDefault}, {})
+                    .name;
+      }
+      step_view = CreateDiyInstallDialogView(
+          icon_image, title, start_url, web_contents,
+          base::BindRepeating(
+              &WebAppInstallDialogDelegate::OnTextFieldChangedMaybeUpdateButton,
+              delegate_weak_ptr));
+      focusable_view = static_cast<SiteIconTextAndOriginView*>(step_view.get())
+                           ->title_field();
+      break;
+    }
+    case InstallDialogType::kSimple:
+      step_view = CreateSimpleInstallDialogView(icon_image, title, start_url,
+                                                dialog_image_info.is_maskable);
+      break;
+  }
+
+  flow_view->SetStepView(InstallDialogStep::kInstallDialog,
+                         std::move(step_view));
+
+  auto dialog_model_builder = ui::DialogModel::Builder(std::move(delegate));
+  dialog_model_builder.SetInternalName("WebAppInstallFlowDialog")
+      .SetTitle(
+          l10n_util::GetStringUTF16(install_type == InstallDialogType::kDiy
+                                        ? IDS_DIY_APP_INSTALL_DIALOG_TITLE
+                                        : IDS_INSTALL_PWA_DIALOG_TITLE))
+      .AddOkButton(
+          base::BindRepeating(
+              [](base::WeakPtr<WebAppInstallFlowDialogDelegate> delegate) {
+                return delegate ? delegate->OnOkButtonClicked() : true;
+              },
+              delegate_weak_ptr),
+          ui::DialogModel::Button::Params().SetLabel(u"Next").SetId(
+              WebAppInstallDialogDelegate::kPwaInstallDialogInstallButton))
+      .AddCancelButton(base::BindOnce(&WebAppInstallDialogDelegate::OnCancel,
+                                      delegate_weak_ptr))
+      .SetCloseActionCallback(base::BindOnce(
+          &WebAppInstallDialogDelegate::OnClose, delegate_weak_ptr))
+      .SetDialogDestroyingCallback(base::BindOnce(
+          &WebAppInstallDialogDelegate::OnDestroyed, delegate_weak_ptr))
+      .OverrideDefaultButton(ui::mojom::DialogButton::kCancel)
+      .AddCustomField(
+          std::make_unique<views::BubbleDialogModelHost::CustomView>(
+              std::move(flow_view),
+              views::BubbleDialogModelHost::FieldType::kControl,
+              focusable_view),
+          WebAppInstallDialogDelegate::kDiyAppsDialogInputTextId);
+
+  if (install_type == InstallDialogType::kDiy) {
+    dialog_model_builder.SetSubtitle(
+        l10n_util::GetStringUTF16(IDS_DIY_APP_INSTALL_DIALOG_SUBTITLE));
+    dialog_model_builder.SetInitiallyFocusedField(
+        WebAppInstallDialogDelegate::kDiyAppsDialogInputTextId);
+  }
+
+  if (show_initiating_origin) {
+    url::Origin initiating_origin =
+        web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin();
+    std::u16string origin_url = url_formatter::FormatOriginForSecurityDisplay(
+        initiating_origin, url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS);
+    dialog_model_builder.SetSubtitle(l10n_util::GetStringFUTF16(
+        IDS_INSTALL_PWA_DIALOG_ORIGIN_LABEL, origin_url));
+  }
+
+  auto dialog = views::BubbleDialogModelHost::CreateModal(
+      dialog_model_builder.Build(), ui::mojom::ModalType::kChild);
+
+  views::Widget* widget = constrained_window::ShowWebModalDialogViews(
+      dialog.release(), web_contents);
+
+  if (IsWidgetCurrentSizeSmallerThanPreferredSize(widget)) {
+    delegate_weak_ptr->CloseDialogAsIgnored();
+    return;
+  }
+  delegate_weak_ptr->OnWidgetShownStartTracking(widget);
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.h b/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.h
new file mode 100644
index 0000000..4efd268
--- /dev/null
+++ b/chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.h
@@ -0,0 +1,79 @@
+// Copyright 2026 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_WEB_APPS_WEB_APP_INSTALL_FLOW_DIALOG_DELEGATE_H_
+#define CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_INSTALL_FLOW_DIALOG_DELEGATE_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h"
+#include "chrome/browser/ui/web_applications/web_app_dialogs.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace webapps {
+class MlInstallOperationTracker;
+}
+
+namespace web_app {
+
+class WebAppScreenshotFetcher;
+class WebAppInstallFlowView;
+struct WebAppInstallInfo;
+
+enum class InstallDialogStep {
+  kInstallDialog = 0,
+  kInstallerOptions = 1,
+  kProgress = 2,
+  kSuccessful = 3,
+};
+
+class WebAppInstallFlowDialogDelegate : public WebAppInstallDialogDelegate {
+ public:
+  WebAppInstallFlowDialogDelegate(
+      content::WebContents* web_contents,
+      std::unique_ptr<WebAppInstallInfo> install_info,
+      std::unique_ptr<webapps::MlInstallOperationTracker> install_tracker,
+      AppInstallationAcceptanceCallback callback,
+      PwaInProductHelpState iph_state,
+      PrefService* prefs,
+      feature_engagement::Tracker* tracker,
+      InstallDialogType dialog_type);
+
+  ~WebAppInstallFlowDialogDelegate() override;
+
+  static void Show(
+      content::WebContents* web_contents,
+      std::unique_ptr<WebAppInstallInfo> install_info,
+      std::unique_ptr<webapps::MlInstallOperationTracker> install_tracker,
+      AppInstallationAcceptanceCallback callback,
+      PwaInProductHelpState iph_state,
+      base::WeakPtr<WebAppScreenshotFetcher> screenshot_fetcher,
+      bool show_initiating_origin,
+      InstallDialogType dialog_type);
+
+  void SetFlowView(base::WeakPtr<WebAppInstallFlowView> flow_view) {
+    flow_view_ = std::move(flow_view);
+  }
+
+  bool OnOkButtonClicked() override;
+
+  base::WeakPtr<WebAppInstallFlowDialogDelegate> AsWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+ protected:
+  InstallDialogStep current_step_ = InstallDialogStep::kInstallDialog;
+  base::WeakPtr<WebAppInstallFlowView> flow_view_;
+
+ private:
+  base::WeakPtrFactory<WebAppInstallFlowDialogDelegate> weak_ptr_factory_{this};
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_INSTALL_FLOW_DIALOG_DELEGATE_H_
diff --git a/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc b/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc
index 85bb5ea..e071632 100644
--- a/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc
@@ -179,4 +179,15 @@
   return base::AutoReset<bool>(&g_dont_close_on_deactivate, true);
 }
 
+// Creates a view for the simple install dialog that contains the
+// WebAppIconNameAndOriginView
+std::unique_ptr<views::View> CreateSimpleInstallDialogView(
+    gfx::ImageSkia icon_image,
+    const std::u16string& title,
+    const GURL& start_url,
+    bool is_maskable) {
+  return WebAppIconNameAndOriginView::Create(icon_image, title, start_url,
+                                             is_maskable);
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/ui/web_applications/BUILD.gn b/chrome/browser/ui/web_applications/BUILD.gn
index 40f5d51..b7d720cd 100644
--- a/chrome/browser/ui/web_applications/BUILD.gn
+++ b/chrome/browser/ui/web_applications/BUILD.gn
@@ -199,8 +199,9 @@
         "//chrome/browser/chromeos/app_mode",
         "//chrome/browser/ui/ash/shelf",
         "//chrome/browser/ui/ash/system_web_apps",
-        "//chrome/browser/ui/webui/ash/settings/app_management",
+        "//chromeos/ash/components/browser_context_helper",
         "//chromeos/ash/components/nonclosable_app_ui",
+        "//chromeos/ash/experiences/settings_ui",
         "//chromeos/ash/experiences/system_web_apps/types",
         "//chromeos/components/kiosk",
         "//chromeos/ui/frame",
diff --git a/chrome/browser/ui/web_applications/web_app_dialog_utils.cc b/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
index 298f557..f0a5e83b 100644
--- a/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
+++ b/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/user_education/browser_user_education_interface.h"
+#include "chrome/browser/ui/views/web_apps/web_app_install_flow_dialog_delegate.h"
 #include "chrome/browser/ui/web_applications/pwa_install_page_action.h"
 #include "chrome/browser/ui/web_applications/web_app_dialogs.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
@@ -34,6 +35,7 @@
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_screenshot_fetcher.h"
 #include "chrome/browser/web_applications/web_app_utils.h"
+#include "chrome/common/chrome_features.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/webapps/browser/banners/app_banner_manager.h"
 #include "components/webapps/browser/banners/web_app_banner_data.h"
@@ -83,6 +85,21 @@
                 .SetAppId(app_id));
       }
 #endif
+      if (base::FeatureList::IsEnabled(features::kWebAppInstallDialog)) {
+        InstallDialogType install_type = kSimple;
+        if (screenshot_fetcher) {
+          install_type = kDetailed;
+        } else if (web_app_info->is_diy_app) {
+          install_type = kDiy;
+        }
+        WebAppInstallFlowDialogDelegate::Show(
+            initiator_web_contents, std::move(web_app_info),
+            std::move(install_tracker), std::move(web_app_acceptance_callback),
+            iph_state, std::move(screenshot_fetcher), show_initiating_origin,
+            install_type);
+        return;
+      }
+
       if (screenshot_fetcher) {
         ShowWebAppDetailedInstallDialog(
             initiator_web_contents, std::move(web_app_info),
diff --git a/chrome/browser/ui/web_applications/web_app_dialogs.h b/chrome/browser/ui/web_applications/web_app_dialogs.h
index 43bec84..a1a90a9 100644
--- a/chrome/browser/ui/web_applications/web_app_dialogs.h
+++ b/chrome/browser/ui/web_applications/web_app_dialogs.h
@@ -22,6 +22,7 @@
 #include "components/webapps/common/web_app_id.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/interaction/element_identifier.h"
+#include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/native_ui_types.h"
 
 static_assert(BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) ||
@@ -45,6 +46,10 @@
 enum class WebappUninstallSource;
 }  // namespace webapps
 
+namespace views {
+class View;
+}  // namespace views
+
 namespace web_app {
 
 enum class NotSupportedReason {
@@ -265,6 +270,34 @@
                                    NotSupportedReason reason,
                                    base::OnceClosure callback);
 
+// Creates a simple install dialog view that contains
+// WebAppIconNameAndOriginView.
+std::unique_ptr<views::View> CreateSimpleInstallDialogView(
+    gfx::ImageSkia icon_image,
+    const std::u16string& title,
+    const GURL& start_url,
+    bool is_maskable);
+
+// Creates a detailed install dialog view that contains
+// a carousel.
+std::unique_ptr<views::View> CreateDetailedInstallDialogView(
+    gfx::ImageSkia icon_image,
+    const std::u16string& title,
+    const GURL& start_url,
+    bool is_maskable,
+    base::WeakPtr<WebAppScreenshotFetcher> fetcher,
+    const std::u16string& description);
+
+// Creates a view for the DIY install dialog that contains the
+// input dialog.
+std::unique_ptr<views::View> CreateDiyInstallDialogView(
+    gfx::ImageSkia icon_image,
+    const std::u16string& title,
+    const GURL& start_url,
+    content::WebContents* web_contents,
+    base::RepeatingCallback<void(const std::u16string&)>
+        on_textfield_changed_callback);
+
 }  // namespace web_app
 
 #endif  // CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_DIALOGS_H_
diff --git a/chrome/browser/ui/web_applications/web_app_ui_utils.cc b/chrome/browser/ui/web_applications/web_app_ui_utils.cc
index 932034c7..61a0c75 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_utils.cc
+++ b/chrome/browser/ui/web_applications/web_app_ui_utils.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/web_applications/web_app_browser_controller.h"
-#include "chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h"
 #include "chrome/browser/web_applications/proto/web_app_install_state.pb.h"
 #include "chrome/browser/web_applications/web_app_filter.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
@@ -23,6 +22,13 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/webapps/common/web_app_id.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "base/check_deref.h"
+#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
+#include "chromeos/ash/experiences/settings_ui/settings_app_manager.h"
+#include "components/user_manager/user.h"
+#endif
+
 namespace web_app {
 
 namespace {
@@ -75,9 +81,23 @@
   }
 
 #if BUILDFLAG(IS_CHROMEOS)
-  chrome::ShowAppManagementPage(
-      Profile::FromBrowserContext(web_contents->GetBrowserContext()), *app_id,
-      ash::settings::AppManagementEntryPoint::kPageInfoView);
+  const user_manager::User* user =
+      ash::BrowserContextHelper::Get()->GetUserByBrowserContext(
+          web_contents->GetBrowserContext());
+  // TODO: Remove the if stmt, and replace it by CHECK().
+  // This method is called only by clicking the "Site setting" or "App setting"
+  // option from the Page Info bubble, which is shown from the browser's
+  // omnibox. Theoretically a shimless RMA profile may have an app. But shimless
+  // RMA screen is full-screen and has no omnibox.
+  if (!user) {
+    return false;
+  }
+  ash::SettingsAppManager::Get()->Open(
+      *user,
+      ash::SettingsAppManager::OpenParams{
+          .sub_page =
+              ash::SettingsAppManager::CreateAppManagementPagePath(*app_id),
+          .entry_point = ash::SettingsAppManager::EntryPoint::kPageInfoView});
   return true;
 #else
   chrome::ShowWebAppSettings(chrome::FindBrowserWithTab(web_contents), *app_id,
@@ -92,9 +112,24 @@
     return;
   }
 #if BUILDFLAG(IS_CHROMEOS)
-  chrome::ShowAppManagementPage(
-      profile.get(), parent_app_id,
-      ash::settings::AppManagementEntryPoint::kSubAppsInstallPrompt);
+  const user_manager::User* user =
+      ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile.get());
+  // TODO: Remove the if stmt, and replace it by CHECK().
+  // The function OpenAppSettingsForParentApp is bound as a callback to the
+  // "Manage" link in the Sub Apps Install dialog. This dialog is only triggered
+  // when a parent Web App tries to install its Sub App.
+  // The Web App can be enabled not only on user profiles but also on shimless
+  // RMA profiles, so this method may get a shimless RMA profile.
+  if (!user) {
+    return;
+  }
+  ash::SettingsAppManager::Get()->Open(
+      *user,
+      ash::SettingsAppManager::OpenParams{
+          .sub_page = ash::SettingsAppManager::CreateAppManagementPagePath(
+              parent_app_id),
+          .entry_point =
+              ash::SettingsAppManager::EntryPoint::kSubAppsInstallPrompt});
 #else
   chrome::ShowWebAppSettings(profile.get(), parent_app_id,
                              AppSettingsPageEntryPoint::kSubAppsInstallPrompt);
@@ -104,8 +139,21 @@
 void OpenAppSettingsForInstalledRelatedApp(const webapps::AppId& app_id,
                                            Profile* profile) {
 #if BUILDFLAG(IS_CHROMEOS)
-  chrome::ShowAppManagementPage(
-      profile, app_id, ash::settings::AppManagementEntryPoint::kSiteDataDialog);
+  const user_manager::User* user =
+      ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile);
+  // TODO: Remove the if stmt, and replace it by CHECK().
+  // This method is called only from PageSpecificSiteDataDialog, which is
+  // accessed by clicking the "Site Data" or "Cookies" option from the Page Info
+  // bubble, which is shown from the browser's omnibox.
+  if (!user) {
+    return;
+  }
+  ash::SettingsAppManager::Get()->Open(
+      *user,
+      ash::SettingsAppManager::OpenParams{
+          .sub_page =
+              ash::SettingsAppManager::CreateAppManagementPagePath(app_id),
+          .entry_point = ash::SettingsAppManager::EntryPoint::kSiteDataDialog});
 #else
   chrome::ShowWebAppSettings(profile, app_id,
                              AppSettingsPageEntryPoint::kSiteDataDialog);
diff --git a/chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_dialog.cc b/chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_dialog.cc
index 072cdcb..5594d8c 100644
--- a/chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_dialog.cc
+++ b/chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_dialog.cc
@@ -10,7 +10,6 @@
 #include "base/json/json_writer.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_ui.h"
-#include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "net/base/url_util.h"
 #include "ui/aura/window.h"
diff --git a/chrome/browser/ui/webui/ash/in_session_password_change/confirm_password_change_handler.cc b/chrome/browser/ui/webui/ash/in_session_password_change/confirm_password_change_handler.cc
index 0536529c..a8baa56 100644
--- a/chrome/browser/ui/webui/ash/in_session_password_change/confirm_password_change_handler.cc
+++ b/chrome/browser/ui/webui/ash/in_session_password_change/confirm_password_change_handler.cc
@@ -10,7 +10,6 @@
 #include "base/values.h"
 #include "chrome/browser/ash/login/saml/in_session_password_change_manager.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/login/auth/public/saml_password_attributes.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ui/webui/ash/in_session_password_change/urgent_password_expiry_notification_handler.cc b/chrome/browser/ui/webui/ash/in_session_password_change/urgent_password_expiry_notification_handler.cc
index 8ca580c..be941d7 100644
--- a/chrome/browser/ui/webui/ash/in_session_password_change/urgent_password_expiry_notification_handler.cc
+++ b/chrome/browser/ui/webui/ash/in_session_password_change/urgent_password_expiry_notification_handler.cc
@@ -11,7 +11,6 @@
 #include "chrome/browser/ash/login/saml/in_session_password_change_manager.h"
 #include "chrome/browser/ash/login/saml/password_expiry_notification.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/common/pref_names.h"
 #include "chromeos/ash/components/login/auth/public/saml_password_attributes.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
diff --git a/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc b/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc
index 54c3f852..c2b01f85 100644
--- a/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc
+++ b/chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.cc
@@ -16,7 +16,6 @@
 #include "chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_handler.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
 #include "chrome/browser/ui/webui/metrics_handler.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/gaia_action_buttons_resources.h"
 #include "chrome/grit/gaia_action_buttons_resources_map.h"
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 842f22a..a8cd669 100644
--- a/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/gaia_screen_handler.cc
@@ -82,7 +82,6 @@
 #include "chrome/browser/ui/webui/signin/signin_utils.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/chrome_features.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/installer/util/google_update_settings.h"
 #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
diff --git a/chrome/browser/ui/webui/ash/login/oobe_ui.cc b/chrome/browser/ui/webui/ash/login/oobe_ui.cc
index 94fc9739..0bb104e 100644
--- a/chrome/browser/ui/webui/ash/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/ash/login/oobe_ui.cc
@@ -141,7 +141,6 @@
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/chrome_unscaled_resources.h"
 #include "chrome/grit/component_extension_resources.h"
diff --git a/chrome/browser/ui/webui/ash/login/terms_of_service_screen_handler.cc b/chrome/browser/ui/webui/ash/login/terms_of_service_screen_handler.cc
index 663b633..db2d0a5 100644
--- a/chrome/browser/ui/webui/ash/login/terms_of_service_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/terms_of_service_screen_handler.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/login/localized_values_builder.h"
diff --git a/chrome/browser/ui/webui/ash/settings/app_management/BUILD.gn b/chrome/browser/ui/webui/ash/settings/app_management/BUILD.gn
deleted file mode 100644
index 21f9bfc9..0000000
--- a/chrome/browser/ui/webui/ash/settings/app_management/BUILD.gn
+++ /dev/null
@@ -1,9 +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.
-
-assert(is_chromeos)
-
-static_library("app_management") {
-  sources = [ "app_management_uma.h" ]
-}
diff --git a/chrome/browser/ui/webui/ash/settings/app_management/DEPS b/chrome/browser/ui/webui/ash/settings/app_management/DEPS
deleted file mode 100644
index c4812ae..0000000
--- a/chrome/browser/ui/webui/ash/settings/app_management/DEPS
+++ /dev/null
@@ -1,9 +0,0 @@
-include_rules = [
-  # ChromeOS should not depend on //chrome. See //docs/chromeos/code.md for
-  # details.
-  "-chrome",
-
-  # Existing dependencies within //chrome. There is an active effort to
-  # refactor //chrome/browser/ui/ash to break these dependencies; see b/332804822.
-  # Whenever possible, avoid adding new //chrome dependencies to this list.
-]
diff --git a/chrome/browser/ui/webui/ash/settings/app_management/DIR_METADATA b/chrome/browser/ui/webui/ash/settings/app_management/DIR_METADATA
deleted file mode 100644
index bda997ea..0000000
--- a/chrome/browser/ui/webui/ash/settings/app_management/DIR_METADATA
+++ /dev/null
@@ -1,5 +0,0 @@
-team_email: "lt-web-apps-team@google.com"
-os: CHROME_OS
-buganizer: {
-  component_id: 1389907
-}
diff --git a/chrome/browser/ui/webui/ash/settings/app_management/OWNERS b/chrome/browser/ui/webui/ash/settings/app_management/OWNERS
deleted file mode 100644
index 75ba0e3..0000000
--- a/chrome/browser/ui/webui/ash/settings/app_management/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://ui/webui/resources/cr_components/app_management/OWNERS
diff --git a/chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h b/chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h
deleted file mode 100644
index 1a076c0e..0000000
--- a/chrome/browser/ui/webui/ash/settings/app_management/app_management_uma.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_APP_MANAGEMENT_APP_MANAGEMENT_UMA_H_
-#define CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_APP_MANAGEMENT_APP_MANAGEMENT_UMA_H_
-
-namespace ash::settings {
-
-// These are used in histograms, do not remove/renumber entries. If you're
-// adding to this enum with the intention that it will be logged, update the
-// AppManagementEntryPoint enum listing in
-// tools/metrics/histograms/metadata/apps/enums.xml.
-enum class AppManagementEntryPoint {
-  kAppListContextMenuAppInfoArc = 0,
-  kAppListContextMenuAppInfoChromeApp = 1,
-  kAppListContextMenuAppInfoWebApp = 2,
-  kShelfContextMenuAppInfoArc = 3,
-  kShelfContextMenuAppInfoChromeApp = 4,
-  kShelfContextMenuAppInfoWebApp = 5,
-  kAppManagementMainViewArc = 6,
-  kAppManagementMainViewChromeApp = 7,
-  kAppManagementMainViewWebApp = 8,
-  kOsSettingsMainPage = 9,
-  kAppManagementMainViewPluginVm = 10,
-  kDBusServicePluginVm = 11,
-  kNotificationPluginVm = 12,
-  kAppManagementMainViewBorealis = 13,
-  kPageInfoView = 14,
-  kPrivacyIndicatorsNotificationSettings = 15,
-  kSubAppsInstallPrompt = 16,
-  kSiteDataDialog = 17,
-  kMaxValue = kSiteDataDialog,
-};
-
-}  // namespace ash::settings
-
-#endif  // CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_APP_MANAGEMENT_APP_MANAGEMENT_UMA_H_
diff --git a/chrome/browser/ui/webui/ash/settings/pages/search/search_section.cc b/chrome/browser/ui/webui/ash/settings/pages/search/search_section.cc
index 79677fc2..639646a 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/search/search_section.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/search/search_section.cc
@@ -233,9 +233,6 @@
   html_source->AddLocalizedStrings(kLocalizedStrings);
 
   html_source->AddBoolean(
-      "quickAnswersTranslationDisabled",
-      chromeos::features::IsQuickAnswersV2TranslationDisabled());
-  html_source->AddBoolean(
       "quickAnswersSubToggleEnabled",
       chromeos::features::IsQuickAnswersV2SettingsSubToggleEnabled());
 }
diff --git a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc
index ae7085d8..1ee9263 100644
--- a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc
+++ b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc
@@ -117,7 +117,8 @@
         pending_searchbox_handler,
     Profile* profile,
     content::WebContents* web_contents,
-    GetSessionHandleCallback get_session_callback)
+    GetSessionHandleCallback get_session_callback,
+    ClearSessionHandleCallback clear_session_callback)
     : ComposeboxHandler(
           std::move(pending_handler),
           std::move(pending_page),
@@ -128,7 +129,8 @@
               std::make_unique<ComposeboxOmniboxClient>(profile,
                                                         web_contents,
                                                         this)),
-          std::move(get_session_callback)) {}
+          std::move(get_session_callback),
+          std::move(clear_session_callback)) {}
 
 ComposeboxHandler::ComposeboxHandler(
     mojo::PendingReceiver<composebox::mojom::PageHandler> pending_handler,
@@ -138,13 +140,15 @@
     Profile* profile,
     content::WebContents* web_contents,
     std::unique_ptr<OmniboxController> controller,
-    GetSessionHandleCallback get_session_callback)
+    GetSessionHandleCallback get_session_callback,
+    ClearSessionHandleCallback clear_session_callback)
     : ContextualSearchboxHandler(std::move(pending_searchbox_handler),
                                  profile,
                                  web_contents,
                                  std::move(controller),
                                  std::move(get_session_callback)),
       web_contents_(web_contents),
+      clear_session_callback_(std::move(clear_session_callback)),
       page_{std::move(pending_page)},
       handler_(this, std::move(pending_handler)) {
   // Set the callback for getting suggest inputs from the session.
@@ -163,6 +167,12 @@
   // connected/disconnected from the DOM, so this is not needed.
 }
 
+void ComposeboxHandler::ClearSessionHandle() {
+  if (clear_session_callback_) {
+    clear_session_callback_.Run();
+  }
+}
+
 void ComposeboxHandler::HandleLensButtonClick() {
   // Ignore, intentionally unimplemented for NTP.
 }
@@ -265,3 +275,25 @@
 
   return SearchboxHandler::AutocompleteIconToResourceName(icon);
 }
+
+void ComposeboxHandler::OpenUrl(GURL url,
+                                const WindowOpenDisposition disposition) {
+  ContextualSearchboxHandler::OpenUrl(url, disposition);
+  // To keep the current composebox in a valid state after passing along its
+  // session handle and input state model, clear both of these values. This
+  // way the state will reset on the next use of the composebox. Clear the
+  // session handle before initializing the input state model, so the model
+  // gets a fresh handle.
+  ResetInputStateModel();
+  ClearSessionHandle();
+  InitializeInputStateModel();
+  // This is technically wrong, it'll start a new session for the composebox
+  // even before its been opened. This is needed to re-request the cluster info
+  // for the composebox.
+  // TODO(crbug.com/491871526): Re-request cluster info when needed, not on
+  // navigation.
+  auto* contextual_session_handle = GetContextualSessionHandle();
+  if (contextual_session_handle) {
+    contextual_session_handle->NotifySessionStarted();
+  }
+}
diff --git a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h
index 9aeb692..ab0bbaaf 100644
--- a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h
+++ b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h
@@ -27,6 +27,8 @@
 class ComposeboxHandler : public composebox::mojom::PageHandler,
                           public ContextualSearchboxHandler {
  public:
+  using ClearSessionHandleCallback = base::RepeatingClosure;
+
   explicit ComposeboxHandler(
       mojo::PendingReceiver<composebox::mojom::PageHandler> pending_handler,
       mojo::PendingRemote<composebox::mojom::Page> pending_page,
@@ -34,7 +36,8 @@
           pending_searchbox_handler,
       Profile* profile,
       content::WebContents* web_contents,
-      GetSessionHandleCallback get_session_callback);
+      GetSessionHandleCallback get_session_callback,
+      ClearSessionHandleCallback clear_session_callback);
   ~ComposeboxHandler() override;
 
   // composebox::mojom::PageHandler:
@@ -73,11 +76,18 @@
                    WindowOpenDisposition disposition,
                    omnibox::ChromeAimEntryPoint aim_entrypoint,
                    std::map<std::string, std::string> additional_params);
-
   // SearchboxHandler:
   std::string AutocompleteIconToResourceName(
       const gfx::VectorIcon& icon) const override;
 
+  virtual void ClearSessionHandle();
+
+ protected:
+  void OpenUrl(GURL url, const WindowOpenDisposition disposition) override;
+
+  FRIEND_TEST_ALL_PREFIXES(ComposeboxHandlerTest,
+                           OpenUrl_ResetsContextControllerObserver);
+
  protected:
   ComposeboxHandler(
       mojo::PendingReceiver<composebox::mojom::PageHandler> pending_handler,
@@ -87,11 +97,14 @@
       Profile* profile,
       content::WebContents* web_contents,
       std::unique_ptr<OmniboxController> omnibox_controller,
-      GetSessionHandleCallback get_session_callback);
+      GetSessionHandleCallback get_session_callback,
+      ClearSessionHandleCallback clear_session_callback);
 
  private:
   raw_ptr<content::WebContents> web_contents_;
 
+  ClearSessionHandleCallback clear_session_callback_;
+
   // These are located at the end of the list of member variables to ensure the
   // WebUI page is disconnected before other members are destroyed.
   mojo::Remote<composebox::mojom::Page> page_;
diff --git a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler_unittest.cc b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler_unittest.cc
index 7a8b978b..a2fd0e9 100644
--- a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler_unittest.cc
+++ b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler_unittest.cc
@@ -14,13 +14,16 @@
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/gmock_move_support.h"
+#include "base/test/test_future.h"
 #include "base/time/time.h"
 #include "base/unguessable_token.h"
 #include "base/version_info/channel.h"
 #include "chrome/browser/contextual_search/contextual_search_service_factory.h"
+#include "chrome/browser/contextual_search/contextual_search_web_contents_helper.h"
 #include "chrome/browser/ui/webui/searchbox/contextual_searchbox_test_utils.h"
 #include "chrome/browser/ui/webui/searchbox/searchbox_test_utils.h"
 #include "components/contextual_search/contextual_search_service.h"
+#include "components/contextual_search/mock_contextual_search_context_controller.h"
 #include "components/omnibox/browser/searchbox.mojom.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
@@ -98,7 +101,8 @@
         mojo::PendingReceiver<searchbox::mojom::PageHandler>(), profile(),
         web_contents(), base::BindLambdaForTesting([&]() {
           return contextual_session_handle_.get();
-        }));
+        }),
+        base::DoNothing());
 
     handler_->SetPage(mock_searchbox_page_.BindAndGetRemote());
   }
@@ -191,6 +195,30 @@
       kComposeboxFileDeleted + file_type + file_status + ".NewTabPage", 1);
 }
 
+// Verifies that TakeSessionHandle transfers ownership out of the helper.
+TEST_F(ComposeboxHandlerTest, TakeSessionHandle_TransfersOwnership) {
+  auto mock_controller = std::make_unique<testing::NiceMock<
+      contextual_search::MockContextualSearchContextController>>();
+  ON_CALL(*mock_controller, AsWeakPtr())
+      .WillByDefault(testing::Return(
+          base::WeakPtr<
+              contextual_search::ContextualSearchContextController>()));
+
+  auto* service = ContextualSearchServiceFactory::GetForProfile(profile());
+  auto handle = service->CreateSessionForTesting(std::move(mock_controller),
+                                                 /*metrics_recorder=*/nullptr);
+
+  auto* helper = ContextualSearchWebContentsHelper::GetOrCreateForWebContents(
+      web_contents());
+  helper->SetTaskSession(/*task_id=*/std::nullopt, std::move(handle),
+                         /*input_state_model=*/nullptr);
+  EXPECT_NE(helper->session_handle(), nullptr);
+
+  auto taken_handle = helper->TakeSessionHandle();
+  EXPECT_NE(taken_handle, nullptr);
+  EXPECT_EQ(helper->session_handle(), nullptr);
+}
+
 TEST_F(ComposeboxHandlerTest, SubmitQueryWithToolMetric) {
   // Submit with no tools enabled.
   SubmitQueryAndWaitForNavigation();
diff --git a/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.cc b/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.cc
index 0ecac0e..8ef2b8f 100644
--- a/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.cc
+++ b/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.cc
@@ -843,6 +843,9 @@
     search_url_request_info->additional_params = additional_params;
     search_url_request_info->aim_entry_point = aim_entry_point;
 
+    file_info_list =
+        contextual_session_handle->GetController()->GetFileInfoList();
+
     contextual_session_handle->CreateSearchUrl(
         std::move(search_url_request_info),
         base::BindOnce(
@@ -853,9 +856,6 @@
               }
             },
             weak_ptr_factory_.GetWeakPtr(), disposition));
-
-    file_info_list =
-        contextual_session_handle->GetController()->GetFileInfoList();
   }
 
 #if !BUILDFLAG(IS_ANDROID)
@@ -1027,7 +1027,10 @@
       target_web_contents->Focus();
     }
   } else {
-    content::OpenURLParams params(url, content::Referrer(), disposition,
+    // TODO(crbug.com/473009258): Override the window disposition for the
+    // ntp composebox until we correctly clear composebox input.
+    content::OpenURLParams params(url, content::Referrer(),
+                                  WindowOpenDisposition::CURRENT_TAB,
                                   ui::PAGE_TRANSITION_LINK, false);
     web_contents_->OpenURL(params, std::move(navigation_handle_callback));
   }
diff --git a/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.h b/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.h
index 78b5a315..3ae41a1f 100644
--- a/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.h
+++ b/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.h
@@ -196,6 +196,8 @@
   // SearchboxHandler:
   omnibox::InputState GetInputState() const override;
 
+  virtual void OpenUrl(GURL url, const WindowOpenDisposition disposition);
+
   void ComputeAndOpenQueryUrl(
       const std::string& query_text,
       WindowOpenDisposition disposition,
@@ -267,8 +269,6 @@
       const base::UnguessableToken& context_token,
       std::unique_ptr<lens::ContextualInputData> page_content_data);
 
-  void OpenUrl(GURL url, const WindowOpenDisposition disposition);
-
   void OnPreviewReceived(GetTabPreviewCallback callback,
                          const SkBitmap& preview_bitmap);
 
diff --git a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips.mojom b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips.mojom
index e71f78bd..3c5e9ec 100644
--- a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips.mojom
+++ b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips.mojom
@@ -66,6 +66,9 @@
 
   // Activates a metrics funnel for the session.
   ActivateMetricsFunnel(string funnel_name);
+
+  // Changes the visibility of the action chips feature.
+  SetActionChipsVisibility(bool is_visible);
 };
 
 // Interface for the page-side handler receiving information from the browser.
diff --git a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.cc b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.cc
index bd8cba4..bcb5daa5 100644
--- a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.cc
+++ b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.cc
@@ -175,6 +175,10 @@
   }
 }
 
+void ActionChipsHandler::SetActionChipsVisibility(bool is_visible) {
+  profile_->GetPrefs()->SetBoolean(prefs::kNtpToolChipsVisible, is_visible);
+}
+
 void ActionChipsHandler::SendActionChipsToUi(base::TimeTicks start_time,
                                              std::vector<ActionChipPtr> chips) {
   if (!page_.is_bound()) {
diff --git a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.h b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.h
index 9109f90..f220d80 100644
--- a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.h
+++ b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler.h
@@ -46,6 +46,7 @@
 
   void StartActionChipsRetrieval() override;
   void ActivateMetricsFunnel(const std::string& funnel_name) override;
+  void SetActionChipsVisibility(bool is_visible) override;
 
   void OnTabStripModelChanged(
       TabStripModel* tab_strip_model,
diff --git a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler_unittest.cc b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler_unittest.cc
index 93a07f0..0175a25e 100644
--- a/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler_unittest.cc
+++ b/chrome/browser/ui/webui/new_tab_page/action_chips/action_chips_handler_unittest.cc
@@ -82,6 +82,8 @@
     return receiver_.BindNewPipeAndPassRemote();
   }
 
+  void FlushForTesting() { receiver_.FlushForTesting(); }
+
   MOCK_METHOD(void,
               OnActionChipsChanged,
               (std::vector<ActionChipPtr> action_chips),
@@ -660,12 +662,24 @@
 }
 
 TEST_F(ActionChipsHandlerTest, ActionChipVisbilityChanged) {
-  // Set visibility to false.
-  profile_->GetPrefs()->SetBoolean(prefs::kNtpToolChipsVisible, false);
+  // Set visibility to false, and this causes no call to OnActionChipsChanged.
   EXPECT_CALL(page_, OnActionChipsChanged(_)).Times(0);
+  profile_->GetPrefs()->SetBoolean(prefs::kNtpToolChipsVisible, false);
+  page_.FlushForTesting();
+  testing::Mock::VerifyAndClearExpectations(&page_);
 
   // Ensure `OnActionChipsChanged` is called when visibility changes to true.
-  profile_->GetPrefs()->SetBoolean(prefs::kNtpToolChipsVisible, true);
   EXPECT_CALL(page_, OnActionChipsChanged(_)).Times(1);
+  profile_->GetPrefs()->SetBoolean(prefs::kNtpToolChipsVisible, true);
+  page_.FlushForTesting();
+  testing::Mock::VerifyAndClearExpectations(&page_);
+}
+
+TEST_F(ActionChipsHandlerTest, SetActionChipsVisibility) {
+  EXPECT_TRUE(profile_->GetPrefs()->GetBoolean(prefs::kNtpToolChipsVisible));
+  handler().SetActionChipsVisibility(false);
+  EXPECT_FALSE(profile_->GetPrefs()->GetBoolean(prefs::kNtpToolChipsVisible));
+  handler().SetActionChipsVisibility(true);
+  EXPECT_TRUE(profile_->GetPrefs()->GetBoolean(prefs::kNtpToolChipsVisible));
 }
 }  // namespace
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index 0274d10a..8a5e6eec 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -303,6 +303,7 @@
       {"title", IDS_NEW_TAB_TITLE},
       {"undo", IDS_NEW_TAB_UNDO_THUMBNAIL_REMOVE},
       {"controlledSettingPolicy", IDS_CONTROLLED_SETTING_POLICY},
+      {"disableSuggestion", IDS_NTP_ACTION_CHIP_DISABLE_TEXT},
 
       // Custom Links.
       {"addLinkTitle", IDS_NTP_CUSTOM_LINKS_ADD_SHORTCUT_TITLE},
@@ -1265,6 +1266,8 @@
       std::move(pending_page_handler), std::move(pending_page),
       std::move(pending_searchbox_handler), profile_, web_contents(),
       base::BindRepeating(&NewTabPageUI::GetOrCreateContextualSessionHandle,
+                          base::Unretained(this)),
+      base::BindRepeating(&NewTabPageUI::ClearContextualSessionHandle,
                           base::Unretained(this)));
 
   // TODO(crbug.com/435288212): Move searchbox mojom to use factory pattern.
@@ -1350,6 +1353,10 @@
   return shared_session_handle_.get();
 }
 
+void NewTabPageUI::ClearContextualSessionHandle() {
+  shared_session_handle_.reset();
+}
+
 void NewTabPageUI::DidStartNavigation(
     content::NavigationHandle* navigation_handle) {
   if (navigation_handle->IsInPrimaryMainFrame() &&
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
index 50f6dbf..e58e658 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
@@ -252,6 +252,8 @@
   contextual_search::ContextualSearchSessionHandle*
   GetOrCreateContextualSessionHandle();
 
+  void ClearContextualSessionHandle();
+
  private:
   // new_tab_page::mojom::PageHandlerFactory:
   void CreatePageHandler(
diff --git a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc
index 1dffe7b..e596e62d 100644
--- a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc
+++ b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc
@@ -282,6 +282,8 @@
       std::move(pending_searchbox_handler), profile_,
       web_ui()->GetWebContents(),
       base::BindRepeating(&OmniboxPopupUI::GetOrCreateContextualSessionHandle,
+                          base::Unretained(this)),
+      base::BindRepeating(&OmniboxPopupUI::ClearContextualSessionHandle,
                           base::Unretained(this)));
 
   // TODO(crbug.com/435288212): Move searchbox mojom to use factory pattern.
@@ -298,7 +300,13 @@
           omnibox::CreateQueryControllerConfigParams(),
           contextual_search::ContextualSearchSource::kOmnibox,
           lens::LensOverlayInvocationSource::kOmniboxContextualQuery);
+      shared_session_handle_->CheckSearchContentSharingSettings(
+          Profile::FromWebUI(web_ui())->GetPrefs());
     }
   }
   return shared_session_handle_.get();
 }
+
+void OmniboxPopupUI::ClearContextualSessionHandle() {
+  shared_session_handle_.reset();
+}
diff --git a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h
index 1ab9395..c88ac15 100644
--- a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h
+++ b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h
@@ -105,6 +105,8 @@
   contextual_search::ContextualSearchSessionHandle*
   GetOrCreateContextualSessionHandle();
 
+  void ClearContextualSessionHandle();
+
  private:
   raw_ptr<Profile> profile_;
 
diff --git a/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.cc b/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.cc
index dc0db88..4a86b45 100644
--- a/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.cc
+++ b/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.cc
@@ -380,7 +380,7 @@
         model_broker_state.usage_tracker()
             .WasOnDeviceEligibleFeatureRecentlyUsed(feature);
     feature_adaptation_info->version =
-        model_broker_state.service_controller()
+        model_broker_state.base_model_controller()
             .GetFeatureMetadata(feature)
             .transform(
                 &optimization_guide::OnDeviceModelAdaptationMetadata::version)
@@ -391,7 +391,7 @@
 
   if (debug_state.state_) {
     auto performance_hint =
-        model_broker_state.service_controller().GetPerformanceHint();
+        model_broker_state.base_model_controller().GetPerformanceHint();
     base::ThreadPool::PostTaskAndReplyWithResult(
         FROM_HERE, {base::MayBlock()},
         base::BindOnce(&GetBaseModelInfo, *debug_state.state_,
diff --git a/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.cc b/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.cc
index e804c532..26b2876 100644
--- a/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.cc
+++ b/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.cc
@@ -83,7 +83,8 @@
         pending_searchbox_handler,
     Profile* profile,
     content::WebContents* web_contents,
-    GetSessionHandleCallback get_session_callback)
+    GetSessionHandleCallback get_session_callback,
+    ClearSessionHandleCallback clear_session_callback)
     : ComposeboxHandler(
           std::move(pending_handler),
           std::move(pending_page),
@@ -94,7 +95,8 @@
               std::make_unique<OmniboxPopupComposeboxClient>(profile,
                                                              web_contents,
                                                              this)),
-          std::move(get_session_callback)) {
+          std::move(get_session_callback),
+          std::move(clear_session_callback)) {
   auto* aim_eligibility_service =
       AimEligibilityServiceFactory::GetForProfile(profile);
   if (aim_eligibility_service) {
diff --git a/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.h b/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.h
index 9ed0a37..57a1327 100644
--- a/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.h
+++ b/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.h
@@ -24,7 +24,8 @@
           pending_searchbox_handler,
       Profile* profile,
       content::WebContents* web_contents,
-      GetSessionHandleCallback get_session_callback);
+      GetSessionHandleCallback get_session_callback,
+      ClearSessionHandleCallback clear_session_callback);
 
   ~OmniboxComposeboxHandler() override;
 
diff --git a/chrome/browser/ui/webui/searchbox/webui_omnibox_interactive_uitest.cc b/chrome/browser/ui/webui/searchbox/webui_omnibox_interactive_uitest.cc
index 912eca2e..e6df60bd 100644
--- a/chrome/browser/ui/webui/searchbox/webui_omnibox_interactive_uitest.cc
+++ b/chrome/browser/ui/webui/searchbox/webui_omnibox_interactive_uitest.cc
@@ -320,7 +320,8 @@
   OmniboxAimWebUiInteractiveTest() {
     feature_list_.InitWithFeaturesAndParameters(
         GetEnabledFeatures(/*enable_aim_popup=*/true),
-        {omnibox::kAimServerEligibilityEnabled});
+        {omnibox::kAimServerEligibilityEnabled,
+         omnibox::kAimFuseboxEligibilityCheckEnabled});
   }
 
   std::unique_ptr<content::ScopedAccessibilityMode> scoped_accessibility_mode_;
@@ -416,7 +417,8 @@
     feature_list_.InitWithFeaturesAndParameters(
         GetEnabledFeatures(/*enable_aim_popup=*/true,
                            /*auto_submit_voice=*/false),
-        {omnibox::kAimServerEligibilityEnabled});
+        {omnibox::kAimServerEligibilityEnabled,
+         omnibox::kAimFuseboxEligibilityCheckEnabled});
   }
 
  private:
@@ -488,7 +490,8 @@
     feature_list_.InitWithFeaturesAndParameters(
         GetEnabledFeatures(/*enable_aim_popup=*/true,
                            /*auto_submit_voice=*/GetParam().auto_submit_voice),
-        {omnibox::kAimServerEligibilityEnabled});
+        {omnibox::kAimServerEligibilityEnabled,
+         omnibox::kAimFuseboxEligibilityCheckEnabled});
   }
 
  private:
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 787f8222..0af39bb 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1773259189-78ef215e9b54dc470ad476466ceae3bdabc70dbd-2eb1f882764dba3081c5d2a17446c3ded6dfb084.profdata
+chrome-mac-arm-main-1773273244-9b0106b221d2d53ce72b4c7a8ae54018f5266587-1dd3629908fff5c8506f0928ccc540eb6237b9c8.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index c58539d..6103a3b 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1773251990-42c5e1537efae1128a28f5ed086151420c63c198-5c1fe9b9d7403e32315a21e5f946ac3452b4506d.profdata
+chrome-win32-main-1773262601-892339f4a5a45b58626cea48b18afcb1dac3f25c-4ca3578a044af4063ef242decf4c9e9d07bfd2a3.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 8d176666..0399e76 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1773251990-bc327efd82c920287c6d17dfd23b719e10a149ce-5c1fe9b9d7403e32315a21e5f946ac3452b4506d.profdata
+chrome-win64-main-1773262601-d35286c8e32846462156f3a266595984d63675f3-4ca3578a044af4063ef242decf4c9e9d07bfd2a3.profdata
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 9bb8cc1..2560524 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -940,16 +940,6 @@
 // system volume, and higher than 1.0 is louder.
 inline constexpr char kTextToSpeechVolume[] = "settings.tts.speech_volume";
 
-// A dictionary containing the latest Time Limits override authorized by parent
-// access code.
-inline constexpr char kTimeLimitLocalOverride[] = "screen_time.local_override";
-
-// A dictionary preference holding the usage time limit definitions for a user.
-inline constexpr char kUsageTimeLimit[] = "screen_time.limit";
-
-// Last state of the screen time limit.
-inline constexpr char kScreenTimeLastState[] = "screen_time.last_state";
-
 // Boolean pref indicating whether the message displayed on the login screen for
 // the managed guest session should be the full warning or not.
 // True means the full warning should be displayed.
@@ -982,9 +972,6 @@
 inline constexpr char kNetBiosShareDiscoveryEnabled[] =
     "network_file_shares.netbios_discovery.enabled";
 
-// Amount of screen time that a child user has used in the current day.
-inline constexpr char kChildScreenTimeMilliseconds[] = "child_screen_time";
-
 // Last time the kChildScreenTimeMilliseconds was saved.
 inline constexpr char kLastChildScreenTimeSaved[] =
     "last_child_screen_time_saved";
@@ -1008,52 +995,6 @@
 inline constexpr char kNTLMShareAuthenticationEnabled[] =
     "network_file_shares.ntlm_share_authentication.enabled";
 
-// Dictionary pref containing configuration used to verify Parent Access Code.
-// Controlled by ParentAccessCodeConfig policy.
-inline constexpr char kParentAccessCodeConfig[] =
-    "child_user.parent_access_code.config";
-
-// List pref containing app activity and state for each application.
-inline constexpr char kPerAppTimeLimitsAppActivities[] =
-    "child_user.per_app_time_limits.app_activities";
-
-// Int64 to specify the last timestamp the AppActivityRegistry was reset.
-inline constexpr char kPerAppTimeLimitsLastResetTime[] =
-    "child_user.per_app_time_limits.last_reset_time";
-
-// Int64 to specify the last timestamp the app activity has been successfully
-// reported.
-inline constexpr char kPerAppTimeLimitsLastSuccessfulReportTime[] =
-    "child_user.per_app_time_limits.last_successful_report_time";
-
-// Int64 to specify the latest AppLimit update timestamp from.
-inline constexpr char kPerAppTimeLimitsLatestLimitUpdateTime[] =
-    "child_user.per_app_time_limits.latest_limit_update_time";
-
-// Dictionary pref containing the per-app time limits configuration for
-// child user. Controlled by PerAppTimeLimits policy.
-inline constexpr char kPerAppTimeLimitsPolicy[] =
-    "child_user.per_app_time_limits.policy";
-
-// Dictionary pref containing the allowed urls, schemes and applications
-// that would not be blocked by per app time limits.
-inline constexpr char kPerAppTimeLimitsAllowlistPolicy[] =
-    "child_user.per_app_time_limits.allowlist";
-
-// Integer pref to record the day id (number of days since origin of time) when
-// family user metrics were last recorded.
-inline constexpr char kFamilyUserMetricsDayId[] = "family_user.metrics.day_id";
-
-// TimeDelta pref to record the accumulated user session duration for family
-// user metrics.
-inline constexpr char kFamilyUserMetricsSessionEngagementDuration[] =
-    "family_user.metrics.session_engagement_duration";
-
-// TimeDelta pref to record the accumulated Chrome browser app usage for family
-// user metrics.
-inline constexpr char kFamilyUserMetricsChromeBrowserEngagementDuration[] =
-    "family_user.metrics.chrome_browser_engagement_duration";
-
 // List of preconfigured network file shares.
 inline constexpr char kNetworkFileSharesPreconfiguredShares[] =
     "network_file_shares.preconfigured_shares";
@@ -2964,11 +2905,6 @@
 inline constexpr char kRemoveUsersRemoteCommand[] =
     "remove_users_remote_command";
 
-// Dictionary pref containing the configuration used to verify Parent Access
-// Code. The data is sent through the ParentAccessCodeConfig policy, which is
-// set for child users only, and kept on the known user storage.
-inline constexpr char kKnownUserParentAccessCodeConfig[] =
-    "child_user.parent_access_code.config";
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 // String which specifies where to store the disk cache.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index dbbc58d..edceb57 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2583,6 +2583,7 @@
       "//chrome/browser/content_settings:content_settings_util",
       "//chrome/browser/custom_handlers",
       "//chrome/browser/data_saver",
+      "//chrome/browser/data_sharing:browser_tests",
       "//chrome/browser/desktop_to_mobile_promos:utils",
       "//chrome/browser/device_api:browser_tests",
       "//chrome/browser/devtools",
@@ -2621,6 +2622,7 @@
       "//chrome/browser/image_fetcher",
       "//chrome/browser/importer",
       "//chrome/browser/importer:browser_tests",
+      "//chrome/browser/indigo/onboarding:browser_tests",
       "//chrome/browser/language_detection:browser_tests",
       "//chrome/browser/launch_time_navigation_signal:browser_tests",
       "//chrome/browser/lifetime:termination_notification",
@@ -3501,8 +3503,6 @@
       "../browser/custom_handlers/protocol_handler_registry_browsertest.cc",
       "../browser/data_saver/data_saver_browsertest.cc",
       "../browser/data_saver/data_saver_webapis_browsertest.cc",
-      "../browser/data_sharing/desktop/data_sharing_sdk_delegate_desktop_browsertest.cc",
-      "../browser/data_sharing/desktop/data_sharing_service_browsertest.cc",
       "../browser/devtools/device/adb/adb_client_socket_browsertest.cc",
       "../browser/devtools/device/devtools_android_bridge_browsertest.cc",
       "../browser/devtools/device/port_forwarding_browsertest.cc",
@@ -3929,7 +3929,6 @@
       "../browser/ui/views/tabs/tab/alert_indicator_button_browsertest.cc",
       "../browser/ui/views/tabs/tab_hover_card_controller_browsertest.cc",
       "../browser/ui/views/tabs/tab_search_button_browsertest.cc",
-      "../browser/ui/views/tabs/tab_search_container_browsertest.cc",
       "../browser/ui/views/tabs/tab_strip_action_container_browsertest.cc",
       "../browser/ui/views/tabs/tab_strip_browsertest.cc",
       "../browser/ui/views/user_education/browser_help_bubble_browsertest.cc",
@@ -6618,9 +6617,6 @@
     "../browser/component_updater/privacy_sandbox_attestations_component_installer_unittest.cc",
     "../browser/component_updater/subresource_filter_component_installer_unittest.cc",
     "../browser/custom_handlers/chrome_protocol_handler_registry_unittest.cc",
-    "../browser/data_sharing/data_sharing_navigation_throttle_unittest.cc",
-    "../browser/data_sharing/data_sharing_service_factory_unittest.cc",
-    "../browser/data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory_unittest.cc",
     "../browser/download/chrome_download_manager_delegate_unittest.cc",
     "../browser/download/deferred_client_wrapper_unittest.cc",
     "../browser/download/download_core_service_impl_unittest.cc",
@@ -6903,7 +6899,6 @@
       "../browser/devtools/device/usb/android_rsa_unittest.cc",
       "../browser/devtools/devtools_availability_checker_unittest.cc",
       "../browser/metrics/power/battery_discharge_reporter_unittest.cc",
-      "../browser/page_info/merchant_trust_service_delegate_unittest.cc",
       "../browser/page_load_metrics/observers/non_tab_webui_page_load_metrics_observer_unittest.cc",
       "../browser/performance_manager/metrics/metrics_provider_desktop_unittest.cc",
       "../browser/performance_manager/policies/transient_keep_alive_policy_unittest.cc",
@@ -7109,6 +7104,7 @@
     "//chrome/browser/custom_handlers",
     "//chrome/browser/data_saver",
     "//chrome/browser/data_sharing",
+    "//chrome/browser/data_sharing:unit_tests",
     "//chrome/browser/devtools",
     "//chrome/browser/diagnostics:unit_tests",
     "//chrome/browser/digital_credentials:unit_tests",
@@ -8284,7 +8280,6 @@
       "../browser/component_updater/soda_component_installer_unittest.cc",
       "../browser/component_updater/soda_language_pack_component_installer_unittest.cc",
       "../browser/component_updater/zxcvbn_data_component_installer_unittest.cc",
-      "../browser/data_sharing/desktop/data_sharing_conversion_utils_unittest.cc",
       "../browser/devtools/device/android_device_manager_unittest.cc",
       "../browser/devtools/protocol/cast_handler_unittest.cc",
       "../browser/devtools/serialize_host_descriptions_unittest.cc",
@@ -8567,6 +8562,7 @@
       "//chrome/browser/indigo:unit_tests",
       "//chrome/browser/lookalikes:unit_tests",
       "//chrome/browser/media:access_handler",
+      "//chrome/browser/page_info:unit_tests",
       "//chrome/browser/password_manager/password_change",
       "//chrome/browser/password_manager/password_change:unit_tests",
       "//chrome/browser/predictors:unit_tests",
@@ -8661,6 +8657,7 @@
       "//chrome/utility:unit_tests",
       "//components/autofill/core/browser",
       "//components/contextual_search:public",
+      "//components/contextual_search:test_support",
       "//components/contextual_search/internal:test_support",
       "//components/data_sharing:test_support",
       "//components/ntp_tiles:pref_names",
@@ -10829,7 +10826,6 @@
       "../browser/ui/views/tabs/tab_container_unittest.cc",
       "../browser/ui/views/tabs/tab_group_views_unittest.cc",
       "../browser/ui/views/tabs/tab_hover_card_bubble_view_unittest.cc",
-      "../browser/ui/views/tabs/tab_search_container_unittest.cc",
       "../browser/ui/views/tabs/tab_strip_control_button_unittest.cc",
       "../browser/ui/views/tabs/tab_strip_layout_unittest.cc",
       "../browser/ui/views/tabs/tab_strip_nudge_button_unittest.cc",
@@ -11651,6 +11647,7 @@
       "//chrome/browser/content_settings:content_settings_factory",
       "//chrome/browser/contextual_cueing",
       "//chrome/browser/contextual_tasks:interactive_ui_tests",
+      "//chrome/browser/data_sharing",
       "//chrome/browser/desktop_to_mobile_promos:utils",
       "//chrome/browser/devtools",
       "//chrome/browser/devtools:test_support",
@@ -11866,7 +11863,6 @@
     deps += [
       "//chrome/browser:flags",
       "//chrome/browser/custom_handlers",
-      "//chrome/browser/data_sharing",
       "//chrome/browser/history",
       "//chrome/browser/privacy_sandbox:test_support",
       "//chrome/browser/ui/location_bar",
diff --git a/chrome/test/data/extensions/api_test/extension_module/cognito_file/manifest.json b/chrome/test/data/extensions/api_test/extension_module/cognito_file/manifest.json
index 176335f..7167205b 100644
--- a/chrome/test/data/extensions/api_test/extension_module/cognito_file/manifest.json
+++ b/chrome/test/data/extensions/api_test/extension_module/cognito_file/manifest.json
@@ -1,10 +1,9 @@
 {
   "name": "chrome.extension",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "description": "end-to-end browser test for some chrome.extension API: cognito mode.",
   "background": {
-    "scripts": ["test.js"],
-    "persistent": true
+    "service_worker": "test.js"
   }
 }
diff --git a/chrome/test/data/extensions/api_test/extension_module/cognito_nofile/manifest.json b/chrome/test/data/extensions/api_test/extension_module/cognito_nofile/manifest.json
index 176335f..7167205b 100644
--- a/chrome/test/data/extensions/api_test/extension_module/cognito_nofile/manifest.json
+++ b/chrome/test/data/extensions/api_test/extension_module/cognito_nofile/manifest.json
@@ -1,10 +1,9 @@
 {
   "name": "chrome.extension",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "description": "end-to-end browser test for some chrome.extension API: cognito mode.",
   "background": {
-    "scripts": ["test.js"],
-    "persistent": true
+    "service_worker": "test.js"
   }
 }
diff --git a/chrome/test/data/extensions/api_test/extension_module/incognito_file/manifest.json b/chrome/test/data/extensions/api_test/extension_module/incognito_file/manifest.json
index 0d304812..c2f4801d 100644
--- a/chrome/test/data/extensions/api_test/extension_module/incognito_file/manifest.json
+++ b/chrome/test/data/extensions/api_test/extension_module/incognito_file/manifest.json
@@ -1,10 +1,9 @@
 {
   "name": "chrome.extension",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "description": "end-to-end browser test for some chrome.extension API: incognito mode",
   "background": {
-    "scripts": ["test.js"],
-    "persistent": true
+    "service_worker": "test.js"
   }
 }
diff --git a/chrome/test/data/extensions/api_test/extension_module/incognito_nofile/manifest.json b/chrome/test/data/extensions/api_test/extension_module/incognito_nofile/manifest.json
index 0d304812..c2f4801d 100644
--- a/chrome/test/data/extensions/api_test/extension_module/incognito_nofile/manifest.json
+++ b/chrome/test/data/extensions/api_test/extension_module/incognito_nofile/manifest.json
@@ -1,10 +1,9 @@
 {
   "name": "chrome.extension",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "description": "end-to-end browser test for some chrome.extension API: incognito mode",
   "background": {
-    "scripts": ["test.js"],
-    "persistent": true
+    "service_worker": "test.js"
   }
 }
diff --git a/chrome/test/data/extensions/api_test/history/incognito/manifest.json b/chrome/test/data/extensions/api_test/history/incognito/manifest.json
index e0065826a..63cd382 100644
--- a/chrome/test/data/extensions/api_test/history/incognito/manifest.json
+++ b/chrome/test/data/extensions/api_test/history/incognito/manifest.json
@@ -1,12 +1,11 @@
 {
   "name": "chrome.history",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "description": "end-to-end browser test for chrome.history API",
   "permissions": ["history"],
   "incognito": "split",
   "background": {
-    "scripts": ["incognito.js"],
-    "persistent": true
+    "service_worker": "incognito.js"
   }
 }
diff --git a/chrome/test/data/extensions/api_test/history/regular/delete/manifest.json b/chrome/test/data/extensions/api_test/history/regular/delete/manifest.json
index 5a7217f..fa5c52a 100644
--- a/chrome/test/data/extensions/api_test/history/regular/delete/manifest.json
+++ b/chrome/test/data/extensions/api_test/history/regular/delete/manifest.json
@@ -1,11 +1,10 @@
 {
   "name": "chrome.history",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "description": "end-to-end browser test for chrome.history API",
   "permissions": ["history"],
   "background": {
-    "scripts": ["delete.js"],
-    "persistent": true
+    "service_worker": "delete.js"
   }
 }
diff --git a/chrome/test/data/extensions/api_test/history/regular/delete_prohibited/manifest.json b/chrome/test/data/extensions/api_test/history/regular/delete_prohibited/manifest.json
index 64b163b..4a7caa0 100644
--- a/chrome/test/data/extensions/api_test/history/regular/delete_prohibited/manifest.json
+++ b/chrome/test/data/extensions/api_test/history/regular/delete_prohibited/manifest.json
@@ -1,11 +1,10 @@
 {
   "name": "chrome.history",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "description": "end-to-end browser test for chrome.history API",
   "permissions": ["history"],
   "background": {
-    "scripts": ["delete_prohibited.js"],
-    "persistent": true
+    "service_worker": "delete_prohibited.js"
   }
 }
diff --git a/chrome/test/data/extensions/api_test/history/regular/get_visits/manifest.json b/chrome/test/data/extensions/api_test/history/regular/get_visits/manifest.json
index 98229a35..2b74a63 100644
--- a/chrome/test/data/extensions/api_test/history/regular/get_visits/manifest.json
+++ b/chrome/test/data/extensions/api_test/history/regular/get_visits/manifest.json
@@ -1,11 +1,10 @@
 {
   "name": "chrome.history",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "description": "end-to-end browser test for chrome.history API",
   "permissions": ["history"],
   "background": {
-    "scripts": ["get_visits.js"],
-    "persistent": true
+    "service_worker": "get_visits.js"
   }
 }
diff --git a/chrome/test/data/extensions/api_test/history/regular/misc_search/manifest.json b/chrome/test/data/extensions/api_test/history/regular/misc_search/manifest.json
index bb05630..f57dc61 100644
--- a/chrome/test/data/extensions/api_test/history/regular/misc_search/manifest.json
+++ b/chrome/test/data/extensions/api_test/history/regular/misc_search/manifest.json
@@ -1,11 +1,10 @@
 {
   "name": "chrome.history",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "description": "end-to-end browser test for chrome.history API",
   "permissions": ["history"],
   "background": {
-    "scripts": ["misc_search.js"],
-    "persistent": true
+    "service_worker": "misc_search.js"
   }
 }
diff --git a/chrome/test/data/extensions/api_test/history/regular/search_after_add/manifest.json b/chrome/test/data/extensions/api_test/history/regular/search_after_add/manifest.json
index b23e61c..83453e9 100644
--- a/chrome/test/data/extensions/api_test/history/regular/search_after_add/manifest.json
+++ b/chrome/test/data/extensions/api_test/history/regular/search_after_add/manifest.json
@@ -1,11 +1,10 @@
 {
   "name": "chrome.history",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "description": "end-to-end browser test for chrome.history API",
   "permissions": ["history"],
   "background": {
-    "scripts": ["search_after_add.js"],
-    "persistent": true
+    "service_worker": "search_after_add.js"
   }
 }
diff --git a/chrome/test/data/extensions/api_test/history/regular/timed_search/manifest.json b/chrome/test/data/extensions/api_test/history/regular/timed_search/manifest.json
index 892da398..463eb1b 100644
--- a/chrome/test/data/extensions/api_test/history/regular/timed_search/manifest.json
+++ b/chrome/test/data/extensions/api_test/history/regular/timed_search/manifest.json
@@ -1,11 +1,10 @@
 {
   "name": "chrome.history",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "description": "end-to-end browser test for chrome.history API",
   "permissions": ["history"],
   "background": {
-    "scripts": ["timed_search.js"],
-    "persistent": true
+    "service_worker": "timed_search.js"
   }
 }
diff --git a/chrome/test/data/extensions/api_test/management/basics/manifest.json b/chrome/test/data/extensions/api_test/management/basics/manifest.json
index 113f49f..65e64db 100644
--- a/chrome/test/data/extensions/api_test/management/basics/manifest.json
+++ b/chrome/test/data/extensions/api_test/management/basics/manifest.json
@@ -1,10 +1,9 @@
 {
   "name": "Extension Management API Test",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "background": {
-    "scripts": ["basics.js"],
-    "persistent": true
+    "service_worker": "basics.js"
   },
   "permissions": ["management"]
 }
diff --git a/chrome/test/data/extensions/api_test/management/create_app_shortcut/manifest.json b/chrome/test/data/extensions/api_test/management/create_app_shortcut/manifest.json
index 62e21c0..dee940b9 100644
--- a/chrome/test/data/extensions/api_test/management/create_app_shortcut/manifest.json
+++ b/chrome/test/data/extensions/api_test/management/create_app_shortcut/manifest.json
@@ -1,10 +1,9 @@
 {
   "name": "Extension Management API Test",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "background": {
-    "scripts": ["createAppShortcut.js"],
-    "persistent": true
+    "service_worker": "createAppShortcut.js"
   },
   "permissions": ["management"]
 }
diff --git a/chrome/test/data/extensions/api_test/management/enabled_extension/manifest.json b/chrome/test/data/extensions/api_test/management/enabled_extension/manifest.json
index b20c2d8d..142c148 100644
--- a/chrome/test/data/extensions/api_test/management/enabled_extension/manifest.json
+++ b/chrome/test/data/extensions/api_test/management/enabled_extension/manifest.json
@@ -1,7 +1,7 @@
 {
   "name": "enabled_extension",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "icons": {
     "128": "icon_128.png",
     "48": "icon_48.png",
diff --git a/chrome/test/data/extensions/api_test/management/generate_app_for_link/manifest.json b/chrome/test/data/extensions/api_test/management/generate_app_for_link/manifest.json
index f626d46..fb4978c 100644
--- a/chrome/test/data/extensions/api_test/management/generate_app_for_link/manifest.json
+++ b/chrome/test/data/extensions/api_test/management/generate_app_for_link/manifest.json
@@ -1,10 +1,9 @@
 {
   "name": "Extension Management API Test",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "background": {
-    "scripts": ["generateAppForLink.js"],
-    "persistent": true
+    "service_worker": "generateAppForLink.js"
   },
   "permissions": ["management"]
 }
diff --git a/chrome/test/data/extensions/api_test/management/launch_on_install/manifest.json b/chrome/test/data/extensions/api_test/management/launch_on_install/manifest.json
index caa659a3..b5d3162 100644
--- a/chrome/test/data/extensions/api_test/management/launch_on_install/manifest.json
+++ b/chrome/test/data/extensions/api_test/management/launch_on_install/manifest.json
@@ -1,10 +1,9 @@
 {
   "name": "Launch App on Install (test extension)",
   "version": "1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "permissions": ["management"],
   "background": {
-    "scripts": ["background.js"],
-    "persistent": true
+    "service_worker": "background.js"
   }
 }
diff --git a/chrome/test/data/extensions/api_test/management/launch_type/manifest.json b/chrome/test/data/extensions/api_test/management/launch_type/manifest.json
index 9837b1b..8648f77 100644
--- a/chrome/test/data/extensions/api_test/management/launch_type/manifest.json
+++ b/chrome/test/data/extensions/api_test/management/launch_type/manifest.json
@@ -1,10 +1,9 @@
 {
   "name": "Extension Management API Test",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "background": {
-    "scripts": ["launchType.js"],
-    "persistent": true
+    "service_worker": "launchType.js"
   },
   "permissions": ["management"]
 }
diff --git a/chrome/test/data/extensions/api_test/management/management_policy/manifest.json b/chrome/test/data/extensions/api_test/management/management_policy/manifest.json
index be9a795..462599eb 100644
--- a/chrome/test/data/extensions/api_test/management/management_policy/manifest.json
+++ b/chrome/test/data/extensions/api_test/management/management_policy/manifest.json
@@ -1,10 +1,9 @@
 {
   "name": "Enable/disable policy controlled extension test",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "background": {
-    "scripts": ["background.js"],
-    "persistent": true
+    "service_worker": "background.js"
   },
   "permissions": ["management"]
 }
diff --git a/chrome/test/data/extensions/api_test/management/no_permission/manifest.json b/chrome/test/data/extensions/api_test/management/no_permission/manifest.json
index 927b237..a1514fd 100644
--- a/chrome/test/data/extensions/api_test/management/no_permission/manifest.json
+++ b/chrome/test/data/extensions/api_test/management/no_permission/manifest.json
@@ -1,9 +1,8 @@
 {
   "name": "Test that permissionless management APIs work without permission",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "background": {
-    "scripts": ["test.js"],
-    "persistent": true
+    "service_worker": "test.js"
   }
 }
diff --git a/chrome/test/data/extensions/api_test/management/permissions/manifest.json b/chrome/test/data/extensions/api_test/management/permissions/manifest.json
index b430c82..209532e 100644
--- a/chrome/test/data/extensions/api_test/management/permissions/manifest.json
+++ b/chrome/test/data/extensions/api_test/management/permissions/manifest.json
@@ -1,6 +1,12 @@
 {
   "name": "permissions",
   "version": "0.1",
-  "manifest_version": 2,
-  "permissions": ["unlimitedStorage", "notifications", "http://*/*"]
+  "manifest_version": 3,
+  "permissions": [
+    "unlimitedStorage",
+    "notifications"
+  ],
+  "host_permissions": [
+    "http://*/*"
+  ]
 }
diff --git a/chrome/test/data/extensions/api_test/management/uninstall/manifest.json b/chrome/test/data/extensions/api_test/management/uninstall/manifest.json
index 25a81212..ed2d39d 100644
--- a/chrome/test/data/extensions/api_test/management/uninstall/manifest.json
+++ b/chrome/test/data/extensions/api_test/management/uninstall/manifest.json
@@ -1,10 +1,9 @@
 {
   "name": "Extension Management API Test",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "background": {
-    "scripts": ["uninstall.js"],
-    "persistent": true
+    "service_worker": "uninstall.js"
   },
   "permissions": ["management"]
 }
diff --git a/chrome/test/data/extensions/api_test/metrics/manifest.json b/chrome/test/data/extensions/api_test/metrics/manifest.json
index c844a9a..927f7d99 100644
--- a/chrome/test/data/extensions/api_test/metrics/manifest.json
+++ b/chrome/test/data/extensions/api_test/metrics/manifest.json
@@ -2,11 +2,10 @@
   "key": "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDJ5+2VmV02HP3jkMhxbp2yuyzf4K4JsffgCf8zsPljLVV2A+JXGj0TbvzvDP3RDCKC5qPMprQkoYlKVW2b6H0kQ8dNmZsjxMqEz/ZDx4Z6/VvbMaz8pP+dENs5Io5XlG5Op2nsJF+y+LqbX6qbff9D/s4fTWyqKillpJN+48qs0wIBIw==",
   "name": "chrome.metrics",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "description": "end-to-end browser test for chrome.metricsPrivate API",
   "background": {
-    "scripts": ["test.js"],
-    "persistent": true
+    "service_worker": "test.js"
   },
   "permissions": ["metricsPrivate"]
 }
diff --git a/chrome/test/data/extensions/api_test/native_messaging/lazy/manifest.json b/chrome/test/data/extensions/api_test/native_messaging/lazy/manifest.json
deleted file mode 100644
index 9e7f7415..0000000
--- a/chrome/test/data/extensions/api_test/native_messaging/lazy/manifest.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  // Extension ID: knldjmfmopnpolahpmmgbagdohdnhkik
-  "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaYhSpDCE9RPocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB",
-  "version": "1.0.0.0",
-  "manifest_version": 2,
-  "name": "native messaging test",
-  "description": "Tests one time messaging functionality in event page or SW based extension.",
-  "background": {
-    "scripts": ["test.js"],
-    "persistent": false
-  },
-  "permissions": [
-    "nativeMessaging"
-  ]
-}
diff --git a/chrome/test/data/extensions/api_test/native_messaging/manifest.json b/chrome/test/data/extensions/api_test/native_messaging/manifest.json
index 2180db2..3fc3b70 100644
--- a/chrome/test/data/extensions/api_test/native_messaging/manifest.json
+++ b/chrome/test/data/extensions/api_test/native_messaging/manifest.json
@@ -2,12 +2,11 @@
   // Extension ID: knldjmfmopnpolahpmmgbagdohdnhkik
   "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaYhSpDCE9RPocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB",
   "version": "1.0.0.0",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "name": "native messaging test",
-  "description": "Test the basic functionality of passing native messages from a persistent extension.",
+  "description": "Test the basic functionality of passing native messages from an extension.",
   "background": {
-    "scripts": ["lazy/test.js"],
-    "persistent": true
+    "service_worker": "test.js"
   },
   "permissions": [
     "nativeMessaging"
diff --git a/chrome/test/data/extensions/api_test/native_messaging/lazy/test.js b/chrome/test/data/extensions/api_test/native_messaging/test.js
similarity index 100%
rename from chrome/test/data/extensions/api_test/native_messaging/lazy/test.js
rename to chrome/test/data/extensions/api_test/native_messaging/test.js
diff --git a/chrome/test/data/extensions/api_test/native_messaging_connect/lazy/manifest.json b/chrome/test/data/extensions/api_test/native_messaging_connect/lazy/manifest.json
deleted file mode 100644
index ee65939..0000000
--- a/chrome/test/data/extensions/api_test/native_messaging_connect/lazy/manifest.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  // Extension ID: knldjmfmopnpolahpmmgbagdohdnhkik
-  "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaYhSpDCE9RPocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB",
-  "version": "1.0.0.0",
-  "manifest_version": 2,
-  "name": "native messaging test",
-  "description": "Tests long lived native messaging functionality in event page or SW based extension.",
-  "background": {
-    "scripts": ["test.js"],
-    "persistent": false
-  },
-  "permissions": [
-    "nativeMessaging"
-  ]
-}
diff --git a/chrome/test/data/extensions/api_test/native_messaging_connect/manifest.json b/chrome/test/data/extensions/api_test/native_messaging_connect/manifest.json
index a3b4f38cf..36ade20 100644
--- a/chrome/test/data/extensions/api_test/native_messaging_connect/manifest.json
+++ b/chrome/test/data/extensions/api_test/native_messaging_connect/manifest.json
@@ -2,12 +2,11 @@
   // Extension ID: knldjmfmopnpolahpmmgbagdohdnhkik
   "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaYhSpDCE9RPocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB",
   "version": "1.0.0.0",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "name": "native messaging test",
-  "description": "Tests long lived native messaging functionality in persistent extension",
+  "description": "Tests long lived native messaging functionality",
   "background": {
-    "scripts": ["lazy/test.js"],
-    "persistent": true
+    "service_worker": "test.js"
   },
   "permissions": [
     "nativeMessaging"
diff --git a/chrome/test/data/extensions/api_test/native_messaging_connect/lazy/test.js b/chrome/test/data/extensions/api_test/native_messaging_connect/test.js
similarity index 100%
rename from chrome/test/data/extensions/api_test/native_messaging_connect/lazy/test.js
rename to chrome/test/data/extensions/api_test/native_messaging_connect/test.js
diff --git a/chrome/test/data/extensions/api_test/omnibox/manifest.json b/chrome/test/data/extensions/api_test/omnibox/manifest.json
index 8bc5397..41fbe5ed 100644
--- a/chrome/test/data/extensions/api_test/omnibox/manifest.json
+++ b/chrome/test/data/extensions/api_test/omnibox/manifest.json
@@ -1,10 +1,10 @@
 {
   "name": "chrome.extension.omnibox",
   "version": "0.1",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "description": "end-to-end browser test for chrome.omnibox API",
   "background": {
-    "scripts": ["test.js"]
+    "service_worker": "test.js"
   },
   "incognito": "split",
   "omnibox": { "keyword": "kw" }
diff --git a/chrome/test/data/extensions/api_test/printer_provider/request_capability/manifest.json b/chrome/test/data/extensions/api_test/printer_provider/request_capability/manifest.json
index 4107676..c56564b 100644
--- a/chrome/test/data/extensions/api_test/printer_provider/request_capability/manifest.json
+++ b/chrome/test/data/extensions/api_test/printer_provider/request_capability/manifest.json
@@ -1,11 +1,10 @@
 {
   "name": "Test printer provider",
   "description": "Test extension for chrome.printerProvider.onGetCapabilityRequested event",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "version": "0.1",
   "background": {
-    "scripts": ["test.js"],
-    "persistent": false
+    "service_worker": "test.js"
   },
   "permissions": [
     "printerProvider"
diff --git a/chrome/test/data/extensions/api_test/printer_provider/request_print/manifest.json b/chrome/test/data/extensions/api_test/printer_provider/request_print/manifest.json
index 783b5ab..473810c 100644
--- a/chrome/test/data/extensions/api_test/printer_provider/request_print/manifest.json
+++ b/chrome/test/data/extensions/api_test/printer_provider/request_print/manifest.json
@@ -1,11 +1,10 @@
 {
   "name": "Test printer provider",
   "description": "Test extension for chrome.printerProvider.onPrintRequested event",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "version": "0.1",
   "background": {
-    "scripts": ["test.js"],
-    "persistent": false
+    "service_worker": "test.js"
   },
   "permissions": [
     "printerProvider"
diff --git a/chrome/test/data/extensions/api_test/printer_provider/request_printers/manifest.json b/chrome/test/data/extensions/api_test/printer_provider/request_printers/manifest.json
index 293f4b5..1394c3d 100644
--- a/chrome/test/data/extensions/api_test/printer_provider/request_printers/manifest.json
+++ b/chrome/test/data/extensions/api_test/printer_provider/request_printers/manifest.json
@@ -1,11 +1,10 @@
 {
   "name": "Test printer provider",
   "description": "Test extension for chrome.printerProvider.onGetPrintersRequested event",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "version": "0.1",
   "background": {
-    "scripts": ["test.js"],
-    "persistent": false
+    "service_worker": "test.js"
   },
   "permissions": [
     "printerProvider"
diff --git a/chrome/test/data/extensions/api_test/printer_provider/request_printers_second/manifest.json b/chrome/test/data/extensions/api_test/printer_provider/request_printers_second/manifest.json
index 293f4b5..1394c3d 100644
--- a/chrome/test/data/extensions/api_test/printer_provider/request_printers_second/manifest.json
+++ b/chrome/test/data/extensions/api_test/printer_provider/request_printers_second/manifest.json
@@ -1,11 +1,10 @@
 {
   "name": "Test printer provider",
   "description": "Test extension for chrome.printerProvider.onGetPrintersRequested event",
-  "manifest_version": 2,
+  "manifest_version": 3,
   "version": "0.1",
   "background": {
-    "scripts": ["test.js"],
-    "persistent": false
+    "service_worker": "test.js"
   },
   "permissions": [
     "printerProvider"
diff --git a/chrome/test/data/webui/contextual_tasks/composebox_misc_inputs_test.ts b/chrome/test/data/webui/contextual_tasks/composebox_misc_inputs_test.ts
index ac5ea33..58cb0ba 100644
--- a/chrome/test/data/webui/contextual_tasks/composebox_misc_inputs_test.ts
+++ b/chrome/test/data/webui/contextual_tasks/composebox_misc_inputs_test.ts
@@ -12,6 +12,7 @@
 import {PageCallbackRouter as ComposeboxPageCallbackRouter, PageHandlerRemote as ComposeboxPageHandlerRemote} from 'chrome://resources/cr_components/composebox/composebox.mojom-webui.js';
 import {ComposeboxProxyImpl} from 'chrome://resources/cr_components/composebox/composebox_proxy.js';
 import {ContextUploadStatus, ToolMode as ComposeboxToolMode} from 'chrome://resources/cr_components/composebox/composebox_query.mojom-webui.js';
+import type {ComposeboxVoiceSearchElement} from 'chrome://resources/cr_components/composebox/composebox_voice_search.js';
 import type {ComposeboxFileCarouselElement} from 'chrome://resources/cr_components/composebox/file_carousel.js';
 import type {ComposeboxFileThumbnailElement} from 'chrome://resources/cr_components/composebox/file_thumbnail.js';
 import {WindowProxy} from 'chrome://resources/cr_components/composebox/window_proxy.js';
@@ -23,10 +24,10 @@
 import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import {MockTimer} from 'chrome://webui-test/mock_timer.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
-import {microtasksFinished} from 'chrome://webui-test/test_util.js';
+import {$$, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 import {TestContextualTasksBrowserProxy} from './test_contextual_tasks_browser_proxy.js';
-import {ADD_FILE_CONTEXT_FN, ADD_TAB_CONTEXT_FN, assertStyle, deleteLastFile, fixtureUrl, FAKE_TOKEN_STRING, installMock, mockInputState} from './test_utils.js';
+import {ADD_FILE_CONTEXT_FN, ADD_TAB_CONTEXT_FN, assertStyle, deleteLastFile, FAKE_TOKEN_STRING, fixtureUrl, installMock, mockInputState} from './test_utils.js';
 
 async function dispatchDragAndDropEvent(dropZone: Element, files: File[]) {
   if (!dropZone) {
@@ -201,6 +202,7 @@
                                                    removeListener() {},
                                                    removeEventListener() {},
                                                  }));
+    windowProxy.setResultFor('hasWebkitSpeechRecognition', true);
 
     window.webkitSpeechRecognition =
         MockSpeechRecognition as unknown as typeof SpeechRecognition;
@@ -350,7 +352,8 @@
 
         assertTrue(mockSpeechRecognition.voiceSearchInProgress);
         assertStyle(composeboxDiv, 'opacity', '0');
-        assertStyle(composebox.$.voiceSearch, 'display', 'inline');
+        assertStyle(
+            $$(composebox, 'cr-composebox-voice-search'), 'display', 'inline');
         assertEquals(composebox.animationState, GlowAnimationState.LISTENING);
         assertEquals(
             1,
@@ -565,20 +568,18 @@
     await microtasksFinished();
     await composebox.updateComplete;
 
-    const voiceSearchElement = composebox.$.voiceSearch;
-    const errorContainer =
-        voiceSearchElement.shadowRoot.querySelector<HTMLElement>(
-            '#error-container');
-    const inputElement =
-        voiceSearchElement.shadowRoot.querySelector<HTMLTextAreaElement>(
-            '#input');
+    const voiceSearchElement = $$(composebox, 'cr-composebox-voice-search');
+    assertTrue(!!voiceSearchElement);
+    const errorContainer = $$(voiceSearchElement, '#error-container');
+    const inputElement = $$(voiceSearchElement, '#input');
 
     assertTrue(!!errorContainer);
     assertFalse(errorContainer.hidden);
     assertFalse(errorContainer.hidden, 'Error container should not be hidden');
     assertTrue(inputElement!.hidden);
     assertStyle(composeboxDiv, 'opacity', '0');
-    assertStyle(composebox.$.voiceSearch, 'display', 'inline');
+    assertStyle(
+        $$(composebox, 'cr-composebox-voice-search'), 'display', 'inline');
     assertEquals(composebox.animationState, GlowAnimationState.LISTENING);
 
     mockSpeechRecognition.onend!();
@@ -597,7 +598,10 @@
         const composeboxDiv =
             contextualTasksApp.$.composebox.$.composebox.$.composebox;
         const composebox = contextualTasksApp.$.composebox.$.composebox;
-        composebox.$.voiceSearch.start();
+        const voiceSearch = $$<ComposeboxVoiceSearchElement>(
+            composebox, 'cr-composebox-voice-search');
+        assertTrue(!!voiceSearch);
+        voiceSearch.start();
         await microtasksFinished();
         assertEquals(
             0,
@@ -611,10 +615,9 @@
         await composebox.updateComplete;
         await microtasksFinished();
 
-        const voiceSearchElement = composebox.$.voiceSearch;
-        const errorContainer =
-            voiceSearchElement.shadowRoot.querySelector<HTMLElement>(
-                '#error-container');
+        const voiceSearchElement = $$(composebox, 'cr-composebox-voice-search');
+        assertTrue(!!voiceSearchElement);
+        const errorContainer = $$(voiceSearchElement, '#error-container');
         assertTrue(!!errorContainer);
         assertTrue(errorContainer.hidden);
 
@@ -640,8 +643,10 @@
         contextualTasksApp.$.composebox.$.composebox.$.composebox;
     const composebox = contextualTasksApp.$.composebox.$.composebox;
     const voiceSearchButton = getVoiceSearchButton(composebox);
-    const voiceSearchElement = composebox.$.voiceSearch;
-    voiceSearchButton!.click();
+    const voiceSearchElement = $$<ComposeboxVoiceSearchElement>(
+        composebox, 'cr-composebox-voice-search');
+    assertTrue(!!voiceSearchButton && !!voiceSearchElement);
+    voiceSearchButton.click();
     await microtasksFinished();
 
     const result = createResults(2);
@@ -680,31 +685,27 @@
     await microtasksFinished();
     await composebox.updateComplete;
 
-    await contextEntrypoint.dispatchEvent(
-        new CustomEvent('tool-click', {
-          detail: {toolMode: ComposeboxToolMode.kDeepSearch},
-          bubbles: true,
-          composed: true,
-        }));
+    await contextEntrypoint.dispatchEvent(new CustomEvent('tool-click', {
+      detail: {toolMode: ComposeboxToolMode.kDeepSearch},
+      bubbles: true,
+      composed: true,
+    }));
     await microtasksFinished();
     await composebox.updateComplete;
     assertEquals(
-        ComposeboxToolMode.kDeepSearch,
-        composebox.activeToolMode,
+        ComposeboxToolMode.kDeepSearch, composebox.activeToolMode,
         'Active tool should be Deep Search after clicking tool');
-    await contextEntrypoint.dispatchEvent(
-        new CustomEvent('tool-click', {
-          detail: {toolMode: ComposeboxToolMode.kDeepSearch},
-          bubbles: true,
-          composed: true,
-        }));
+    await contextEntrypoint.dispatchEvent(new CustomEvent('tool-click', {
+      detail: {toolMode: ComposeboxToolMode.kDeepSearch},
+      bubbles: true,
+      composed: true,
+    }));
 
     await microtasksFinished();
     await composebox.updateComplete;
 
     assertEquals(
-        ComposeboxToolMode.kUnspecified,
-        composebox.activeToolMode,
+        ComposeboxToolMode.kUnspecified, composebox.activeToolMode,
         'Active tool should be unspecified after clicking tool twice');
   });
 
@@ -717,34 +718,31 @@
     await microtasksFinished();
     await composebox.updateComplete;
 
-    await contextEntrypoint.dispatchEvent(
-        new CustomEvent(
-            'create-image-click',
-            {
-              bubbles: true,
-              composed: true,
-            },
-            ));
+    await contextEntrypoint.dispatchEvent(new CustomEvent(
+        'create-image-click',
+        {
+          bubbles: true,
+          composed: true,
+        },
+        ));
     await microtasksFinished();
     await composebox.updateComplete;
     assertEquals(
         ComposeboxToolMode.kImageGen, composebox.activeToolMode,
         'Active tool should be nano after clicking tool');
-    await contextEntrypoint.dispatchEvent(
-        new CustomEvent(
-            'create-image-click',
-            {
-              bubbles: true,
-              composed: true,
-            },
-            ));
+    await contextEntrypoint.dispatchEvent(new CustomEvent(
+        'create-image-click',
+        {
+          bubbles: true,
+          composed: true,
+        },
+        ));
 
     await microtasksFinished();
     await composebox.updateComplete;
 
     assertEquals(
-        ComposeboxToolMode.kUnspecified,
-        composebox.activeToolMode,
+        ComposeboxToolMode.kUnspecified, composebox.activeToolMode,
         'Active tool should be unspecified after clicking tool twice');
   });
 
@@ -754,6 +752,10 @@
     // context menu mode, we just call the underlying function that responds to
     // both `tool-click` and individual `deep-search-click` events.
     composebox.onToolClickForTesting(ComposeboxToolMode.kDeepSearch);
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kDeepSearch,
+    });
     await composebox.updateComplete;
     await microtasksFinished();
 
@@ -785,6 +787,11 @@
 
   test('Image tool is not reset after submitting a query', async () => {
     composebox.onToolClickForTesting(ComposeboxToolMode.kImageGen);
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kImageGen,
+    });
+
     await composebox.updateComplete;
     await microtasksFinished();
 
@@ -813,6 +820,11 @@
 
   test('Canvas tool is not reset after submitting a query', async () => {
     composebox.onToolClickForTesting(ComposeboxToolMode.kCanvas);
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kCanvas,
+    });
+
     await composebox.updateComplete;
     await microtasksFinished();
 
@@ -839,6 +851,10 @@
 
   test('Deepsearch mode: cancel resets mode', async () => {
     composebox.onToolClickForTesting(ComposeboxToolMode.kDeepSearch);
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kDeepSearch,
+    });
 
     await composebox.updateComplete;
     await microtasksFinished();
@@ -848,6 +864,10 @@
     assertTrue(!!deepSearchChip, 'Deep search chip should be present');
     // Simulate cancel button click without having to fully render button.
     composebox.onCancelClick_();
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kUnspecified,
+    });
 
     await composebox.updateComplete;
     await microtasksFinished();
@@ -858,6 +878,11 @@
 
   test('Image mode: cancel resets mode', async () => {
     composebox.onToolClickForTesting(ComposeboxToolMode.kImageGen);
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kImageGen,
+    });
+
 
     await composebox.updateComplete;
     await microtasksFinished();
@@ -866,6 +891,10 @@
     assertTrue(!!imageChip, 'Nano banana chip should be present');
     // Simulate cancel button click without having to fully render button.
     composebox.onCancelClick_();
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kUnspecified,
+    });
 
     await composebox.updateComplete;
     await microtasksFinished();
@@ -876,6 +905,11 @@
 
   test('canvas mode: cancel resets mode', async () => {
     composebox.onToolClickForTesting(ComposeboxToolMode.kCanvas);
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kCanvas,
+    });
+
 
     await composebox.updateComplete;
     await microtasksFinished();
@@ -885,6 +919,10 @@
     assertTrue(!!canvasChip, 'Canvas chip should be present');
     // Simulate cancel button click without having to fully render button.
     composebox.onCancelClick_();
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kUnspecified,
+    });
 
     await composebox.updateComplete;
     await microtasksFinished();
@@ -895,6 +933,11 @@
 
   test('Deepsearch mode: esc resets mode', async () => {
     composebox.onToolClickForTesting(ComposeboxToolMode.kDeepSearch);
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kDeepSearch,
+    });
+
 
     await composebox.updateComplete;
     await microtasksFinished();
@@ -902,6 +945,10 @@
 
     assertTrue(!!deepSearchChip, 'Deep search chip should be present');
     composebox.handleEscapeKeyLogic();
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kUnspecified,
+    });
 
     await composebox.updateComplete;
     await microtasksFinished();
@@ -912,6 +959,11 @@
 
   test('Image mode: esc resets mode', async () => {
     composebox.onToolClickForTesting(ComposeboxToolMode.kImageGen);
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kImageGen,
+    });
+
 
     await composebox.updateComplete;
     await microtasksFinished();
@@ -919,7 +971,10 @@
 
     assertTrue(!!imageChip, 'Nano banana chip should be present');
     composebox.handleEscapeKeyLogic();
-
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kUnspecified,
+    });
     await composebox.updateComplete;
     await microtasksFinished();
 
@@ -929,6 +984,10 @@
 
   test('canvas mode: esc resets mode', async () => {
     composebox.onToolClickForTesting(ComposeboxToolMode.kCanvas);
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kCanvas,
+    });
 
     await composebox.updateComplete;
     await microtasksFinished();
@@ -936,6 +995,10 @@
 
     assertTrue(!!canvasChip, 'Canvas chip should be present');
     composebox.handleEscapeKeyLogic();
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kUnspecified,
+    });
 
     await composebox.updateComplete;
     await microtasksFinished();
diff --git a/chrome/test/data/webui/contextual_tasks/composebox_test.ts b/chrome/test/data/webui/contextual_tasks/composebox_test.ts
index 216b5406e..f469764 100644
--- a/chrome/test/data/webui/contextual_tasks/composebox_test.ts
+++ b/chrome/test/data/webui/contextual_tasks/composebox_test.ts
@@ -9,7 +9,7 @@
 import type {ComposeboxFile} from 'chrome://resources/cr_components/composebox/common.js';
 import {PageCallbackRouter as ComposeboxPageCallbackRouter, PageHandlerRemote as ComposeboxPageHandlerRemote} from 'chrome://resources/cr_components/composebox/composebox.mojom-webui.js';
 import {ComposeboxProxyImpl} from 'chrome://resources/cr_components/composebox/composebox_proxy.js';
-import {ContextUploadStatus} from 'chrome://resources/cr_components/composebox/composebox_query.mojom-webui.js';
+import {ContextUploadStatus, ToolMode} from 'chrome://resources/cr_components/composebox/composebox_query.mojom-webui.js';
 import {createAutocompleteMatch, createAutocompleteResultForTesting} from 'chrome://resources/cr_components/searchbox/searchbox_browser_proxy.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PageCallbackRouter as SearchboxPageCallbackRouter, PageHandlerRemote as SearchboxPageHandlerRemote, type PageRemote as SearchboxPageRemote} from 'chrome://resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js';
@@ -562,6 +562,78 @@
     assertEquals(1, composeboxElement.numberOfTimesTooltipShownForTesting);
   });
 
+  test('ToolChipVisibilityBasedOnInputState', async () => {
+    const innerComposebox = contextualTasksApp.$.composebox.$.composebox;
+
+    const getChip = (id: string) =>
+        innerComposebox.shadowRoot?.querySelector<HTMLElement>(`#${id}`);
+
+    // Initial state: No tool active.
+    let newInputState = {
+      ...mockInputState,
+      activeTool: ToolMode.kUnspecified,
+    };
+    searchboxCallbackRouterRemote.onInputStateChanged(newInputState);
+    await microtasksFinished();
+    await innerComposebox.updateComplete;
+
+    assertFalse(isVisible(getChip('deepSearchChip')));
+    assertFalse(isVisible(getChip('nanoBananaChip')));
+    assertFalse(isVisible(getChip('canvasChip')));
+
+    // Activate Deep Search.
+    newInputState = {
+      ...mockInputState,
+      activeTool: ToolMode.kDeepSearch,
+    };
+    searchboxCallbackRouterRemote.onInputStateChanged(newInputState);
+    await microtasksFinished();
+    await innerComposebox.updateComplete;
+
+    assertTrue(isVisible(getChip('deepSearchChip')));
+    assertFalse(isVisible(getChip('nanoBananaChip')));
+    assertFalse(isVisible(getChip('canvasChip')));
+
+    // Activate Image Gen (nanoBananaChip).
+    newInputState = {
+      ...mockInputState,
+      activeTool: ToolMode.kImageGen,
+    };
+    searchboxCallbackRouterRemote.onInputStateChanged(newInputState);
+    await microtasksFinished();
+    await innerComposebox.updateComplete;
+
+    assertFalse(isVisible(getChip('deepSearchChip')));
+    assertTrue(isVisible(getChip('nanoBananaChip')));
+    assertFalse(isVisible(getChip('canvasChip')));
+
+    // Activate Canvas.
+    newInputState = {
+      ...mockInputState,
+      activeTool: ToolMode.kCanvas,
+    };
+    searchboxCallbackRouterRemote.onInputStateChanged(newInputState);
+    await microtasksFinished();
+    await innerComposebox.updateComplete;
+
+    assertFalse(isVisible(getChip('deepSearchChip')));
+    assertFalse(isVisible(getChip('nanoBananaChip')));
+    assertTrue(isVisible(getChip('canvasChip')));
+
+    // Back to Unspecified.
+    newInputState = {
+      ...mockInputState,
+      activeTool: ToolMode.kUnspecified,
+    };
+    searchboxCallbackRouterRemote.onInputStateChanged(newInputState);
+    await microtasksFinished();
+    await innerComposebox.updateComplete;
+
+    assertFalse(isVisible(getChip('deepSearchChip')));
+    assertFalse(isVisible(getChip('nanoBananaChip')));
+    assertFalse(isVisible(getChip('canvasChip')));
+  });
+
   test('EnterKeyAfterSubmitDoesNotAddNewLine', async () => {
     mockTimer.install();
     const TEST_QUERY = 'test query';
diff --git a/chrome/test/data/webui/contextual_tasks/composebox_zero_state_test.ts b/chrome/test/data/webui/contextual_tasks/composebox_zero_state_test.ts
index 1329896..1f549d2 100644
--- a/chrome/test/data/webui/contextual_tasks/composebox_zero_state_test.ts
+++ b/chrome/test/data/webui/contextual_tasks/composebox_zero_state_test.ts
@@ -723,6 +723,10 @@
 
   test('deep search: thread change resets input', async () => {
     composebox.onToolClickForTesting(ComposeboxToolMode.kDeepSearch);
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kDeepSearch,
+    });
 
     await composebox.updateComplete;
     await microtasksFinished();
@@ -748,6 +752,10 @@
 
   test('image gen: thread change resets input', async () => {
     composebox.onToolClickForTesting(ComposeboxToolMode.kImageGen);
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kImageGen,
+    });
 
     await composebox.updateComplete;
     await microtasksFinished();
@@ -774,6 +782,10 @@
 
   test('canvas: thread change resets input', async () => {
     composebox.onToolClickForTesting(ComposeboxToolMode.kCanvas);
+    searchboxCallbackRouterRemote.onInputStateChanged({
+      ...mockInputState,
+      activeTool: ComposeboxToolMode.kCanvas,
+    });
 
     await composebox.updateComplete;
     await microtasksFinished();
diff --git a/chrome/test/data/webui/cr_components/composebox/composebox_voice_search_test.ts b/chrome/test/data/webui/cr_components/composebox/composebox_voice_search_test.ts
index de76cf7..b23ef32 100644
--- a/chrome/test/data/webui/cr_components/composebox/composebox_voice_search_test.ts
+++ b/chrome/test/data/webui/cr_components/composebox/composebox_voice_search_test.ts
@@ -4,12 +4,12 @@
 
 import 'chrome://new-tab-page/strings.m.js';
 import 'chrome://resources/cr_components/composebox/composebox.js';
+import 'chrome://resources/cr_components/composebox/composebox_voice_search.js';
 
 import type {ComposeboxElement} from 'chrome://resources/cr_components/composebox/composebox.js';
-// import {VoiceSearchAction} from
-// 'chrome://resources/cr_components/composebox/composebox.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://resources/cr_components/composebox/composebox.mojom-webui.js';
 import {ComposeboxProxyImpl} from 'chrome://resources/cr_components/composebox/composebox_proxy.js';
+import type {ComposeboxVoiceSearchElement} from 'chrome://resources/cr_components/composebox/composebox_voice_search.js';
 import {WindowProxy} from 'chrome://resources/cr_components/composebox/window_proxy.js';
 import type {AudioWaveElement} from 'chrome://resources/cr_components/search/audio_wave.js';
 import {GlowAnimationState} from 'chrome://resources/cr_components/search/constants.js';
@@ -17,7 +17,7 @@
 import {PageCallbackRouter as SearchboxPageCallbackRouter, PageHandlerRemote as SearchboxPageHandlerRemote} from 'chrome://resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import type {TestMock} from 'chrome://webui-test/test_mock.js';
-import {microtasksFinished} from 'chrome://webui-test/test_util.js';
+import {$$, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 import {assertStyle, installMock} from './composebox_test_utils.js';
 
@@ -83,7 +83,7 @@
   } as unknown as SpeechRecognitionEvent;
 }
 
-suite('Composebox voice search', () => {
+suite('ComposeboxVoiceSearch', () => {
   let composeboxElement: ComposeboxElement;
   let handler: TestMock<PageHandlerRemote>;
   let searchboxHandler: TestMock<SearchboxPageHandlerRemote>;
@@ -131,6 +131,7 @@
                                                    removeListener() {},
                                                    removeEventListener() {},
                                                  }));
+    windowProxy.setResultFor('hasWebkitSpeechRecognition', true);
 
     composeboxElement = document.createElement('cr-composebox');
     document.body.appendChild(composeboxElement);
@@ -144,6 +145,14 @@
         '#voiceSearchButton');
   }
 
+  function getVoiceSearchElement(composeboxElement: ComposeboxElement):
+      ComposeboxVoiceSearchElement {
+    const voiceSearchElement = $$<ComposeboxVoiceSearchElement>(
+        composeboxElement, 'cr-composebox-voice-search');
+    assertTrue(!!voiceSearchElement);
+    return voiceSearchElement;
+  }
+
   test('voice search button does not show when disabled', async () => {
     loadTimeData.overrideValues({
       steadyComposeboxShowVoiceSearch: false,
@@ -154,7 +163,7 @@
     document.body.appendChild(composeboxElement);
     await microtasksFinished();
 
-    const voiceSearchButton = await getVoiceSearchButton(composeboxElement);
+    const voiceSearchButton = getVoiceSearchButton(composeboxElement);
     assertFalse(!!voiceSearchButton);
 
     // Restore.
@@ -182,7 +191,8 @@
         // Clicking the voice search button should start speech recognition.
         assertTrue(mockSpeechRecognition.voiceSearchInProgress);
         assertStyle(composeboxElement.$.composebox, 'opacity', '0');
-        assertStyle(composeboxElement.$.voiceSearch, 'display', 'inline');
+        assertStyle(
+            getVoiceSearchElement(composeboxElement), 'display', 'inline');
         assertEquals(
             composeboxElement.animationState, GlowAnimationState.LISTENING);
       });
@@ -196,7 +206,7 @@
     mockSpeechRecognition.onresult!(result);
     await microtasksFinished();
 
-    const voiceSearchInput = composeboxElement.$.voiceSearch.$.input;
+    const voiceSearchInput = getVoiceSearchElement(composeboxElement).$.input;
 
     assertEquals('helloworld', voiceSearchInput.value);
 
@@ -234,13 +244,14 @@
     await microtasksFinished();
     await showPromise;
     await composeboxElement.updateComplete;
-    await composeboxElement.$.voiceSearch.updateComplete;
+    const voiceSearchElement = getVoiceSearchElement(composeboxElement);
+    await voiceSearchElement.updateComplete;
 
     // Assert.
     assertFalse(mockSpeechRecognition.voiceSearchInProgress);
     assertEquals(searchboxHandler.getCallCount('submitQuery'), 0);
     assertStyle(composeboxElement.$.composebox, 'display', 'flex');
-    assertStyle(composeboxElement.$.voiceSearch, 'display', 'none');
+    assertStyle(voiceSearchElement, 'display', 'none');
     assertEquals(composeboxElement.animationState, GlowAnimationState.NONE);
   });
 
@@ -265,13 +276,13 @@
     await microtasksFinished();
     await showPromise;
     await composeboxElement.updateComplete;
-    await composeboxElement.$.voiceSearch.updateComplete;
+    await getVoiceSearchElement(composeboxElement).updateComplete;
 
     // Assert.
     assertEquals(searchboxHandler.getCallCount('submitQuery'), 1);
 
     assertStyle(composeboxElement.$.composebox, 'display', 'flex');
-    assertStyle(composeboxElement.$.voiceSearch, 'display', 'none');
+    assertStyle(getVoiceSearchElement(composeboxElement), 'display', 'none');
   });
 
   test(
@@ -295,25 +306,24 @@
 
         // Act.
         mockSpeechRecognition.onresult!(result);
-        assertEquals(
-            (composeboxElement.$.voiceSearch as any).interimResult_, 'hello');
-        assertEquals(
-            (composeboxElement.$.voiceSearch as any).finalResult_, 'world');
-        assertEquals(
-            (composeboxElement.$.voiceSearch as any).transcript_, 'helloworld');
+        const voiceSearchElement =
+            getVoiceSearchElement(composeboxElement) as any;
+        assertEquals(voiceSearchElement.interimResult_, 'hello');
+        assertEquals(voiceSearchElement.finalResult_, 'world');
+        assertEquals(voiceSearchElement.transcript_, 'helloworld');
 
         const [callback] = await windowProxy.whenCalled('setTimeout');
         callback();
         await microtasksFinished();
         await showPromise;
         await composeboxElement.updateComplete;
-        await composeboxElement.$.voiceSearch.updateComplete;
+        await voiceSearchElement.updateComplete;
 
         // Assert.
         assertEquals(searchboxHandler.getCallCount('submitQuery'), 1);
 
         assertStyle(composeboxElement.$.composebox, 'display', 'flex');
-        assertStyle(composeboxElement.$.voiceSearch, 'display', 'none');
+        assertStyle(voiceSearchElement, 'display', 'none');
       });
 
   test('idle timeout with final result submits query', async () => {
@@ -333,22 +343,21 @@
 
     // Act.
     mockSpeechRecognition.onresult!(result);
-    assertEquals(
-        (composeboxElement.$.voiceSearch as any).finalResult_, 'helloworld');
-    assertEquals(
-        (composeboxElement.$.voiceSearch as any).transcript_, 'helloworld');
+    const voiceSearchElement = getVoiceSearchElement(composeboxElement) as any;
+    assertEquals(voiceSearchElement.finalResult_, 'helloworld');
+    assertEquals(voiceSearchElement.transcript_, 'helloworld');
 
     const [callback] = await windowProxy.whenCalled('setTimeout');
     callback();
     await microtasksFinished();
     await showPromise;
     await composeboxElement.updateComplete;
-    await composeboxElement.$.voiceSearch.updateComplete;
+    await voiceSearchElement.updateComplete;
     // Assert.
     assertEquals(searchboxHandler.getCallCount('submitQuery'), 1);
 
     assertStyle(composeboxElement.$.composebox, 'display', 'flex');
-    assertStyle(composeboxElement.$.voiceSearch, 'display', 'none');
+    assertStyle(voiceSearchElement, 'display', 'none');
   });
 
   test(
@@ -377,10 +386,12 @@
         await microtasksFinished();
         await showPromise;
         await composeboxElement.updateComplete;
-        await composeboxElement.$.voiceSearch.updateComplete;
-        assertEquals((composeboxElement.$.voiceSearch as any).finalResult_, '');
+        const voiceSearchElement =
+            getVoiceSearchElement(composeboxElement) as any;
+        await voiceSearchElement.updateComplete;
+        assertEquals(voiceSearchElement.finalResult_, '');
         assertEquals(
-            (composeboxElement.$.voiceSearch as any).transcript_, 'helloworld',
+            voiceSearchElement.transcript_, 'helloworld',
             'transcript should be set after result is' +
                 'processed but not finalized');
 
@@ -388,15 +399,16 @@
         callback();
         await microtasksFinished();
 
-        assertEquals((composeboxElement.$.voiceSearch as any).finalResult_, '');
+        assertEquals(voiceSearchElement.finalResult_, '');
         assertEquals(
-            (composeboxElement.$.voiceSearch as any).transcript_, '',
+            voiceSearchElement.transcript_, '',
             'transcript should be cleared after onend with no final result');
 
         // Assert.
         assertEquals(searchboxHandler.getCallCount('submitQuery'), 1);
         assertStyle(composeboxElement.$.composebox, 'display', 'flex');
-        assertStyle(composeboxElement.$.voiceSearch, 'display', 'none');
+        assertStyle(
+            getVoiceSearchElement(composeboxElement), 'display', 'none');
         assertEquals(
             composeboxElement.animationState, GlowAnimationState.SUBMITTING);
       });
@@ -419,32 +431,33 @@
     const showPromise =
         getTransitionEndPromise(composeboxElement.$.composebox, 'opacity');
 
-    assertEquals((composeboxElement.$.voiceSearch as any).finalResult_, '');
+    const voiceSearchElement = getVoiceSearchElement(composeboxElement) as any;
+    assertEquals(voiceSearchElement.finalResult_, '');
     assertEquals(
-        (composeboxElement.$.voiceSearch as any).transcript_, 'helloworld',
+        voiceSearchElement.transcript_, 'helloworld',
         'transcript should be set after result is processed');
     await showPromise;
     await composeboxElement.updateComplete;
-    await composeboxElement.$.voiceSearch.updateComplete;
+    await getVoiceSearchElement(composeboxElement).updateComplete;
     const [callback] = await windowProxy.whenCalled('setTimeout');
     callback();
     await microtasksFinished();
 
     assertEquals(
-        (composeboxElement.$.voiceSearch as any).finalResult_, '',
+        voiceSearchElement.finalResult_, '',
         'finalResult should be empty as no final result was received');
 
     assertEquals(
-        (composeboxElement.$.voiceSearch as any).interimResult_, '',
+        voiceSearchElement.interimResult_, '',
         'interimResult should be cleared after idle timeout');
 
     assertEquals(
-        (composeboxElement.$.voiceSearch as any).transcript_, '',
+        voiceSearchElement.transcript_, '',
         'transcript should be cleared after idle timeout');
 
     assertEquals(searchboxHandler.getCallCount('submitQuery'), 1);
     assertStyle(composeboxElement.$.composebox, 'display', 'flex');
-    assertStyle(composeboxElement.$.voiceSearch, 'display', 'none');
+    assertStyle(getVoiceSearchElement(composeboxElement), 'display', 'none');
     assertEquals(
         composeboxElement.animationState, GlowAnimationState.SUBMITTING);
   });
@@ -463,21 +476,17 @@
     await microtasksFinished();
     await hidePromise;
     await composeboxElement.updateComplete;
-    await composeboxElement.$.voiceSearch.updateComplete;
+    await getVoiceSearchElement(composeboxElement).updateComplete;
 
-    const voiceSearchElement = composeboxElement.$.voiceSearch;
-    const errorContainer =
-        voiceSearchElement.shadowRoot.querySelector<HTMLElement>(
-            '#error-container');
-    const inputElement =
-        voiceSearchElement.shadowRoot.querySelector<HTMLTextAreaElement>(
-            '#input');
+    const voiceSearchElement = getVoiceSearchElement(composeboxElement);
+    const errorContainer = $$(voiceSearchElement, '#error-container');
+    const inputElement = $$(voiceSearchElement, '#input');
 
     assertTrue(!!errorContainer);
     assertFalse(errorContainer.hidden);
     assertTrue(inputElement!.hidden);
     assertStyle(composeboxElement.$.composebox, 'opacity', '0');
-    assertStyle(composeboxElement.$.voiceSearch, 'display', 'inline');
+    assertStyle(getVoiceSearchElement(composeboxElement), 'display', 'inline');
     assertEquals(
         composeboxElement.animationState, GlowAnimationState.LISTENING);
   });
@@ -497,18 +506,14 @@
     await microtasksFinished();
     await showPromise;
     await composeboxElement.updateComplete;
-    await composeboxElement.$.voiceSearch.updateComplete;
+    await getVoiceSearchElement(composeboxElement).updateComplete;
 
     const [callback] = await windowProxy.whenCalled('setTimeout');
     callback();
 
-    const voiceSearchElement = composeboxElement.$.voiceSearch;
-    const errorContainer =
-        voiceSearchElement.shadowRoot.querySelector<HTMLElement>(
-            '#error-container');
-    const inputElement =
-        voiceSearchElement.shadowRoot.querySelector<HTMLTextAreaElement>(
-            '#input');
+    const voiceSearchElement = getVoiceSearchElement(composeboxElement);
+    const errorContainer = $$(voiceSearchElement, '#error-container');
+    const inputElement = $$(voiceSearchElement, '#input');
 
     // The error container should be hidden because it's not a NOT_ALLOWED
     // error, and the voice search should close.
@@ -516,7 +521,7 @@
     assertFalse(inputElement!.hidden);
 
     assertStyle(composeboxElement.$.composebox, 'display', 'flex');
-    assertStyle(composeboxElement.$.voiceSearch, 'display', 'none');
+    assertStyle(getVoiceSearchElement(composeboxElement), 'display', 'none');
     assertEquals(composeboxElement.animationState, GlowAnimationState.NONE);
   });
 
@@ -555,4 +560,26 @@
         searchAnimatedGlow!.shadowRoot.querySelector('audio-wave');
     assertTrue(!!audioWave);
   });
+
+  test(
+      'voice search container is empty without webkitSpeechRecognition API',
+      async () => {
+        // Temporarily remove API
+        windowProxy.setResultFor('hasWebkitSpeechRecognition', false);
+        await microtasksFinished();
+
+        composeboxElement = document.createElement('cr-composebox');
+        document.body.appendChild(composeboxElement);
+        await microtasksFinished();
+
+        // Query the DOM directly instead of using the `getVoiceSearchElement`
+        // helper, because the helper internally asserts that the element exists
+        // (assertTrue), which would cause this test to fail prematurely.
+        const voiceSearchElement = $$<ComposeboxVoiceSearchElement>(
+            composeboxElement, 'cr-composebox-voice-search');
+        assertFalse(!!voiceSearchElement);
+
+        // Restore API
+        windowProxy.setResultFor('hasWebkitSpeechRecognition', true);
+      });
 });
diff --git a/chrome/test/data/webui/cr_components/help_bubble/help_bubble_mixin_lit_test.ts b/chrome/test/data/webui/cr_components/help_bubble/help_bubble_mixin_lit_test.ts
index 7641456..35bced7 100644
--- a/chrome/test/data/webui/cr_components/help_bubble/help_bubble_mixin_lit_test.ts
+++ b/chrome/test/data/webui/cr_components/help_bubble/help_bubble_mixin_lit_test.ts
@@ -62,8 +62,8 @@
         <li>List item 2</li>
       </ul>
       <span style="display: block;">Span text</span>
-      <help-bubble-mixin-container id="container-element">
-      </help-bubble-mixin-container>
+      <help-bubble-mixin-test-container id="container-element">
+      </help-bubble-mixin-test-container>
     </div>`;
   }
 
@@ -90,7 +90,7 @@
 // HelpBubbleMixinTestContainerElement
 export class HelpBubbleMixinTestContainerElement extends CrLitElement {
   static get is() {
-    return 'help-bubble-mixin-container';
+    return 'help-bubble-mixin-test-container';
   }
 
   override render() {
diff --git a/chrome/test/data/webui/intro/dice_app_test.ts b/chrome/test/data/webui/intro/dice_app_test.ts
index fb738042..d86c817b 100644
--- a/chrome/test/data/webui/intro/dice_app_test.ts
+++ b/chrome/test/data/webui/intro/dice_app_test.ts
@@ -5,14 +5,14 @@
 import 'chrome://intro/dice_app.js';
 
 import {IntroBrowserProxyImpl} from 'chrome://intro/browser_proxy.js';
-import type {DiceAppElement} from 'chrome://intro/dice_app.js';
+import type {IntroAppElement} from 'chrome://intro/dice_app.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
 
 import {TestIntroBrowserProxy} from './test_intro_browser_proxy.js';
 
 suite('DiceAppTest', function() {
-  let testElement: DiceAppElement;
+  let testElement: IntroAppElement;
   let testBrowserProxy: TestIntroBrowserProxy;
 
   setup(function() {
diff --git a/chrome/test/data/webui/new_tab_page/action_chips/action_chips_test.ts b/chrome/test/data/webui/new_tab_page/action_chips/action_chips_test.ts
index 14d1694..0f044e76 100644
--- a/chrome/test/data/webui/new_tab_page/action_chips/action_chips_test.ts
+++ b/chrome/test/data/webui/new_tab_page/action_chips/action_chips_test.ts
@@ -11,8 +11,9 @@
 import {WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import type {TabUpload} from 'chrome://resources/cr_components/composebox/common.js';
 import {TabUploadOrigin} from 'chrome://resources/cr_components/composebox/common.js';
+import type {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
 import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import type {TestMock} from 'chrome://webui-test/test_mock.js';
@@ -426,7 +427,8 @@
           await initializeChips({actionChips});
           assertTrue(!!chips);
           const allChips = Array.from<HTMLButtonElement>(
-              chips.shadowRoot.querySelectorAll<HTMLButtonElement>('button'));
+              chips.shadowRoot.querySelectorAll<HTMLButtonElement>(
+                  '.action-chip'));
           assertEquals(3, allChips.length);
           assertTrue(allChips.every((e: HTMLButtonElement) => !!e));
 
@@ -470,7 +472,7 @@
       await initializeChips({});
       assertTrue(!!chips);
       const allChips = Array.from<HTMLButtonElement>(
-          chips.shadowRoot.querySelectorAll<HTMLButtonElement>('button'));
+          chips.shadowRoot.querySelectorAll<HTMLButtonElement>('.action-chip'));
       assertEquals(3, allChips.length);
 
       assertDeepEquals(
@@ -576,6 +578,84 @@
           chips.shadowRoot.querySelectorAll<HTMLButtonElement>('.action-chip');
       assertEquals(2, allChips.length);
     });
+
+    test(
+        'shows context menu on container when conditions are met', async () => {
+          loadTimeData.overrideValues({
+            ntpNextShowSimplificationUIEnabled: true,
+          });
+          await initializeChips({});
+          chips.showBackground = true;
+          await microtasksFinished();
+
+          const container = chips.shadowRoot.querySelector<HTMLElement>(
+              '.action-chips-container');
+          assertTrue(!!container);
+
+          const actionMenu =
+              chips.shadowRoot.querySelector<CrActionMenuElement>(
+                  '#actionMenu');
+          assertTrue(!!actionMenu);
+
+          container.dispatchEvent(
+              new MouseEvent('contextmenu', {cancelable: true}));
+          await microtasksFinished();
+
+          assertTrue(actionMenu.open);
+        });
+
+    test(
+        'does not show context menu on container when conditions are not met',
+        async () => {
+          loadTimeData.overrideValues({
+            ntpNextShowSimplificationUIEnabled: false,
+          });
+          await initializeChips({});
+          chips.showBackground = false;
+          await microtasksFinished();
+
+          const container = chips.shadowRoot.querySelector<HTMLElement>(
+              '.action-chips-container');
+          assertTrue(!!container);
+
+          const actionMenu =
+              chips.shadowRoot.querySelector<CrActionMenuElement>(
+                  '#actionMenu');
+          assertTrue(!!actionMenu);
+
+          container.dispatchEvent(
+              new MouseEvent('contextmenu', {cancelable: true}));
+          await microtasksFinished();
+
+          assertFalse(actionMenu.open);
+        });
+
+    test(
+        'disables action chips when context menu option is clicked',
+        async () => {
+          await initializeChips({});
+          const allChips = chips.shadowRoot.querySelectorAll<HTMLButtonElement>(
+              '.action-chip');
+          assertEquals(3, allChips.length);
+
+          const firstChip = allChips[0]!;
+          const actionMenu =
+              chips.shadowRoot.querySelector<HTMLElement>('#actionMenu');
+          assertTrue(!!actionMenu);
+
+          firstChip.dispatchEvent(
+              new MouseEvent('contextmenu', {cancelable: true}));
+          await microtasksFinished();
+
+          const disableButton =
+              actionMenu.querySelector<HTMLButtonElement>('.dropdown-item');
+          assertTrue(!!disableButton);
+          disableButton.click();
+          await microtasksFinished();
+
+          assertEquals(1, handler.getCallCount('setActionChipsVisibility'));
+          assertEquals(false, handler.getArgs('setActionChipsVisibility')[0]);
+        });
   });
 
   suite('ReducedMotion', () => {
diff --git a/chrome/test/data/webui/new_tab_page/composebox/composebox_autocomplete_test.ts b/chrome/test/data/webui/new_tab_page/composebox/composebox_autocomplete_test.ts
index 73d06b82..adfe46d 100644
--- a/chrome/test/data/webui/new_tab_page/composebox/composebox_autocomplete_test.ts
+++ b/chrome/test/data/webui/new_tab_page/composebox/composebox_autocomplete_test.ts
@@ -3,13 +3,13 @@
 // found in the LICENSE file.
 
 import {ComposeboxElement, VoiceSearchAction} from 'chrome://new-tab-page/lazy_load.js';
-import {$$} from 'chrome://new-tab-page/new_tab_page.js';
 import {InputType} from 'chrome://resources/cr_components/composebox/composebox_query.mojom-webui.js';
+import type {ComposeboxVoiceSearchElement} from 'chrome://resources/cr_components/composebox/composebox_voice_search.js';
 import {createAutocompleteResultForTesting, createSearchMatchForTesting} from 'chrome://resources/cr_components/searchbox/searchbox_browser_proxy.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import type {TabInfo} from 'chrome://resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
+import {$$, eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 import {assertStyle} from '../test_support.js';
 
@@ -1190,7 +1190,10 @@
     testProxy.searchboxHandler.reset();
 
     const voiceQuery = 'hello';
-    testProxy.element.$.voiceSearch.dispatchEvent(new CustomEvent(
+    const voiceSearchElement = $$<ComposeboxVoiceSearchElement>(
+        testProxy.element, 'cr-composebox-voice-search');
+    assertTrue(!!voiceSearchElement);
+    voiceSearchElement.dispatchEvent(new CustomEvent(
         'voice-search-final-result',
         {detail: voiceQuery, bubbles: true, composed: true}));
     await microtasksFinished();
@@ -1255,7 +1258,10 @@
         const voiceSearchActionPromise =
             eventToPromise('voice-search-action', testProxy.element);
         const voiceQuery = 'hello';
-        testProxy.element.$.voiceSearch.dispatchEvent(new CustomEvent(
+        const voiceSearchElement = $$<ComposeboxVoiceSearchElement>(
+            testProxy.element, 'cr-composebox-voice-search');
+        assertTrue(!!voiceSearchElement);
+        voiceSearchElement.dispatchEvent(new CustomEvent(
             'voice-search-final-result',
             {detail: voiceQuery, bubbles: true, composed: true}));
 
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index c2ad332..134db74 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-16612.0.0-1075871
\ No newline at end of file
+16613.0.0-1075889
\ No newline at end of file
diff --git a/chromeos/ash/experiences/settings_ui/DEPS b/chromeos/ash/experiences/settings_ui/DEPS
index 925893f..7c7bc6c 100644
--- a/chromeos/ash/experiences/settings_ui/DEPS
+++ b/chromeos/ash/experiences/settings_ui/DEPS
@@ -1,3 +1,3 @@
 include_rules = [
-  "+ash/webui/settings/public/constants/setting.mojom-shared.h",
+  "+ash/webui/settings/public/constants",
 ]
diff --git a/chromeos/ash/experiences/settings_ui/settings_app_manager.cc b/chromeos/ash/experiences/settings_ui/settings_app_manager.cc
index 0d20780f..bec1a3173 100644
--- a/chromeos/ash/experiences/settings_ui/settings_app_manager.cc
+++ b/chromeos/ash/experiences/settings_ui/settings_app_manager.cc
@@ -4,8 +4,10 @@
 
 #include "chromeos/ash/experiences/settings_ui/settings_app_manager.h"
 
+#include "ash/webui/settings/public/constants/routes.mojom.h"
 #include "base/check.h"
 #include "base/check_op.h"
+#include "base/strings/strcat.h"
 
 namespace ash {
 namespace {
@@ -13,6 +15,13 @@
 }  // namespace
 
 // static
+std::string SettingsAppManager::CreateAppManagementPagePath(
+    std::string_view app_id) {
+  return base::StrCat(
+      {chromeos::settings::mojom::kAppDetailsSubpagePath, "?id=", app_id});
+}
+
+// static
 SettingsAppManager* SettingsAppManager::Get() {
   return g_instance;
 }
diff --git a/chromeos/ash/experiences/settings_ui/settings_app_manager.h b/chromeos/ash/experiences/settings_ui/settings_app_manager.h
index e889b313..147c5f8 100644
--- a/chromeos/ash/experiences/settings_ui/settings_app_manager.h
+++ b/chromeos/ash/experiences/settings_ui/settings_app_manager.h
@@ -21,6 +21,35 @@
 // This (its implementation class) can be alive at most once at a time.
 class COMPONENT_EXPORT(SETTINGS_UI) SettingsAppManager {
  public:
+  // These are used in histograms, do not remove/renumber entries. If you're
+  // adding to this enum with the intention that it will be logged, update the
+  // EntryPoint enum listing in
+  // tools/metrics/histograms/metadata/apps/enums.xml.
+  enum class EntryPoint {
+    kAppListContextMenuAppInfoArc = 0,
+    kAppListContextMenuAppInfoChromeApp = 1,
+    kAppListContextMenuAppInfoWebApp = 2,
+    kShelfContextMenuAppInfoArc = 3,
+    kShelfContextMenuAppInfoChromeApp = 4,
+    kShelfContextMenuAppInfoWebApp = 5,
+    kAppManagementMainViewArc = 6,
+    kAppManagementMainViewChromeApp = 7,
+    kAppManagementMainViewWebApp = 8,
+    kOsSettingsMainPage = 9,
+    kAppManagementMainViewPluginVm = 10,
+    kDBusServicePluginVm = 11,
+    kNotificationPluginVm = 12,
+    kAppManagementMainViewBorealis = 13,
+    kPageInfoView = 14,
+    kPrivacyIndicatorsNotificationSettings = 15,
+    kSubAppsInstallPrompt = 16,
+    kSiteDataDialog = 17,
+    kMaxValue = kSiteDataDialog,
+  };
+
+  // Helper method to create the path.
+  static std::string CreateAppManagementPagePath(std::string_view app_id);
+
   // Returns the singleton instance
   static SettingsAppManager* Get();
 
@@ -31,10 +60,13 @@
     // If specified, opens the subpage, instead of settings top page.
     std::string_view sub_page;
     std::optional<chromeos::settings::mojom::Setting> setting_id;
+    // If specified, used to record AppManagement.EntryPoints histogram.
+    std::optional<EntryPoint> entry_point;
 
     // ID of the display to be used, if given.
     int64_t display_id = display::kInvalidDisplayId;
   };
+
   // Opens the settings app of the given `user`.
   virtual void Open(const user_manager::User& user, OpenParams params) = 0;
 
diff --git a/chromeos/components/quick_answers/understanding/intent_generator.cc b/chromeos/components/quick_answers/understanding/intent_generator.cc
index a41ed84..35ab2ad 100644
--- a/chromeos/components/quick_answers/understanding/intent_generator.cc
+++ b/chromeos/components/quick_answers/understanding/intent_generator.cc
@@ -322,8 +322,7 @@
     const QuickAnswersRequest& request) {
   DCHECK(complete_callback_);
 
-  if (!QuickAnswersState::IsIntentEligible(Intent::kTranslation) ||
-      chromeos::features::IsQuickAnswersV2TranslationDisabled()) {
+  if (!QuickAnswersState::IsIntentEligible(Intent::kTranslation)) {
     std::move(complete_callback_)
         .Run(IntentInfo(request.selected_text, IntentType::kUnknown));
     return;
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index a164691..3d7245c 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -50,10 +50,6 @@
 // Enables data migration.
 BASE_FEATURE(kDataMigration, base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Disables translation services of the Quick Answers V2.
-BASE_FEATURE(kDisableQuickAnswersV2Translation,
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 // Enables Essential Search in Omnibox for both launcher and browser.
 BASE_FEATURE(kEssentialSearch, base::FEATURE_ENABLED_BY_DEFAULT);
 
@@ -406,10 +402,6 @@
   return base::FeatureList::IsEnabled(kQuickAnswersMaterialNextUI);
 }
 
-bool IsQuickAnswersV2TranslationDisabled() {
-  return base::FeatureList::IsEnabled(kDisableQuickAnswersV2Translation);
-}
-
 bool IsQuickAnswersRichCardEnabled() {
   return base::FeatureList::IsEnabled(kQuickAnswersRichCard);
 }
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index 78cf0c9..888be09 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -33,9 +33,6 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 BASE_DECLARE_FEATURE(kDataControlsFileAccessDefaultDeny);
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kDataMigration);
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
-BASE_DECLARE_FEATURE(kDisableQuickAnswersV2Translation);
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kEssentialSearch);
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 BASE_DECLARE_FEATURE(kExternalDisplayEventTelemetry);
@@ -171,7 +168,6 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsOrcaEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsOrcaUseL10nStringsEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsOrcaInternationalizeEnabled();
-COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsQuickAnswersV2TranslationDisabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 bool IsQuickAnswersV2SettingsSubToggleEnabled();
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsQuickAnswersMaterialNextUIEnabled();
diff --git a/components/bookmarks/browser/bookmark_model_unittest.cc b/components/bookmarks/browser/bookmark_model_unittest.cc
index f1dceafe..340560d 100644
--- a/components/bookmarks/browser/bookmark_model_unittest.cc
+++ b/components/bookmarks/browser/bookmark_model_unittest.cc
@@ -9,7 +9,6 @@
 
 #include <algorithm>
 #include <array>
-#include <optional>
 #include <set>
 #include <string>
 #include <string_view>
@@ -303,75 +302,21 @@
 
 class BookmarkModelTest : public testing::Test, public BookmarkModelObserver {
  public:
-  struct ObserverDetails {
-    ObserverDetails() {
-      Set(nullptr, nullptr, static_cast<size_t>(-1), static_cast<size_t>(-1),
-          false, FROM_HERE);
-    }
-
-    void Set(const BookmarkNode* node1,
-             const BookmarkNode* node2,
-             size_t index1,
-             size_t index2,
-             bool added_by_user,
-             const base::Location& location) {
-      node1_ = node1;
-      node2_ = node2;
-      index1_ = index1;
-      index2_ = index2;
-      added_by_user_ = added_by_user;
-      location_ = location;
-    }
-
-    void Reset() {
-      Set(nullptr, nullptr, static_cast<size_t>(-1), static_cast<size_t>(-1),
-          false, FROM_HERE);
-    }
-
-    void ExpectEquals(const BookmarkNode* node1,
-                      const BookmarkNode* node2,
-                      size_t index1,
-                      size_t index2,
-                      bool added_by_user,
-                      std::optional<base::Location> location = std::nullopt) {
-      EXPECT_EQ(node1_, node1);
-      EXPECT_EQ(node2_, node2);
-      EXPECT_EQ(index1_, index1);
-      EXPECT_EQ(index2_, index2);
-      EXPECT_EQ(added_by_user_, added_by_user);
-      if (location.has_value()) {
-        EXPECT_EQ(location_, *location);
-      }
-    }
-
-   private:
-    raw_ptr<const BookmarkNode> node1_;
-    raw_ptr<const BookmarkNode> node2_;
-    size_t index1_;
-    size_t index2_;
-    bool added_by_user_;
-    base::Location location_;
-  };
-
-  struct AllNodesRemovedDetail {
-    explicit AllNodesRemovedDetail(const std::set<GURL>& removed_urls)
-        : removed_urls(removed_urls) {}
-
-    bool operator==(const AllNodesRemovedDetail& other) const {
-      return removed_urls == other.removed_urls;
-    }
-
-    std::set<GURL> removed_urls;
-  };
-
   BookmarkModelTest()
       : model_(TestBookmarkClient::CreateModelWithClient(
             std::make_unique<TestBookmarkClientWithUndo>())) {
+    // TODO(crbug.com/380282512): Remove this legacy observer/counting
+    // mechanism once the remaining tests are migrated to
+    // MockBookmarkModelObserver.
     model_->AddObserver(this);
+    model_->AddObserver(&mock_observer_);
     ClearCounts();
   }
 
-  ~BookmarkModelTest() override { model_->RemoveObserver(this); }
+  ~BookmarkModelTest() override {
+    model_->RemoveObserver(&mock_observer_);
+    model_->RemoveObserver(this);
+  }
 
   BookmarkModelTest(const BookmarkModelTest&) = delete;
   BookmarkModelTest& operator=(const BookmarkModelTest&) = delete;
@@ -386,8 +331,6 @@
                               const BookmarkNode* new_parent,
                               size_t new_index) override {
     ++before_moved_count_;
-    before_observer_details_.Set(old_parent, new_parent, old_index, new_index,
-                                 false, FROM_HERE);
   }
 
   void BookmarkNodeMoved(const BookmarkNode* old_parent,
@@ -395,16 +338,12 @@
                          const BookmarkNode* new_parent,
                          size_t new_index) override {
     ++moved_count_;
-    observer_details_.Set(old_parent, new_parent, old_index, new_index, false,
-                          FROM_HERE);
   }
 
   void BookmarkNodeAdded(const BookmarkNode* parent,
                          size_t index,
                          bool added_by_user) override {
     ++added_count_;
-    observer_details_.Set(parent, nullptr, index, static_cast<size_t>(-1),
-                          added_by_user, FROM_HERE);
   }
 
   void OnWillRemoveBookmarks(const BookmarkNode* parent,
@@ -412,8 +351,6 @@
                              const BookmarkNode* node,
                              const base::Location& location) override {
     ++before_remove_count_;
-    before_observer_details_.Set(parent, nullptr, old_index,
-                                 static_cast<size_t>(-1), false, location);
   }
 
   void BookmarkNodeRemoved(const BookmarkNode* parent,
@@ -422,20 +359,14 @@
                            const std::set<GURL>& removed_urls,
                            const base::Location& location) override {
     ++removed_count_;
-    observer_details_.Set(parent, nullptr, old_index, static_cast<size_t>(-1),
-                          false, location);
   }
 
   void BookmarkNodeChanged(const BookmarkNode* node) override {
     ++changed_count_;
-    observer_details_.Set(node, nullptr, static_cast<size_t>(-1),
-                          static_cast<size_t>(-1), false, FROM_HERE);
   }
 
   void OnWillChangeBookmarkNode(const BookmarkNode* node) override {
     ++before_change_count_;
-    before_observer_details_.Set(node, nullptr, static_cast<size_t>(-1),
-                                 static_cast<size_t>(-1), false, FROM_HERE);
   }
 
   void BookmarkNodeChildrenReordered(const BookmarkNode* node) override {
@@ -462,13 +393,10 @@
   void BookmarkAllUserNodesRemoved(const std::set<GURL>& removed_urls,
                                    const base::Location& location) override {
     ++all_bookmarks_removed_;
-    all_bookmarks_removed_details_.emplace_back(removed_urls);
   }
 
   void OnWillRemoveAllUserBookmarks(const base::Location& location) override {
     ++before_remove_all_count_;
-    observer_details_.Reset();
-    before_observer_details_.Reset();
   }
 
   void GroupedBookmarkChangesBeginning() override {
@@ -534,9 +462,8 @@
     EXPECT_EQ(grouped_changes_ended_count, grouped_changes_ended_count_);
   }
 
-  int AllNodesRemovedObserverCount() const { return all_bookmarks_removed_; }
-
   BookmarkPermanentNode* ReloadModelWithManagedNode() {
+    model_->RemoveObserver(&mock_observer_);
     model_->RemoveObserver(this);
 
     auto client = std::make_unique<TestBookmarkClient>();
@@ -544,6 +471,7 @@
 
     model_ = TestBookmarkClient::CreateModelWithClient(std::move(client));
     model_->AddObserver(this);
+    model_->AddObserver(&mock_observer_);
     ClearCounts();
 
     if (!model_->root_node()->GetIndexOf(managed_node).has_value()) {
@@ -586,16 +514,15 @@
   }
 
   base::HistogramTester* histogram_tester() { return &histogram_tester_; }
+  MockBookmarkModelObserver& mock_observer() { return mock_observer_; }
   base::UserActionTester* user_action_tester() { return &user_action_tester_; }
 
  protected:
   base::test::ScopedFeatureList features_{
       switches::kSyncEnableBookmarksInTransportMode};
   std::unique_ptr<BookmarkModel> model_;
-  ObserverDetails observer_details_;
-  ObserverDetails before_observer_details_;
-  std::vector<AllNodesRemovedDetail> all_bookmarks_removed_details_;
   base::HistogramTester histogram_tester_;
+  testing::NiceMock<MockBookmarkModelObserver> mock_observer_;
   base::UserActionTester user_action_tester_;
 
  private:
@@ -642,11 +569,10 @@
   const std::u16string kTitle(u"foo");
   const GURL kUrl("http://foo.com");
 
+  EXPECT_CALL(mock_observer(), BookmarkNodeAdded(bookmark_bar_node, 0, false));
+
   const BookmarkNode* new_node =
       model_->AddURL(bookmark_bar_node, 0, kTitle, kUrl);
-  AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
-                                 static_cast<size_t>(-1), false);
 
   ASSERT_EQ(1u, bookmark_bar_node->children().size());
   ASSERT_EQ(kTitle, new_node->GetTitle());
@@ -666,11 +592,10 @@
   const std::u16string kTitle(u"foo");
   const GURL kUrl("http://foo.com");
 
+  EXPECT_CALL(mock_observer(), BookmarkNodeAdded(bookmark_bar_node, 0, true));
+
   const BookmarkNode* new_node =
       model_->AddNewURL(bookmark_bar_node, 0, kTitle, kUrl);
-  AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
-                                 static_cast<size_t>(-1), true);
 
   ASSERT_EQ(1u, bookmark_bar_node->children().size());
   ASSERT_EQ(kTitle, new_node->GetTitle());
@@ -756,11 +681,10 @@
       u"\u767e\u5ea6\u4e00\u4e0b\uff0c\u4f60\u5c31\u77e5\u9053");
   const GURL kUrl("https://www.baidu.com/");
 
+  EXPECT_CALL(mock_observer(), BookmarkNodeAdded(bookmark_bar_node, 0, false));
+
   const BookmarkNode* new_node =
       model_->AddURL(bookmark_bar_node, 0, kTitle, kUrl);
-  AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
-                                 static_cast<size_t>(-1), false);
 
   ASSERT_EQ(1u, bookmark_bar_node->children().size());
   ASSERT_EQ(kTitle, new_node->GetTitle());
@@ -799,11 +723,10 @@
   BookmarkNode::MetaInfoMap meta_info;
   meta_info["foo"] = "bar";
 
+  EXPECT_CALL(mock_observer(), BookmarkNodeAdded(bookmark_bar_node, 0, false));
+
   const BookmarkNode* new_node =
       model_->AddURL(bookmark_bar_node, 0, kTitle, kUrl, &meta_info, kTime);
-  AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
-                                 static_cast<size_t>(-1), false);
 
   ASSERT_EQ(1u, bookmark_bar_node->children().size());
   ASSERT_EQ(kTitle, new_node->GetTitle());
@@ -836,14 +759,13 @@
 }
 
 TEST_F(BookmarkModelTest, AddURLToMobileBookmarks) {
-  const BookmarkNode* mobile_node = model_->mobile_node();
+  const BookmarkPermanentNode* mobile_node = model_->mobile_node();
   const std::u16string kTitle(u"foo");
   const GURL kUrl("http://foo.com");
 
+  EXPECT_CALL(mock_observer(), BookmarkNodeAdded(mobile_node, 0, false));
+
   const BookmarkNode* new_node = model_->AddURL(mobile_node, 0, kTitle, kUrl);
-  AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(mobile_node, nullptr, 0,
-                                 static_cast<size_t>(-1), false);
 
   ASSERT_EQ(1u, mobile_node->children().size());
   ASSERT_EQ(kTitle, new_node->GetTitle());
@@ -861,11 +783,10 @@
   const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   const std::u16string kTitle(u"foo");
 
+  EXPECT_CALL(mock_observer(), BookmarkNodeAdded(bookmark_bar_node, 0, false));
+
   const BookmarkNode* new_node =
       model_->AddFolder(bookmark_bar_node, 0, kTitle);
-  AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
-                                 static_cast<size_t>(-1), false);
 
   ASSERT_EQ(1u, bookmark_bar_node->children().size());
   ASSERT_EQ(kTitle, new_node->GetTitle());
@@ -877,12 +798,11 @@
                              testing::Ne(model_->other_node()->id()),
                              testing::Ne(model_->mobile_node()->id())));
 
+  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_observer()));
+
   // Add another folder, just to make sure folder_ids are incremented correctly.
-  ClearCounts();
+  EXPECT_CALL(mock_observer(), BookmarkNodeAdded(bookmark_bar_node, 0, false));
   model_->AddFolder(bookmark_bar_node, 0, kTitle);
-  AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
-                                 static_cast<size_t>(-1), false);
 }
 
 TEST_F(BookmarkModelTest, AddFolderWithCreationTime) {
@@ -931,19 +851,21 @@
   const GURL kUrl("http://foo.com");
   const base::Location kLocation = FROM_HERE;
 
-  model_->AddURL(bookmark_bar_node, 0, kTitle, kUrl);
-  ClearCounts();
+  const BookmarkNode* node = model_->AddURL(bookmark_bar_node, 0, kTitle, kUrl);
 
-  model_->Remove(bookmark_bar_node->children().front().get(),
-                 bookmarks::metrics::BookmarkEditSource::kOther, kLocation);
+  testing::InSequence seq;
+  EXPECT_CALL(mock_observer(),
+              OnWillRemoveBookmarks(bookmark_bar_node, 0, node, kLocation));
+  EXPECT_CALL(mock_observer(),
+              BookmarkNodeRemoved(bookmark_bar_node, 0, node, _, kLocation));
+
+  model_->Remove(node, bookmarks::metrics::BookmarkEditSource::kOther,
+                 kLocation);
   ASSERT_EQ(0u, bookmark_bar_node->children().size());
   histogram_tester()->ExpectTotalCount("Bookmarks.RemovedSource", 1);
   histogram_tester()->ExpectBucketCount(
       "Bookmarks.RemovedSource",
       static_cast<int>(metrics::BookmarkEditSource::kOther), 1);
-  AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
-                                 static_cast<size_t>(-1), false, kLocation);
 
   // Make sure there is no mapping for the URL.
   ASSERT_EQ(model_->GetMostRecentlyAddedUserNodeForURL(kUrl), nullptr);
@@ -953,26 +875,27 @@
   const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   const BookmarkNode* folder = model_->AddFolder(bookmark_bar_node, 0, u"foo");
 
-  ClearCounts();
-
   // Add a URL as a child.
   const std::u16string kTitle(u"foo");
   const GURL kUrl("http://foo.com");
   model_->AddURL(folder, 0, kTitle, kUrl);
 
-  ClearCounts();
+  const base::Location kLocation = FROM_HERE;
+
+  testing::InSequence seq;
+  EXPECT_CALL(mock_observer(),
+              OnWillRemoveBookmarks(bookmark_bar_node, 0, folder, kLocation));
+  EXPECT_CALL(mock_observer(),
+              BookmarkNodeRemoved(bookmark_bar_node, 0, folder, _, kLocation));
 
   // Now remove the folder.
-  model_->Remove(bookmark_bar_node->children().front().get(),
-                 bookmarks::metrics::BookmarkEditSource::kOther, FROM_HERE);
+  model_->Remove(folder, bookmarks::metrics::BookmarkEditSource::kOther,
+                 kLocation);
   ASSERT_EQ(0u, bookmark_bar_node->children().size());
   histogram_tester()->ExpectTotalCount("Bookmarks.RemovedSource", 1);
   histogram_tester()->ExpectBucketCount(
       "Bookmarks.RemovedSource",
       static_cast<int>(metrics::BookmarkEditSource::kOther), 1);
-  AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
-                                 static_cast<size_t>(-1), false);
 
   // Make sure there is no mapping for the URL.
   ASSERT_EQ(model_->GetMostRecentlyAddedUserNodeForURL(kUrl), nullptr);
@@ -987,8 +910,15 @@
   const base::Location kLocation = FROM_HERE;
 
   model_->AddURL(bookmark_bar_node, 0, kTitle1, kUrl1);
-  model_->AddURL(bookmark_bar_node, 1, kTitle2, kUrl2);
-  ClearCounts();
+
+  const BookmarkNode* node2 =
+      model_->AddURL(bookmark_bar_node, 1, kTitle2, kUrl2);
+
+  testing::InSequence seq;
+  EXPECT_CALL(mock_observer(),
+              OnWillRemoveBookmarks(bookmark_bar_node, 1, node2, kLocation));
+  EXPECT_CALL(mock_observer(),
+              BookmarkNodeRemoved(bookmark_bar_node, 1, node2, _, kLocation));
 
   model_->RemoveLastChild(bookmark_bar_node,
                           bookmarks::metrics::BookmarkEditSource::kOther,
@@ -998,9 +928,6 @@
   histogram_tester()->ExpectBucketCount(
       "Bookmarks.RemovedSource",
       static_cast<int>(metrics::BookmarkEditSource::kOther), 1);
-  AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 1,
-                                 static_cast<size_t>(-1), false, kLocation);
 
   ASSERT_NE(nullptr, model_->GetMostRecentlyAddedUserNodeForURL(kUrl1));
   // Make sure there is no mapping for the removed URL.
@@ -1010,8 +937,6 @@
 TEST_F(BookmarkModelTest, RemoveAllUserBookmarks) {
   const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
 
-  ClearCounts();
-
   // Add a url to bookmark bar.
   const std::u16string kTitle(u"foo");
   const GURL kUrl("http://foo.com");
@@ -1021,25 +946,27 @@
   const BookmarkNode* folder = model_->AddFolder(bookmark_bar_node, 0, kTitle);
   model_->AddURL(folder, 0, kTitle, kUrl);
 
-  AssertObserverCount(3, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-  ClearCounts();
+  const base::Location kLocation = FROM_HERE;
 
   size_t permanent_node_count = model_->root_node()->children().size();
 
-  model_->RemoveAllUserBookmarks(FROM_HERE);
+  EXPECT_CALL(mock_observer(), BookmarkNodeRemoved).Times(0);
+
+  testing::InSequence seq;
+  EXPECT_CALL(mock_observer(), OnWillRemoveAllUserBookmarks(kLocation));
+  EXPECT_CALL(mock_observer(), ExtensiveBookmarkChangesBeginning());
+  EXPECT_CALL(mock_observer(), ExtensiveBookmarkChangesEnded());
+  EXPECT_CALL(mock_observer(),
+              BookmarkAllUserNodesRemoved(
+                  testing::ContainerEq(std::set<GURL>{kUrl}), kLocation));
+  EXPECT_CALL(mock_observer(), GroupedBookmarkChangesBeginning());
+  EXPECT_CALL(mock_observer(), GroupedBookmarkChangesEnded());
+
+  model_->RemoveAllUserBookmarks(kLocation);
 
   EXPECT_EQ(0u, bookmark_bar_node->children().size());
   // No permanent node should be removed.
   EXPECT_EQ(permanent_node_count, model_->root_node()->children().size());
-  // No individual BookmarkNodeRemoved events are fired, so removed count
-  // should be 0.
-  AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
-  AssertExtensiveChangesObserverCount(1, 1);
-  AssertGroupedChangesObserverCount(1, 1);
-  EXPECT_EQ(1, AllNodesRemovedObserverCount());
-  EXPECT_EQ(1, AllNodesRemovedObserverCount());
-  EXPECT_THAT(all_bookmarks_removed_details_,
-              ElementsAre(AllNodesRemovedDetail(/*removed_urls=*/{kUrl})));
 }
 
 TEST_F(BookmarkModelTest, UpdateLastUsedTimeInRange) {
@@ -1149,13 +1076,12 @@
   const BookmarkNode* node =
       model_->AddURL(bookmark_bar_node, 0, kOriginalTitle, kUrl);
 
-  ClearCounts();
+  testing::InSequence seq;
+  EXPECT_CALL(mock_observer(), OnWillChangeBookmarkNode(node));
+  EXPECT_CALL(mock_observer(), BookmarkNodeChanged(node));
 
   const std::u16string kNewTitle(u"goo");
   model_->SetTitle(node, kNewTitle, metrics::BookmarkEditSource::kOther);
-  AssertObserverCount(0, 0, 0, 1, 0, 0, 1, 0, 0, 0);
-  observer_details_.ExpectEquals(node, nullptr, static_cast<size_t>(-1),
-                                 static_cast<size_t>(-1), false);
   EXPECT_EQ(kNewTitle, node->GetTitle());
 
   // Should update the index.
@@ -1192,7 +1118,9 @@
   const GURL kUrl("http://foo.com");
   const BookmarkNode* node = model_->AddURL(folder, 0, kTitle, kUrl);
 
-  ClearCounts();
+  testing::InSequence seq;
+  EXPECT_CALL(mock_observer(), OnWillChangeBookmarkNode(folder));
+  EXPECT_CALL(mock_observer(), BookmarkNodeChanged(folder));
 
   model_->SetTitle(folder, u"golder", metrics::BookmarkEditSource::kOther);
 
@@ -1223,13 +1151,12 @@
   const BookmarkNode* node =
       model_->AddURL(bookmark_bar_node, 0, kTitle, kOriginalUrl);
 
-  ClearCounts();
+  testing::InSequence seq;
+  EXPECT_CALL(mock_observer(), OnWillChangeBookmarkNode(node));
+  EXPECT_CALL(mock_observer(), BookmarkNodeChanged(node));
 
   const GURL kNewUrl("http://foo2.com");
   model_->SetURL(node, kNewUrl, metrics::BookmarkEditSource::kOther);
-  AssertObserverCount(0, 0, 0, 1, 0, 0, 1, 0, 0, 0);
-  observer_details_.ExpectEquals(node, nullptr, static_cast<size_t>(-1),
-                                 static_cast<size_t>(-1), false);
   EXPECT_EQ(kNewUrl, node->url());
   histogram_tester()->ExpectBucketCount("Bookmarks.EditURLSource",
                                         metrics::BookmarkEditSource::kOther, 1);
@@ -1258,15 +1185,16 @@
   const BookmarkNode* folder1 =
       model_->AddFolder(bookmark_bar_node, 0, u"folder");
 
-  ClearCounts();
+  {
+    testing::InSequence seq;
+    EXPECT_CALL(mock_observer(),
+                OnWillMoveBookmarkNode(bookmark_bar_node, 1, folder1, 0));
+    EXPECT_CALL(mock_observer(),
+                BookmarkNodeMoved(bookmark_bar_node, 1, folder1, 0));
+  }
 
   model_->Move(node, folder1, 0);
 
-  AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0,
-                      /*before_moved_count=*/1);
-  before_observer_details_.ExpectEquals(bookmark_bar_node, folder1, 1, 0,
-                                        false);
-  observer_details_.ExpectEquals(bookmark_bar_node, folder1, 1, 0, false);
   EXPECT_EQ(folder1, node->parent());
   EXPECT_EQ(1u, bookmark_bar_node->children().size());
   EXPECT_EQ(folder1, bookmark_bar_node->children().front().get());
@@ -1277,13 +1205,19 @@
       u"folder foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT);
   EXPECT_EQ(matches[0].node, node);
 
+  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_observer()));
+
   // And remove the folder.
-  ClearCounts();
-  model_->Remove(bookmark_bar_node->children().front().get(),
-                 bookmarks::metrics::BookmarkEditSource::kOther, FROM_HERE);
-  AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
-                                 static_cast<size_t>(-1), false);
+  const base::Location kLocation = FROM_HERE;
+  {
+    testing::InSequence seq;
+    EXPECT_CALL(mock_observer(), OnWillRemoveBookmarks(bookmark_bar_node, 0,
+                                                       folder1, kLocation));
+    EXPECT_CALL(mock_observer(), BookmarkNodeRemoved(bookmark_bar_node, 0,
+                                                     folder1, _, kLocation));
+  }
+  model_->Remove(folder1, bookmarks::metrics::BookmarkEditSource::kOther,
+                 kLocation);
   EXPECT_EQ(model_->GetMostRecentlyAddedUserNodeForURL(kUrl), nullptr);
   EXPECT_EQ(0u, bookmark_bar_node->children().size());
 
@@ -1305,37 +1239,44 @@
   PopulateBookmarkNode(&abc, model_.get(), parent);
 
   // Move to current_index - 1 moves to the left: [a, b, c] -> [b, a, c]
-  ClearCounts();
-  model_->Move(parent->children()[1].get(), parent, 0);
+  {
+    testing::InSequence seq;
+    EXPECT_CALL(mock_observer(), OnWillMoveBookmarkNode(parent, 1, parent, 0));
+    EXPECT_CALL(mock_observer(), BookmarkNodeMoved(parent, 1, parent, 0));
+
+    model_->Move(parent->children()[1].get(), parent, 0);
+  }
   VerifyModelMatchesNode(&bac, parent);
-  AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0,
-                      /*before_moved_count=*/1);
-  before_observer_details_.ExpectEquals(parent, parent, 1, 0, false);
-  observer_details_.ExpectEquals(parent, parent, 1, 0, false);
+  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_observer()));
 
   // Move the current_index is a no-op: [b, a, c] -> [b, a, c]
-  ClearCounts();
-  model_->Move(parent->children()[1].get(), parent, 1);
+  {
+    EXPECT_CALL(mock_observer(), OnWillMoveBookmarkNode).Times(0);
+    EXPECT_CALL(mock_observer(), BookmarkNodeMoved).Times(0);
+    model_->Move(parent->children()[1].get(), parent, 1);
+  }
   VerifyModelMatchesNode(&bac, parent);
-  AssertObserverCount(0, /*moved_count=*/0, 0, 0, 0, 0, 0, 0, 0,
-                      /*before_moved_count=*/0);
+  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_observer()));
 
   // Move to current_index + 1 is a no-op: [b, a, c] -> [b, a, c]
-  ClearCounts();
-  model_->Move(parent->children()[1].get(), parent, 2);
+  {
+    EXPECT_CALL(mock_observer(), OnWillMoveBookmarkNode).Times(0);
+    EXPECT_CALL(mock_observer(), BookmarkNodeMoved).Times(0);
+    model_->Move(parent->children()[1].get(), parent, 2);
+  }
   VerifyModelMatchesNode(&bac, parent);
-  AssertObserverCount(0, /*moved_count=*/0, 0, 0, 0, 0, 0, 0, 0,
-                      /*before_moved_count=*/0);
+  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&mock_observer()));
 
   // Move to current_index + 2 moves 1 to the right: [b, a, c] -> [b, c, a]
   // Note: this tests moving to an index that is one past the end of the list.
-  ClearCounts();
-  model_->Move(parent->children()[1].get(), parent, 3);
+  {
+    testing::InSequence seq;
+    EXPECT_CALL(mock_observer(), OnWillMoveBookmarkNode(parent, 1, parent, 2));
+    EXPECT_CALL(mock_observer(), BookmarkNodeMoved(parent, 1, parent, 2));
+
+    model_->Move(parent->children()[1].get(), parent, 3);
+  }
   VerifyModelMatchesNode(&bca, parent);
-  AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0,
-                      /*before_moved_count=*/1);
-  before_observer_details_.ExpectEquals(parent, parent, 1, 2, false);
-  observer_details_.ExpectEquals(parent, parent, 1, 2, false);
 }
 
 TEST_F(BookmarkModelTest, NonMovingMoveCall) {
@@ -1365,15 +1306,16 @@
   const GURL kUrl("http://foo.com");
   const BookmarkNode* node = model_->AddURL(folder1, 0, kTitle, kUrl);
 
-  ClearCounts();
+  {
+    testing::InSequence seq;
+    EXPECT_CALL(mock_observer(),
+                OnWillMoveBookmarkNode(folder1, 0, folder2, 0));
+    EXPECT_CALL(mock_observer(), BookmarkNodeMoved(folder1, 0, folder2, 0));
+  }
 
   model_->Move(node, folder2, 0);
 
   // Should update the hierarchy.
-  AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0,
-                      /*before_moved_count=*/1);
-  before_observer_details_.ExpectEquals(folder1, folder2, 0, 0, false);
-  observer_details_.ExpectEquals(folder1, folder2, 0, 0, false);
   EXPECT_EQ(bookmark_bar_node->children().size(), 2u);
   EXPECT_EQ(folder1->children().size(), 0u);
   EXPECT_EQ(folder2->children().size(), 1u);
@@ -1388,14 +1330,15 @@
   matches.clear();
 
   // Move back.
-  ClearCounts();
+  {
+    testing::InSequence seq;
+    EXPECT_CALL(mock_observer(),
+                OnWillMoveBookmarkNode(folder2, 0, folder1, 0));
+    EXPECT_CALL(mock_observer(), BookmarkNodeMoved(folder2, 0, folder1, 0));
+  }
   model_->Move(node, folder1, 0);
 
   // Should update the hierarchy.
-  AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0,
-                      /*before_moved_count=*/1);
-  before_observer_details_.ExpectEquals(folder2, folder1, 0, 0, false);
-  observer_details_.ExpectEquals(folder2, folder1, 0, 0, false);
   EXPECT_EQ(bookmark_bar_node->children().size(), 2u);
   EXPECT_EQ(folder1->children().size(), 1u);
   EXPECT_EQ(folder2->children().size(), 0u);
@@ -1421,15 +1364,13 @@
   const GURL kUrl("http://foo.com");
   const BookmarkNode* node = model_->AddURL(folder3, 0, kTitle, kUrl);
 
-  ClearCounts();
+  testing::InSequence seq;
+  EXPECT_CALL(mock_observer(), OnWillMoveBookmarkNode(folder1, 0, folder2, 0));
+  EXPECT_CALL(mock_observer(), BookmarkNodeMoved(folder1, 0, folder2, 0));
 
   model_->Move(folder3, folder2, 0);
 
   // Should update the hierarchy.
-  AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0,
-                      /*before_moved_count=*/1);
-  before_observer_details_.ExpectEquals(folder1, folder2, 0, 0, false);
-  observer_details_.ExpectEquals(folder1, folder2, 0, 0, false);
   EXPECT_EQ(bookmark_bar_node->children().size(), 2u);
   EXPECT_EQ(bookmark_bar_node->children()[0].get(), folder1);
   EXPECT_EQ(bookmark_bar_node->children()[1].get(), folder2);
@@ -1471,17 +1412,16 @@
                         /*meta_info=*/nullptr,
                         /*creation_time=*/std::nullopt, kInitialUuid);
 
-  ClearCounts();
+  testing::InSequence seq;
+  EXPECT_CALL(mock_observer(),
+              OnWillMoveBookmarkNode(bookmark_bar_node, 0,
+                                     account_bookmark_bar_node, 0));
+  EXPECT_CALL(mock_observer(), BookmarkNodeMoved(bookmark_bar_node, 0,
+                                                 account_bookmark_bar_node, 0));
 
   model_->Move(folder1, account_bookmark_bar_node, 0);
 
   // Should update the hierarchy.
-  AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0,
-                      /*before_moved_count=*/1);
-  before_observer_details_.ExpectEquals(bookmark_bar_node,
-                                        account_bookmark_bar_node, 0, 0, false);
-  observer_details_.ExpectEquals(bookmark_bar_node, account_bookmark_bar_node,
-                                 0, 0, false);
   EXPECT_EQ(bookmark_bar_node->children().size(), 0u);
   EXPECT_EQ(account_bookmark_bar_node->children().size(), 2u);
 
@@ -1525,19 +1465,18 @@
                         /*meta_info=*/nullptr,
                         /*creation_time=*/std::nullopt, kInitialUuid);
 
-  ClearCounts();
+  testing::InSequence seq;
+  EXPECT_CALL(mock_observer(),
+              OnWillMoveBookmarkNode(bookmark_bar_node, 0,
+                                     account_bookmark_bar_node, 0));
+  EXPECT_CALL(mock_observer(), BookmarkNodeMoved(bookmark_bar_node, 0,
+                                                 account_bookmark_bar_node, 0));
 
   // Move `folder1`, which also includes the nested `folder2`, to account
   // storage.
   model_->Move(folder1, account_bookmark_bar_node, 0);
 
   // Should update the hierarchy.
-  AssertObserverCount(0, /*moved_count=*/1, 0, 0, 0, 0, 0, 0, 0,
-                      /*before_moved_count=*/1);
-  before_observer_details_.ExpectEquals(bookmark_bar_node,
-                                        account_bookmark_bar_node, 0, 0, false);
-  observer_details_.ExpectEquals(bookmark_bar_node, account_bookmark_bar_node,
-                                 0, 0, false);
   EXPECT_EQ(bookmark_bar_node->children().size(), 0u);
   EXPECT_EQ(account_bookmark_bar_node->children().size(), 2u);
 
@@ -1913,7 +1852,7 @@
 
   // Expect first a callback that the folder is visible, then that the child
   // bookmark is added.
-  testing::InSequence enforce_callback_order;
+  testing::InSequence seq;
   EXPECT_CALL(observer,
               BookmarkPermanentNodeVisibilityChanged(permanent_folder))
       .WillOnce([](const BookmarkPermanentNode* node) {
@@ -1946,7 +1885,7 @@
 
   // Expect first a callback that the folder is visible, then that the child
   // bookmark is added.
-  testing::InSequence enforce_callback_order;
+  testing::InSequence seq;
   EXPECT_CALL(observer,
               BookmarkPermanentNodeVisibilityChanged(model_->other_node()))
       .WillOnce([this](const BookmarkPermanentNode* node) {
@@ -1997,7 +1936,7 @@
 
   // Expect first a callback that the folder is visible, then that the child
   // bookmark is added.
-  testing::InSequence enforce_callback_order;
+  testing::InSequence seq;
   EXPECT_CALL(observer,
               BookmarkPermanentNodeVisibilityChanged(model_->mobile_node()))
       .WillOnce([this](const BookmarkPermanentNode* node) {
@@ -2065,7 +2004,7 @@
 
   // Expect first a callback that the folder is visible, then that the child
   // bookmark is added.
-  testing::InSequence enforce_callback_order;
+  testing::InSequence seq;
   EXPECT_CALL(observer, OnWillRemoveBookmarks(_, _, bookmark, _));
   EXPECT_CALL(observer, BookmarkNodeRemoved(_, _, bookmark, _, _))
       .WillOnce(WithArg<2>(
@@ -2101,7 +2040,7 @@
 
   // Expect first a callback that the folder is visible, then that the child
   // bookmark is added.
-  testing::InSequence enforce_callback_order;
+  testing::InSequence seq;
   EXPECT_CALL(observer, OnWillMoveBookmarkNode(source_folder, 0,
                                                model_->bookmark_bar_node(), 0));
   EXPECT_CALL(observer, BookmarkNodeMoved(source_folder, 0,
@@ -2136,7 +2075,7 @@
 
   // Expect first a callback that the folder is visible, then that the child
   // bookmark is added.
-  testing::InSequence enforce_callback_order;
+  testing::InSequence seq;
   EXPECT_CALL(observer,
               OnWillMoveBookmarkNode(source_folder, 0, destination_folder, 0));
   EXPECT_CALL(observer,
@@ -2184,7 +2123,7 @@
 
   // Expect first a callback that the folder is visible, then that the child
   // bookmark is added.
-  testing::InSequence enforce_callback_order;
+  testing::InSequence seq;
   EXPECT_CALL(observer,
               OnWillMoveBookmarkNode(source_folder, 0, destination_folder, 0));
   EXPECT_CALL(observer,
@@ -2219,7 +2158,7 @@
   base::ScopedObservation<BookmarkModel, BookmarkModelObserver> observation(
       &observer);
   observation.Observe(model_.get());
-  testing::InSequence enforce_callback_order;
+  testing::InSequence seq;
 
   // First expect callbacks for the previously-visible local permanent folders.
   EXPECT_CALL(observer, BookmarkPermanentNodeVisibilityChanged(
@@ -2290,7 +2229,7 @@
   base::ScopedObservation<BookmarkModel, BookmarkModelObserver> observation(
       &observer);
   observation.Observe(model_.get());
-  testing::InSequence enforce_callback_order;
+  testing::InSequence seq;
 
   // Then expect callbacks for the previously-invisible local permanent
   // folders.
@@ -2352,9 +2291,12 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(
       bookmarks::kAllBookmarksBaselineFolderVisibility);
+  model_->RemoveObserver(&mock_observer());
   model_->RemoveObserver(this);
   model_ = TestBookmarkClient::CreateModelWithClient(
       std::make_unique<TestBookmarkClientWithUndo>());
+  model_->AddObserver(this);
+  model_->AddObserver(&mock_observer());
 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
   EXPECT_FALSE(model_->bookmark_bar_node()->IsVisible());
 #else
diff --git a/components/bookmarks/test/mock_bookmark_model_observer.h b/components/bookmarks/test/mock_bookmark_model_observer.h
index 1f622e2e..8771da6 100644
--- a/components/bookmarks/test/mock_bookmark_model_observer.h
+++ b/components/bookmarks/test/mock_bookmark_model_observer.h
@@ -61,6 +61,12 @@
               BookmarkAllUserNodesRemoved,
               (const std::set<GURL>&, const base::Location&));
 
+  MOCK_METHOD(void, OnWillRemoveAllUserBookmarks, (const base::Location&));
+
+  MOCK_METHOD(void, ExtensiveBookmarkChangesBeginning, ());
+
+  MOCK_METHOD(void, ExtensiveBookmarkChangesEnded, ());
+
   MOCK_METHOD(void, GroupedBookmarkChangesBeginning, ());
 
   MOCK_METHOD(void, GroupedBookmarkChangesEnded, ());
diff --git a/components/collaboration/internal/android/collaboration_service_android.cc b/components/collaboration/internal/android/collaboration_service_android.cc
index d6eadfb..7d53493a 100644
--- a/components/collaboration/internal/android/collaboration_service_android.cc
+++ b/components/collaboration/internal/android/collaboration_service_android.cc
@@ -22,7 +22,6 @@
 #include "components/collaboration/public/core_jni_headers/ServiceStatus_jni.h"
 
 using base::android::AttachCurrentThread;
-using base::android::ConvertJavaStringToUTF8;
 using base::android::JavaRef;
 using base::android::ScopedJavaGlobalRef;
 using base::android::ScopedJavaLocalRef;
@@ -84,11 +83,11 @@
 void CollaborationServiceAndroid::StartShareOrManageFlow(
     JNIEnv* env,
     int64_t delegateNativePtr,
-    const JavaRef<jstring>& j_sync_group_id,
+    const std::string& sync_group_id,
     const JavaRef<jobject>& j_local_group_id,
     int32_t entry) {
   tab_groups::EitherGroupID either_id =
-      tab_groups::JavaSyncOrLocalGroupIdToEitherGroupId(env, j_sync_group_id,
+      tab_groups::JavaSyncOrLocalGroupIdToEitherGroupId(env, sync_group_id,
                                                         j_local_group_id);
 
   collaboration_service_->StartShareOrManageFlow(
@@ -99,11 +98,11 @@
 void CollaborationServiceAndroid::StartLeaveOrDeleteFlow(
     JNIEnv* env,
     int64_t delegateNativePtr,
-    const JavaRef<jstring>& j_sync_group_id,
+    const std::string& sync_group_id,
     const JavaRef<jobject>& j_local_group_id,
     int32_t entry) {
   tab_groups::EitherGroupID either_id =
-      tab_groups::JavaSyncOrLocalGroupIdToEitherGroupId(env, j_sync_group_id,
+      tab_groups::JavaSyncOrLocalGroupIdToEitherGroupId(env, sync_group_id,
                                                         j_local_group_id);
 
   collaboration_service_->StartLeaveOrDeleteFlow(
@@ -123,19 +122,18 @@
 
 int32_t CollaborationServiceAndroid::GetCurrentUserRoleForGroup(
     JNIEnv* env,
-    const JavaRef<jstring>& group_id) {
+    const std::string& group_id) {
   data_sharing::MemberRole role =
-      collaboration_service_->GetCurrentUserRoleForGroup(
-          GroupId(ConvertJavaStringToUTF8(env, group_id)));
+      collaboration_service_->GetCurrentUserRoleForGroup(GroupId(group_id));
 
   return static_cast<int32_t>(role);
 }
 
 jni_zero::ScopedJavaLocalRef<jobject> CollaborationServiceAndroid::GetGroupData(
     JNIEnv* env,
-    const base::android::JavaRef<jstring>& group_id) {
-  const std::optional<GroupData> data = collaboration_service_->GetGroupData(
-      GroupId(ConvertJavaStringToUTF8(env, group_id)));
+    const std::string& group_id) {
+  const std::optional<GroupData> data =
+      collaboration_service_->GetGroupData(GroupId(group_id));
   if (!data.has_value()) {
     return nullptr;
   }
@@ -145,20 +143,20 @@
 
 void CollaborationServiceAndroid::LeaveGroup(
     JNIEnv* env,
-    const JavaRef<jstring>& group_id,
+    const std::string& group_id,
     const JavaRef<jobject>& j_callback) {
   collaboration_service_->LeaveGroup(
-      GroupId(ConvertJavaStringToUTF8(env, group_id)),
+      GroupId(group_id),
       base::BindOnce(&base::android::RunBooleanCallbackAndroid,
                      ScopedJavaGlobalRef<jobject>(j_callback)));
 }
 
 void CollaborationServiceAndroid::DeleteGroup(
     JNIEnv* env,
-    const JavaRef<jstring>& group_id,
+    const std::string& group_id,
     const JavaRef<jobject>& j_callback) {
   collaboration_service_->DeleteGroup(
-      GroupId(ConvertJavaStringToUTF8(env, group_id)),
+      GroupId(group_id),
       base::BindOnce(&base::android::RunBooleanCallbackAndroid,
                      ScopedJavaGlobalRef<jobject>(j_callback)));
 }
diff --git a/components/collaboration/internal/android/collaboration_service_android.h b/components/collaboration/internal/android/collaboration_service_android.h
index fa5c8d6..19fe16b3 100644
--- a/components/collaboration/internal/android/collaboration_service_android.h
+++ b/components/collaboration/internal/android/collaboration_service_android.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_COLLABORATION_INTERNAL_ANDROID_COLLABORATION_SERVICE_ANDROID_H_
 #define COMPONENTS_COLLABORATION_INTERNAL_ANDROID_COLLABORATION_SERVICE_ANDROID_H_
 
+#include <string>
+
 #include "base/android/jni_android.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/memory/raw_ptr.h"
@@ -30,27 +32,25 @@
   void StartShareOrManageFlow(
       JNIEnv* env,
       int64_t delegate,
-      const base::android::JavaRef<jstring>& j_sync_group_id,
+      const std::string& sync_group_id,
       const base::android::JavaRef<jobject>& j_local_group_id,
       int32_t entry);
   void StartLeaveOrDeleteFlow(
       JNIEnv* env,
       int64_t delegate,
-      const base::android::JavaRef<jstring>& j_sync_group_id,
+      const std::string& sync_group_id,
       const base::android::JavaRef<jobject>& j_local_group_id,
       int32_t entry);
   base::android::ScopedJavaLocalRef<jobject> GetServiceStatus(JNIEnv* env);
-  int32_t GetCurrentUserRoleForGroup(
-      JNIEnv* env,
-      const base::android::JavaRef<jstring>& group_id);
+  int32_t GetCurrentUserRoleForGroup(JNIEnv* env, const std::string& group_id);
   jni_zero::ScopedJavaLocalRef<jobject> GetGroupData(
       JNIEnv* env,
-      const base::android::JavaRef<jstring>& group_id);
+      const std::string& group_id);
   void LeaveGroup(JNIEnv* env,
-                  const base::android::JavaRef<jstring>& group_id,
+                  const std::string& group_id,
                   const base::android::JavaRef<jobject>& j_callback);
   void DeleteGroup(JNIEnv* env,
-                   const base::android::JavaRef<jstring>& group_id,
+                   const std::string& group_id,
                    const base::android::JavaRef<jobject>& j_callback);
 
   // Returns the CollaborationServiceImpl java object.
diff --git a/components/collaboration/internal/android/comments/comments_service_bridge.cc b/components/collaboration/internal/android/comments/comments_service_bridge.cc
index eb012b5..6281e2b 100644
--- a/components/collaboration/internal/android/comments/comments_service_bridge.cc
+++ b/components/collaboration/internal/android/comments/comments_service_bridge.cc
@@ -14,7 +14,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/uuid.h"
 #include "components/collaboration/public/comments/comments_service.h"
-#include "components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h"
+#include "third_party/jni_zero/default_conversions.h"
 #include "url/android/gurl_android.h"
 #include "url/gurl.h"
 
@@ -22,7 +22,6 @@
 #include "components/collaboration/internal/comments_jni_headers/CommentsServiceBridge_jni.h"
 
 using base::android::AttachCurrentThread;
-using base::android::ConvertJavaStringToUTF8;
 using base::android::JavaRef;
 using base::android::RunBooleanCallbackAndroid;
 using base::android::ScopedJavaGlobalRef;
@@ -70,70 +69,51 @@
   return ScopedJavaLocalRef<jobject>(java_ref_);
 }
 
-bool CommentsServiceBridge::IsInitialized(JNIEnv* env,
-                                          const JavaRef<jobject>& j_caller) {
+bool CommentsServiceBridge::IsInitialized(JNIEnv* env) {
   return service_->IsInitialized();
 }
 
-bool CommentsServiceBridge::IsEmptyService(JNIEnv* env,
-                                           const JavaRef<jobject>& j_caller) {
+bool CommentsServiceBridge::IsEmptyService(JNIEnv* env) {
   return service_->IsEmptyService();
 }
 
-ScopedJavaLocalRef<jstring> CommentsServiceBridge::AddComment(
+base::Uuid CommentsServiceBridge::AddComment(
     JNIEnv* env,
-    const JavaRef<jobject>& j_caller,
-    const JavaRef<jstring>& j_collaboration_id,
-    const JavaRef<jobject>& j_url,
-    const JavaRef<jstring>& j_content,
-    const JavaRef<jstring>& j_parent_comment_id,
+    const std::string& collaboration_id,
+    const GURL& url,
+    const std::string& content,
+    const std::optional<base::Uuid>& parent_comment_id,
     const JavaRef<jobject>& j_success_callback) {
-  std::optional<base::Uuid> parent_comment_id;
-  if (j_parent_comment_id) {
-    parent_comment_id = tab_groups::JavaStringToUuid(env, j_parent_comment_id);
-  }
-
   auto callback =
       base::BindOnce(&RunBooleanCallbackAndroid,
                      ScopedJavaGlobalRef<jobject>(j_success_callback));
-  base::Uuid new_comment_id = service_->AddComment(
-      GroupId(ConvertJavaStringToUTF8(env, j_collaboration_id)),
-      url::GURLAndroid::ToNativeGURL(env, j_url),
-      ConvertJavaStringToUTF8(env, j_content), parent_comment_id,
-      std::move(callback));
-
-  return tab_groups::UuidToJavaString(env, new_comment_id);
+  return service_->AddComment(GroupId(collaboration_id), url, content,
+                              parent_comment_id, std::move(callback));
 }
 
 void CommentsServiceBridge::EditComment(
     JNIEnv* env,
-    const JavaRef<jobject>& j_caller,
-    const JavaRef<jstring>& j_comment_id,
-    const JavaRef<jstring>& j_new_content,
+    const base::Uuid& comment_id,
+    const std::string& new_content,
     const JavaRef<jobject>& j_success_callback) {
   auto callback =
       base::BindOnce(&RunBooleanCallbackAndroid,
                      ScopedJavaGlobalRef<jobject>(j_success_callback));
-  service_->EditComment(tab_groups::JavaStringToUuid(env, j_comment_id),
-                        ConvertJavaStringToUTF8(env, j_new_content),
-                        std::move(callback));
+  service_->EditComment(comment_id, new_content, std::move(callback));
 }
 
 void CommentsServiceBridge::DeleteComment(
     JNIEnv* env,
-    const JavaRef<jobject>& j_caller,
-    const JavaRef<jstring>& j_comment_id,
+    const base::Uuid& comment_id,
     const JavaRef<jobject>& j_success_callback) {
   auto callback =
       base::BindOnce(&RunBooleanCallbackAndroid,
                      ScopedJavaGlobalRef<jobject>(j_success_callback));
-  service_->DeleteComment(tab_groups::JavaStringToUuid(env, j_comment_id),
-                          std::move(callback));
+  service_->DeleteComment(comment_id, std::move(callback));
 }
 
 void CommentsServiceBridge::QueryComments(
     JNIEnv* env,
-    const JavaRef<jobject>& j_caller,
     const JavaRef<jobject>& j_filter_criteria,
     const JavaRef<jobject>& j_pagination_criteria,
     const JavaRef<jobject>& j_callback) {
@@ -142,14 +122,12 @@
 
 void CommentsServiceBridge::AddObserver(
     JNIEnv* env,
-    const JavaRef<jobject>& j_caller,
     const JavaRef<jobject>& j_observer,
     const JavaRef<jobject>& j_filter_criteria) {
   // TODO(crbug.com/435005417): Implement this.
 }
 
 void CommentsServiceBridge::RemoveObserver(JNIEnv* env,
-                                           const JavaRef<jobject>& j_caller,
                                            const JavaRef<jobject>& j_observer) {
   // TODO(crbug.com/435005417): Implement this.
 }
diff --git a/components/collaboration/internal/android/comments/comments_service_bridge.h b/components/collaboration/internal/android/comments/comments_service_bridge.h
index 20e7d164..6d95f22 100644
--- a/components/collaboration/internal/android/comments/comments_service_bridge.h
+++ b/components/collaboration/internal/android/comments/comments_service_bridge.h
@@ -7,6 +7,7 @@
 
 #include "base/android/scoped_java_ref.h"
 #include "base/supports_user_data.h"
+#include "base/uuid.h"
 #include "components/collaboration/public/comments/comments_service.h"
 
 namespace collaboration::comments::android {
@@ -25,39 +26,31 @@
       CommentsService* service);
 
   // Methods called from Java via JNI.
-  bool IsInitialized(JNIEnv* env,
-                     const base::android::JavaRef<jobject>& j_caller);
-  bool IsEmptyService(JNIEnv* env,
-                      const base::android::JavaRef<jobject>& j_caller);
-  base::android::ScopedJavaLocalRef<jstring> AddComment(
+  bool IsInitialized(JNIEnv* env);
+  bool IsEmptyService(JNIEnv* env);
+  base::Uuid AddComment(
       JNIEnv* env,
-      const base::android::JavaRef<jobject>& j_caller,
-      const base::android::JavaRef<jstring>& j_collaboration_id,
-      const base::android::JavaRef<jobject>& j_url,
-      const base::android::JavaRef<jstring>& j_content,
-      const base::android::JavaRef<jstring>& j_parent_comment_id,
+      const std::string& collaboration_id,
+      const GURL& url,
+      const std::string& content,
+      const std::optional<base::Uuid>& parent_comment_id,
       const base::android::JavaRef<jobject>& j_success_callback);
   void EditComment(JNIEnv* env,
-                   const base::android::JavaRef<jobject>& j_caller,
-                   const base::android::JavaRef<jstring>& j_comment_id,
-                   const base::android::JavaRef<jstring>& j_new_content,
+                   const base::Uuid& comment_id,
+                   const std::string& new_content,
                    const base::android::JavaRef<jobject>& j_success_callback);
   void DeleteComment(JNIEnv* env,
-                     const base::android::JavaRef<jobject>& j_caller,
-                     const base::android::JavaRef<jstring>& j_comment_id,
+                     const base::Uuid& comment_id,
                      const base::android::JavaRef<jobject>& j_success_callback);
   void QueryComments(
       JNIEnv* env,
-      const base::android::JavaRef<jobject>& j_caller,
       const base::android::JavaRef<jobject>& j_filter_criteria,
       const base::android::JavaRef<jobject>& j_pagination_criteria,
       const base::android::JavaRef<jobject>& j_callback);
   void AddObserver(JNIEnv* env,
-                   const base::android::JavaRef<jobject>& j_caller,
                    const base::android::JavaRef<jobject>& j_observer,
                    const base::android::JavaRef<jobject>& j_filter_criteria);
   void RemoveObserver(JNIEnv* env,
-                      const base::android::JavaRef<jobject>& j_caller,
                       const base::android::JavaRef<jobject>& j_observer);
 
   // Returns the companion Java object for this bridge.
diff --git a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java
index 7c383a8..847e359 100644
--- a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java
+++ b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java
@@ -6,6 +6,7 @@
 
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
+import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
 import org.chromium.base.Callback;
@@ -141,29 +142,35 @@
         void startShareOrManageFlow(
                 long nativeCollaborationServiceAndroid,
                 long delegateNativePtr,
-                @Nullable String syncId,
+                @JniType("std::string") @Nullable String syncId,
                 @Nullable LocalTabGroupId localId,
                 int entry);
 
         void startLeaveOrDeleteFlow(
                 long nativeCollaborationServiceAndroid,
                 long delegateNativePtr,
-                @Nullable String syncId,
+                @JniType("std::string") @Nullable String syncId,
                 @Nullable LocalTabGroupId localId,
                 int entry);
 
         ServiceStatus getServiceStatus(long nativeCollaborationServiceAndroid);
 
         int getCurrentUserRoleForGroup(
-                long nativeCollaborationServiceAndroid, @Nullable String collaborationId);
+                long nativeCollaborationServiceAndroid,
+                @JniType("std::string") @Nullable String collaborationId);
 
         GroupData getGroupData(
-                long nativeCollaborationServiceAndroid, @Nullable String collaborationId);
+                long nativeCollaborationServiceAndroid,
+                @JniType("std::string") @Nullable String collaborationId);
 
         void leaveGroup(
-                long nativeCollaborationServiceAndroid, String groupId, Callback<Boolean> callback);
+                long nativeCollaborationServiceAndroid,
+                @JniType("std::string") String groupId,
+                Callback<Boolean> callback);
 
         void deleteGroup(
-                long nativeCollaborationServiceAndroid, String groupId, Callback<Boolean> callback);
+                long nativeCollaborationServiceAndroid,
+                @JniType("std::string") String groupId,
+                Callback<Boolean> callback);
     }
 }
diff --git a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/comments/CommentsServiceBridge.java b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/comments/CommentsServiceBridge.java
index 923d301d..06bc4a6 100644
--- a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/comments/CommentsServiceBridge.java
+++ b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/comments/CommentsServiceBridge.java
@@ -6,6 +6,7 @@
 
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
+import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
 import org.chromium.base.Callback;
@@ -38,12 +39,12 @@
 
     @Override
     public boolean isInitialized() {
-        return CommentsServiceBridgeJni.get().isInitialized(mNativeCommentsServiceBridge, this);
+        return CommentsServiceBridgeJni.get().isInitialized(mNativeCommentsServiceBridge);
     }
 
     @Override
     public boolean isEmptyService() {
-        return CommentsServiceBridgeJni.get().isEmptyService(mNativeCommentsServiceBridge, this);
+        return CommentsServiceBridgeJni.get().isEmptyService(mNativeCommentsServiceBridge);
     }
 
     @Override
@@ -53,17 +54,16 @@
             String content,
             @Nullable UUID parentCommentId,
             Callback<Boolean> successCallback) {
-        String uuid =
+        String uuidString =
                 CommentsServiceBridgeJni.get()
                         .addComment(
                                 mNativeCommentsServiceBridge,
-                                this,
                                 collaborationId,
                                 url,
                                 content,
-                                parentCommentId == null ? "" : parentCommentId.toString(),
+                                parentCommentId == null ? null : parentCommentId.toString(),
                                 successCallback);
-        return UUID.fromString(uuid);
+        return UUID.fromString(uuidString);
     }
 
     @Override
@@ -71,7 +71,6 @@
         CommentsServiceBridgeJni.get()
                 .editComment(
                         mNativeCommentsServiceBridge,
-                        this,
                         commentId.toString(),
                         newContent,
                         successCallback);
@@ -80,8 +79,7 @@
     @Override
     public void deleteComment(UUID commentId, Callback<Boolean> successCallback) {
         CommentsServiceBridgeJni.get()
-                .deleteComment(
-                        mNativeCommentsServiceBridge, this, commentId.toString(), successCallback);
+                .deleteComment(mNativeCommentsServiceBridge, commentId.toString(), successCallback);
     }
 
     @Override
@@ -91,11 +89,7 @@
             Callback<CommentsService.QueryResult> callback) {
         CommentsServiceBridgeJni.get()
                 .queryComments(
-                        mNativeCommentsServiceBridge,
-                        this,
-                        filterCriteria,
-                        paginationCriteria,
-                        callback);
+                        mNativeCommentsServiceBridge, filterCriteria, paginationCriteria, callback);
     }
 
     @Override
@@ -103,58 +97,52 @@
             CommentsService.CommentsObserver observer,
             CommentsService.FilterCriteria filterCriteria) {
         CommentsServiceBridgeJni.get()
-                .addObserver(mNativeCommentsServiceBridge, this, observer, filterCriteria);
+                .addObserver(mNativeCommentsServiceBridge, observer, filterCriteria);
     }
 
     @Override
     public void removeObserver(CommentsService.CommentsObserver observer) {
-        CommentsServiceBridgeJni.get().removeObserver(mNativeCommentsServiceBridge, this, observer);
+        CommentsServiceBridgeJni.get().removeObserver(mNativeCommentsServiceBridge, observer);
     }
 
     @NativeMethods
     interface Natives {
-        boolean isInitialized(long nativeCommentsServiceBridge, CommentsServiceBridge caller);
+        boolean isInitialized(long nativeCommentsServiceBridge);
 
-        boolean isEmptyService(long nativeCommentsServiceBridge, CommentsServiceBridge caller);
+        boolean isEmptyService(long nativeCommentsServiceBridge);
 
+        @JniType("base::Uuid")
         String addComment(
                 long nativeCommentsServiceBridge,
-                CommentsServiceBridge caller,
-                String collaborationId,
-                GURL url,
-                String content,
-                String parentCommentId,
+                @JniType("std::string") String collaborationId,
+                @JniType("GURL") GURL url,
+                @JniType("std::string") String content,
+                @JniType("std::optional<base::Uuid>") @Nullable String parentCommentId,
                 Callback<Boolean> successCallback);
 
         void editComment(
                 long nativeCommentsServiceBridge,
-                CommentsServiceBridge caller,
-                String commentId,
-                String newContent,
+                @JniType("base::Uuid") String commentId,
+                @JniType("std::string") String newContent,
                 Callback<Boolean> successCallback);
 
         void deleteComment(
                 long nativeCommentsServiceBridge,
-                CommentsServiceBridge caller,
-                String commentId,
+                @JniType("base::Uuid") String commentId,
                 Callback<Boolean> successCallback);
 
         void queryComments(
                 long nativeCommentsServiceBridge,
-                CommentsServiceBridge caller,
                 CommentsService.FilterCriteria filterCriteria,
                 CommentsService.PaginationCriteria paginationCriteria,
                 Callback<CommentsService.QueryResult> callback);
 
         void addObserver(
                 long nativeCommentsServiceBridge,
-                CommentsServiceBridge caller,
                 CommentsService.CommentsObserver observer,
                 CommentsService.FilterCriteria filterCriteria);
 
         void removeObserver(
-                long nativeCommentsServiceBridge,
-                CommentsServiceBridge caller,
-                CommentsService.CommentsObserver observer);
+                long nativeCommentsServiceBridge, CommentsService.CommentsObserver observer);
     }
 }
diff --git a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/messaging/MessagingBackendServiceBridge.java b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/messaging/MessagingBackendServiceBridge.java
index ddecadd..07f92567 100644
--- a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/messaging/MessagingBackendServiceBridge.java
+++ b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/messaging/MessagingBackendServiceBridge.java
@@ -6,6 +6,7 @@
 
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
+import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
 import org.chromium.base.ObserverList;
@@ -206,30 +207,32 @@
         List<PersistentMessage> getMessagesForTab(
                 long nativeMessagingBackendServiceBridge,
                 int localTabId,
-                @Nullable String syncTabId,
+                @JniType("std::optional<base::Uuid>") @Nullable String syncTabId,
                 @PersistentNotificationType int type);
 
         List<PersistentMessage> getMessagesForGroup(
                 long nativeMessagingBackendServiceBridge,
                 @Nullable LocalTabGroupId localGroupId,
-                @Nullable String syncGroupId,
+                @JniType("std::optional<base::Uuid>") @Nullable String syncGroupId,
                 @PersistentNotificationType int type);
 
         List<PersistentMessage> getMessages(
                 long nativeMessagingBackendServiceBridge, @PersistentNotificationType int type);
 
         List<ActivityLogItem> getActivityLog(
-                long nativeMessagingBackendServiceBridge, String collaborationId);
+                long nativeMessagingBackendServiceBridge,
+                @JniType("std::string") String collaborationId);
 
         void clearDirtyTabMessagesForGroup(
-                long nativeMessagingBackendServiceBridge, String collaborationId);
+                long nativeMessagingBackendServiceBridge,
+                @JniType("std::string") String collaborationId);
 
         void runInstantaneousMessageSuccessCallback(
                 long nativeMessagingBackendServiceBridge, long callback, boolean success);
 
         void clearPersistentMessage(
                 long nativeMessagingBackendServiceBridge,
-                String messageId,
+                @JniType("base::Uuid") String messageId,
                 @PersistentNotificationType int type);
     }
 }
diff --git a/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.cc b/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.cc
index d70a26c8..a77b89b 100644
--- a/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.cc
+++ b/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.cc
@@ -9,7 +9,6 @@
 #include <set>
 
 #include "base/android/jni_android.h"
-#include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/functional/callback.h"
@@ -21,12 +20,11 @@
 #include "components/collaboration/public/messaging/message.h"
 #include "components/saved_tab_groups/public/android/tab_group_sync_conversions_bridge.h"
 #include "components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h"
+#include "third_party/jni_zero/default_conversions.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
 #include "components/collaboration/internal/messaging_jni_headers/MessagingBackendServiceBridge_jni.h"
 
-using base::android::ConvertJavaStringToUTF16;
-
 namespace collaboration::messaging::android {
 namespace {
 const char kMessagingBackendServiceBridgeUserDataKey[] =
@@ -89,22 +87,19 @@
 MessagingBackendServiceBridge::GetMessagesForTab(
     JNIEnv* env,
     int32_t j_local_tab_id,
-    const base::android::JavaRef<jstring>& j_sync_tab_id,
+    const std::optional<base::Uuid>& sync_tab_id,
     int32_t j_type) {
   auto type = static_cast<PersistentNotificationType>(j_type);
 
   if (j_local_tab_id != kInvalidTabId) {
-    CHECK(!j_sync_tab_id);
+    CHECK(!sync_tab_id);
     tab_groups::LocalTabID tab_id = tab_groups::FromJavaTabId(j_local_tab_id);
     auto messages = service_->GetMessagesForTab(tab_id, type);
     return PersistentMessagesToJava(env, messages);
   }
-  if (j_sync_tab_id) {
+  if (sync_tab_id) {
     CHECK(j_local_tab_id == kInvalidTabId);
-    std::string sync_tab_id_str =
-        base::android::ConvertJavaStringToUTF8(env, j_sync_tab_id);
-    auto tab_id = base::Uuid::ParseLowercase(sync_tab_id_str);
-    auto messages = service_->GetMessagesForTab(tab_id, type);
+    auto messages = service_->GetMessagesForTab(*sync_tab_id, type);
     return PersistentMessagesToJava(env, messages);
   }
 
@@ -115,24 +110,21 @@
 MessagingBackendServiceBridge::GetMessagesForGroup(
     JNIEnv* env,
     const base::android::JavaRef<jobject>& j_local_group_id,
-    const base::android::JavaRef<jstring>& j_sync_group_id,
+    const std::optional<base::Uuid>& sync_group_id,
     int32_t j_type) {
   auto type = static_cast<PersistentNotificationType>(j_type);
 
   if (j_local_group_id) {
-    CHECK(!j_sync_group_id);
+    CHECK(!sync_group_id);
     auto group_id =
         tab_groups::TabGroupSyncConversionsBridge::FromJavaTabGroupId(
             env, j_local_group_id);
     auto messages = service_->GetMessagesForGroup(group_id, type);
     return PersistentMessagesToJava(env, messages);
   }
-  if (j_sync_group_id) {
+  if (sync_group_id) {
     CHECK(!j_local_group_id);
-    std::string sync_group_id_str =
-        base::android::ConvertJavaStringToUTF8(env, j_sync_group_id);
-    auto group_id = base::Uuid::ParseLowercase(sync_group_id_str);
-    auto messages = service_->GetMessagesForGroup(group_id, type);
+    auto messages = service_->GetMessagesForGroup(*sync_group_id, type);
     return PersistentMessagesToJava(env, messages);
   }
 
@@ -149,29 +141,24 @@
 base::android::ScopedJavaLocalRef<jobject>
 MessagingBackendServiceBridge::GetActivityLog(
     JNIEnv* env,
-    const base::android::JavaRef<jstring>& j_collaboration_id) {
+    const std::string& collaboration_id) {
   ActivityLogQueryParams query_params;
-  query_params.collaboration_id = data_sharing::GroupId(
-      base::android::ConvertJavaStringToUTF8(env, j_collaboration_id));
+  query_params.collaboration_id = data_sharing::GroupId(collaboration_id);
   auto activity_log_items = service_->GetActivityLog(query_params);
   return ActivityLogItemsToJava(env, activity_log_items);
 }
 
 void MessagingBackendServiceBridge::ClearDirtyTabMessagesForGroup(
     JNIEnv* env,
-    const base::android::JavaRef<jstring>& j_collaboration_id) {
-  auto collaboration_id = data_sharing::GroupId(
-      base::android::ConvertJavaStringToUTF8(env, j_collaboration_id));
-  service_->ClearDirtyTabMessagesForGroup(collaboration_id);
+    const std::string& collaboration_id) {
+  service_->ClearDirtyTabMessagesForGroup(
+      data_sharing::GroupId(collaboration_id));
 }
 
 void MessagingBackendServiceBridge::ClearPersistentMessage(
     JNIEnv* env,
-    const base::android::JavaRef<jstring>& j_message_id,
+    const base::Uuid& message_id,
     int32_t j_type) {
-  CHECK(j_message_id);
-  auto message_id = base::Uuid::ParseLowercase(
-      base::android::ConvertJavaStringToUTF8(env, j_message_id));
   auto type = static_cast<PersistentNotificationType>(j_type);
   service_->ClearPersistentMessage(message_id, type);
 }
diff --git a/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.h b/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.h
index fd1e5724..e70b5498 100644
--- a/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.h
+++ b/components/collaboration/internal/android/messaging/messaging_backend_service_bridge.h
@@ -5,11 +5,14 @@
 #ifndef COMPONENTS_COLLABORATION_INTERNAL_ANDROID_MESSAGING_MESSAGING_BACKEND_SERVICE_BRIDGE_H_
 #define COMPONENTS_COLLABORATION_INTERNAL_ANDROID_MESSAGING_MESSAGING_BACKEND_SERVICE_BRIDGE_H_
 
+#include <optional>
 #include <set>
+#include <string>
 
 #include "base/android/scoped_java_ref.h"
 #include "base/supports_user_data.h"
 #include "base/uuid.h"
+#include "components/collaboration/internal/android/messaging/conversion_utils.h"
 #include "components/collaboration/public/messaging/messaging_backend_service.h"
 
 namespace collaboration::messaging::android {
@@ -45,25 +48,23 @@
   base::android::ScopedJavaLocalRef<jobject> GetMessagesForTab(
       JNIEnv* env,
       int32_t j_local_tab_id,
-      const base::android::JavaRef<jstring>& j_sync_tab_id,
+      const std::optional<base::Uuid>& sync_tab_id,
       int32_t j_type);
   base::android::ScopedJavaLocalRef<jobject> GetMessagesForGroup(
       JNIEnv* env,
       const base::android::JavaRef<jobject>& j_local_group_id,
-      const base::android::JavaRef<jstring>& j_sync_group_id,
+      const std::optional<base::Uuid>& sync_group_id,
       int32_t j_type);
   base::android::ScopedJavaLocalRef<jobject> GetMessages(JNIEnv* env,
                                                          int32_t j_type);
   base::android::ScopedJavaLocalRef<jobject> GetActivityLog(
       JNIEnv* env,
-      const base::android::JavaRef<jstring>& j_collaboration_id);
-  void ClearDirtyTabMessagesForGroup(
-      JNIEnv* env,
-      const base::android::JavaRef<jstring>& j_collaboration_id);
-  void ClearPersistentMessage(
-      JNIEnv* env,
-      const base::android::JavaRef<jstring>& j_message_id,
-      int32_t j_type);
+      const std::string& collaboration_id);
+  void ClearDirtyTabMessagesForGroup(JNIEnv* env,
+                                     const std::string& collaboration_id);
+  void ClearPersistentMessage(JNIEnv* env,
+                              const base::Uuid& message_id,
+                              int32_t j_type);
 
   void RunInstantaneousMessageSuccessCallback(JNIEnv* env,
                                               int64_t j_callback,
diff --git a/components/commerce/core/android/java/src/org/chromium/components/commerce/core/ShoppingService.java b/components/commerce/core/android/java/src/org/chromium/components/commerce/core/ShoppingService.java
index 13cacf92..7eecf2e2 100644
--- a/components/commerce/core/android/java/src/org/chromium/components/commerce/core/ShoppingService.java
+++ b/components/commerce/core/android/java/src/org/chromium/components/commerce/core/ShoppingService.java
@@ -8,6 +8,7 @@
 
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
+import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
 import org.chromium.base.Callback;
@@ -602,11 +603,11 @@
                 int type,
                 int idType,
                 int managementType,
-                String id,
-                String seenOfferId,
+                @JniType("std::string") String id,
+                @JniType("std::string") String seenOfferId,
                 long seenPrice,
-                String seenCountry,
-                String seenLocale,
+                @JniType("std::string") String seenCountry,
+                @JniType("std::string") String seenLocale,
                 Callback<Boolean> callback);
 
         void unsubscribe(
@@ -614,7 +615,7 @@
                 int type,
                 int idType,
                 int managementType,
-                String id,
+                @JniType("std::string") String id,
                 Callback<Boolean> callback);
 
         void isSubscribed(
@@ -622,7 +623,7 @@
                 int type,
                 int idType,
                 int managementType,
-                String id,
+                @JniType("std::string") String id,
                 Callback<Boolean> callback);
 
         boolean isSubscribedFromCache(
@@ -630,7 +631,7 @@
                 int type,
                 int idType,
                 int managementType,
-                String id);
+                @JniType("std::string") String id);
 
         void getAllPriceTrackedBookmarks(
                 long nativeShoppingServiceAndroid, Callback<List<BookmarkId>> callback);
diff --git a/components/commerce/core/android/shopping_service_android.cc b/components/commerce/core/android/shopping_service_android.cc
index 8d06cbe..ecdda65 100644
--- a/components/commerce/core/android/shopping_service_android.cc
+++ b/components/commerce/core/android/shopping_service_android.cc
@@ -21,7 +21,6 @@
 #include "components/commerce/core/android/core_jni/DiscountInfo_jni.h"
 #include "components/commerce/core/android/core_jni/ShoppingService_jni.h"
 
-using base::android::ConvertJavaStringToUTF8;
 using base::android::ConvertUTF8ToJavaString;
 using base::android::RunBooleanCallbackAndroid;
 using base::android::ScopedJavaLocalRef;
@@ -284,36 +283,31 @@
                             ScopedJavaGlobalRef<jobject>(j_callback)));
 }
 
-void ShoppingServiceAndroid::FetchPriceEmailPref(JNIEnv* env) {
+void ShoppingServiceAndroid::FetchPriceEmailPref() {
   CHECK(shopping_service_);
 
   shopping_service_->FetchPriceEmailPref();
 }
 
-void ShoppingServiceAndroid::ScheduleSavedProductUpdate(JNIEnv* env) {
+void ShoppingServiceAndroid::ScheduleSavedProductUpdate() {
   CHECK(shopping_service_);
 
   shopping_service_->ScheduleSavedProductUpdate();
 }
 
-void ShoppingServiceAndroid::Subscribe(JNIEnv* env,
-                                       int32_t j_type,
+void ShoppingServiceAndroid::Subscribe(int32_t j_type,
                                        int32_t j_id_type,
                                        int32_t j_management_type,
-                                       const JavaRef<jstring>& j_id,
-                                       const JavaRef<jstring>& j_seen_offer_id,
-                                       int64_t j_seen_price,
-                                       const JavaRef<jstring>& j_seen_country,
-                                       const JavaRef<jstring>& j_seen_locale,
+                                       const std::string& id,
+                                       const std::string& seen_offer_id,
+                                       int64_t seen_price,
+                                       const std::string& seen_country,
+                                       const std::string& seen_locale,
                                        const JavaRef<jobject>& j_callback) {
-  std::string id = ConvertJavaStringToUTF8(j_id);
-  std::string seen_offer_id = ConvertJavaStringToUTF8(j_seen_offer_id);
-  std::string seen_country = ConvertJavaStringToUTF8(j_seen_country);
-  std::string seen_locale = ConvertJavaStringToUTF8(j_seen_locale);
   CHECK(!id.empty());
 
   auto user_seen_offer = std::make_optional<UserSeenOffer>(
-      seen_offer_id, j_seen_price, seen_country, seen_locale);
+      seen_offer_id, seen_price, seen_country, seen_locale);
   CommerceSubscription sub(SubscriptionType(j_type), IdentifierType(j_id_type),
                            id, ManagementType(j_management_type),
                            kUnknownSubscriptionTimestamp,
@@ -328,13 +322,11 @@
   shopping_service_->Subscribe(std::move(subs), std::move(callback));
 }
 
-void ShoppingServiceAndroid::Unsubscribe(JNIEnv* env,
-                                         int32_t j_type,
+void ShoppingServiceAndroid::Unsubscribe(int32_t j_type,
                                          int32_t j_id_type,
                                          int32_t j_management_type,
-                                         const JavaRef<jstring>& j_id,
+                                         const std::string& id,
                                          const JavaRef<jobject>& j_callback) {
-  std::string id = ConvertJavaStringToUTF8(j_id);
   CHECK(!id.empty());
 
   CommerceSubscription sub(SubscriptionType(j_type), IdentifierType(j_id_type),
@@ -350,13 +342,11 @@
   shopping_service_->Unsubscribe(std::move(subs), std::move(callback));
 }
 
-void ShoppingServiceAndroid::IsSubscribed(JNIEnv* env,
-                                          int32_t j_type,
+void ShoppingServiceAndroid::IsSubscribed(int32_t j_type,
                                           int32_t j_id_type,
                                           int32_t j_management_type,
-                                          const JavaRef<jstring>& j_id,
+                                          const std::string& id,
                                           const JavaRef<jobject>& j_callback) {
-  std::string id = ConvertJavaStringToUTF8(j_id);
   CHECK(!id.empty());
 
   CommerceSubscription sub(SubscriptionType(j_type), IdentifierType(j_id_type),
@@ -372,13 +362,10 @@
           ScopedJavaGlobalRef<jobject>(j_callback)));
 }
 
-bool ShoppingServiceAndroid::IsSubscribedFromCache(
-    JNIEnv* env,
-    int32_t j_type,
-    int32_t j_id_type,
-    int32_t j_management_type,
-    const JavaRef<jstring>& j_id) {
-  std::string id = ConvertJavaStringToUTF8(j_id);
+bool ShoppingServiceAndroid::IsSubscribedFromCache(int32_t j_type,
+                                                   int32_t j_id_type,
+                                                   int32_t j_management_type,
+                                                   const std::string& id) {
   CHECK(!id.empty());
 
   CommerceSubscription sub(SubscriptionType(j_type), IdentifierType(j_id_type),
@@ -418,27 +405,27 @@
                                      succeeded);
 }
 
-bool ShoppingServiceAndroid::IsShoppingListEligible(JNIEnv* env) {
+bool ShoppingServiceAndroid::IsShoppingListEligible() {
   CHECK(shopping_service_);
 
   return shopping_service_->IsShoppingListEligible();
 }
 
-bool ShoppingServiceAndroid::IsMerchantViewerEnabled(JNIEnv* env) {
+bool ShoppingServiceAndroid::IsMerchantViewerEnabled() {
   CHECK(shopping_service_);
 
   return commerce::IsMerchantViewerEnabled(
       shopping_service_->GetAccountChecker());
 }
 
-bool ShoppingServiceAndroid::IsPriceInsightsEligible(JNIEnv* env) {
+bool ShoppingServiceAndroid::IsPriceInsightsEligible() {
   CHECK(shopping_service_);
 
   return commerce::IsPriceInsightsEligible(
       shopping_service_->GetAccountChecker());
 }
 
-bool ShoppingServiceAndroid::IsDiscountEligibleToShowOnNavigation(JNIEnv* env) {
+bool ShoppingServiceAndroid::IsDiscountEligibleToShowOnNavigation() {
   CHECK(shopping_service_);
 
   return commerce::IsDiscountEligibleToShowOnNavigation(
diff --git a/components/commerce/core/android/shopping_service_android.h b/components/commerce/core/android/shopping_service_android.h
index 2adfa02..469c0a12 100644
--- a/components/commerce/core/android/shopping_service_android.h
+++ b/components/commerce/core/android/shopping_service_android.h
@@ -60,51 +60,47 @@
                                       const JavaRef<jobject>& j_gurl,
                                       const JavaRef<jobject>& j_callback);
 
-  void FetchPriceEmailPref(JNIEnv* env);
+  void FetchPriceEmailPref();
 
-  void ScheduleSavedProductUpdate(JNIEnv* env);
+  void ScheduleSavedProductUpdate();
 
-  void Subscribe(JNIEnv* env,
-                 int32_t j_type,
+  void Subscribe(int32_t j_type,
                  int32_t j_id_type,
                  int32_t j_management_type,
-                 const JavaRef<jstring>& j_id,
-                 const JavaRef<jstring>& j_seen_offer_id,
-                 int64_t j_seen_price,
-                 const JavaRef<jstring>& j_seen_country,
-                 const JavaRef<jstring>& j_seen_locale,
+                 const std::string& id,
+                 const std::string& seen_offer_id,
+                 int64_t seen_price,
+                 const std::string& seen_country,
+                 const std::string& seen_locale,
                  const JavaRef<jobject>& j_callback);
 
-  void Unsubscribe(JNIEnv* env,
-                   int32_t j_type,
+  void Unsubscribe(int32_t j_type,
                    int32_t j_id_type,
                    int32_t j_management_type,
-                   const JavaRef<jstring>& j_id,
+                   const std::string& id,
                    const JavaRef<jobject>& j_callback);
 
-  void IsSubscribed(JNIEnv* env,
-                    int32_t j_type,
+  void IsSubscribed(int32_t j_type,
                     int32_t j_id_type,
                     int32_t j_management_type,
-                    const JavaRef<jstring>& j_id,
+                    const std::string& id,
                     const JavaRef<jobject>& j_callback);
 
-  bool IsSubscribedFromCache(JNIEnv* env,
-                             int32_t j_type,
+  bool IsSubscribedFromCache(int32_t j_type,
                              int32_t j_id_type,
                              int32_t j_management_type,
-                             const JavaRef<jstring>& j_id);
+                             const std::string& j_id);
 
   void GetAllPriceTrackedBookmarks(JNIEnv* env,
                                    const JavaRef<jobject>& j_callback);
 
-  bool IsShoppingListEligible(JNIEnv* env);
+  bool IsShoppingListEligible();
 
-  bool IsMerchantViewerEnabled(JNIEnv* env);
+  bool IsMerchantViewerEnabled();
 
-  bool IsPriceInsightsEligible(JNIEnv* env);
+  bool IsPriceInsightsEligible();
 
-  bool IsDiscountEligibleToShowOnNavigation(JNIEnv* env);
+  bool IsDiscountEligibleToShowOnNavigation();
 
   ScopedJavaGlobalRef<jobject> java_ref() { return java_ref_; }
 
diff --git a/components/contextual_search/contextual_search_service.cc b/components/contextual_search/contextual_search_service.cc
index 1565c71..9c306ed 100644
--- a/components/contextual_search/contextual_search_service.cc
+++ b/components/contextual_search/contextual_search_service.cc
@@ -54,6 +54,9 @@
   // The IdentityManager may be destroyed before this service, so clear the
   // pointer to avoid dangling pointer issues.
   identity_manager_ = nullptr;
+  // Destroy sessions now to invalidate the WeakPtrs held by pending
+  // callbacks (e.g. the cluster-info reset timer in ComposeboxQueryController).
+  sessions_.clear();
 }
 
 // static
diff --git a/components/contextual_search/internal/composebox_query_controller.cc b/components/contextual_search/internal/composebox_query_controller.cc
index 2472d8f..7eec23db 100644
--- a/components/contextual_search/internal/composebox_query_controller.cc
+++ b/components/contextual_search/internal/composebox_query_controller.cc
@@ -21,6 +21,7 @@
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
 #include "components/contextual_tasks/public/features.h"
+#include "components/contextual_tasks/public/utils.h"
 #include "components/lens/contextual_input.h"
 #include "components/lens/lens_features.h"
 #include "components/lens/lens_overlay_mime_type.h"
@@ -140,6 +141,36 @@
 
 namespace {
 
+// The maximum number of times to retry fetching cluster info.
+constexpr int kMaxClusterInfoRetries = 3;
+
+// The backoff policy for Contextual Tasks network requests.
+constexpr net::BackoffEntry::Policy kClusterInfoBackoffPolicy = {
+    // Number of initial errors (in sequence) to ignore before applying
+    // exponential back-off rules.
+    1,
+
+    // Initial delay for exponential back-off in ms.
+    500,
+
+    // Factor by which the waiting time will be multiplied.
+    2,
+
+    // Fuzzing percentage. ex: 10% will spread requests randomly
+    // between 90%-100% of the calculated time.
+    0.2,  // 20%
+
+    // Maximum amount of time we are willing to delay our request in ms.
+    10000,  // 10 seconds.
+
+    // Time to keep an entry from being discarded even when it
+    // has no significant state, -1 to never discard.
+    -1,
+
+    // Don't use initial delay unless the last request was an error.
+    false,
+};
+
 // Creates a payload for a contextual data upload request, for webpage contents
 // or for uploaded pdf files.
 lens::Payload CreateContentextualDataUploadPayload(
@@ -295,7 +326,8 @@
       channel_(channel),
       locale_(locale),
       template_url_service_(template_url_service),
-      variations_client_(variations_client) {
+      variations_client_(variations_client),
+      cluster_info_backoff_(&kClusterInfoBackoffPolicy) {
   send_lns_surface_ = feature_params->send_lns_surface;
   suppress_lns_surface_param_if_no_image_ =
       feature_params->suppress_lns_surface_param_if_no_image;
@@ -900,11 +932,31 @@
     }
   }
 
-  // Update the file upload status to processing.
   UpdateFileUploadStatus(file_token,
                          contextual_search::ContextUploadStatus::kProcessing,
                          std::nullopt);
 
+  if (query_controller_state_ == QueryControllerState::kClusterInfoInvalid) {
+    // If we've exhausted retries or are still in the backoff period, fail the
+    // context upload immediately.
+    if (cluster_info_retries_ >= kMaxClusterInfoRetries) {
+      UpdateFileUploadStatus(
+          file_token, contextual_search::ContextUploadStatus::kUploadFailed,
+          contextual_search::ContextUploadErrorType::kServerError);
+      return;
+    }
+
+    base::TimeDelta delay = cluster_info_backoff_.GetTimeUntilRelease();
+    if (delay.is_positive()) {
+      UpdateFileUploadStatus(
+          file_token, contextual_search::ContextUploadStatus::kUploadFailed,
+          contextual_search::ContextUploadErrorType::kServerError);
+      return;
+    }
+
+    FetchClusterInfo();
+  }
+
   // If the cluster info is available, update the file upload status to ready
   // for suggest.
   // If the file upload later fails due to
@@ -1140,7 +1192,10 @@
   cluster_info_access_token_fetcher_.reset();
   cluster_info_endpoint_fetcher_.reset();
   cluster_info_.reset();
+  cluster_info_retries_ = 0;
+  cluster_info_backoff_.Reset();
   request_id_generator_.ResetRequestId();
+  SetQueryControllerState(QueryControllerState::kOff);
 }
 
 void ComposeboxQueryController::ResetRequestClusterInfoState() {
@@ -1312,14 +1367,38 @@
 void ComposeboxQueryController::HandleClusterInfoResponse(
     std::unique_ptr<endpoint_fetcher::EndpointResponse> response) {
   cluster_info_endpoint_fetcher_.reset();
+
   if (response->http_status_code != google_apis::ApiErrorCode::HTTP_SUCCESS) {
+    ++cluster_info_retries_;
+
+    if (cluster_info_retries_ <= kMaxClusterInfoRetries) {
+      cluster_info_backoff_.InformOfRequest(false);
+    }
+
     SetQueryControllerState(QueryControllerState::kClusterInfoInvalid);
+
+    // Fail uploads that are waiting on cluster info.
+    std::vector<base::UnguessableToken> file_tokens_to_fail;
+    for (const auto& [file_token, file_info] : active_files_) {
+      if (file_info->upload_status ==
+          contextual_search::ContextUploadStatus::kProcessing) {
+        file_tokens_to_fail.push_back(file_token);
+      }
+    }
+    for (const auto& file_token : file_tokens_to_fail) {
+      UpdateFileUploadStatus(
+          file_token, contextual_search::ContextUploadStatus::kUploadFailed,
+          contextual_search::ContextUploadErrorType::kServerError);
+    }
+
     if (pending_search_url_request_) {
       std::move(pending_search_url_request_).Run(/*failure=*/false);
     }
     return;
   }
 
+  cluster_info_backoff_.Reset();
+
   lens::LensOverlayServerClusterInfoResponse server_response;
   if (!server_response.ParseFromString(response->response)) {
     SetQueryControllerState(QueryControllerState::kClusterInfoInvalid);
diff --git a/components/contextual_search/internal/composebox_query_controller.h b/components/contextual_search/internal/composebox_query_controller.h
index 4eb9575..a3976031 100644
--- a/components/contextual_search/internal/composebox_query_controller.h
+++ b/components/contextual_search/internal/composebox_query_controller.h
@@ -19,6 +19,7 @@
 #include "components/endpoint_fetcher/endpoint_fetcher.h"
 #include "components/lens/lens_overlay_request_id_generator.h"
 #include "components/lens/proto/server/lens_overlay_response.pb.h"
+#include "net/base/backoff_entry.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/lens_server_proto/added_inputs.pb.h"
 #include "third_party/lens_server_proto/aim_communication.pb.h"
@@ -510,6 +511,9 @@
   // The last received cluster info.
   std::optional<lens::LensOverlayClusterInfo> cluster_info_ = std::nullopt;
 
+  // The number of times fetching cluster info has failed.
+  int cluster_info_retries_ = 0;
+
   // The endpoint fetcher used for the cluster info request.
   std::unique_ptr<endpoint_fetcher::EndpointFetcher>
       cluster_info_endpoint_fetcher_;
@@ -544,6 +548,9 @@
   // Owned by the Profile, and thus guaranteed to outlive this instance.
   const raw_ptr<variations::VariationsClient> variations_client_;
 
+  // Backoff entry used to control the retry logic for the cluster info request.
+  net::BackoffEntry cluster_info_backoff_;
+
   // Whether or not to send the lns_surface parameter.
   // TODO(crbug.com/430070871): Remove this once the server supports the
   // `lns_surface` parameter.
diff --git a/components/contextual_search/internal/composebox_query_controller_unittest.cc b/components/contextual_search/internal/composebox_query_controller_unittest.cc
index ddb113f..007d3e9 100644
--- a/components/contextual_search/internal/composebox_query_controller_unittest.cc
+++ b/components/contextual_search/internal/composebox_query_controller_unittest.cc
@@ -287,7 +287,8 @@
       ContextUploadStatus expected_status =
           ContextUploadStatus::kUploadSuccessful,
       std::optional<ContextUploadErrorType> expected_error_type = std::nullopt,
-      bool expect_suggest_signals_ready = true) {
+      bool expect_suggest_signals_ready = true,
+      bool expect_upload_started = true) {
     ContextUploadStatusTuple processing_file_upload_status =
         file_upload_status_future_.Take();
     EXPECT_EQ(file_token, std::get<0>(processing_file_upload_status));
@@ -307,7 +308,8 @@
                 std::get<3>(processing_suggest_file_upload_status));
     }
 
-    if (expected_status != ContextUploadStatus::kValidationFailed) {
+    if (expected_status != ContextUploadStatus::kValidationFailed &&
+        expect_upload_started) {
       // For client-side validation failures, the state will never change to
       // kUploadStarted.
       ContextUploadStatusTuple upload_started_file_upload_status =
@@ -326,8 +328,11 @@
     EXPECT_EQ(expected_status, std::get<2>(final_file_upload_status));
     EXPECT_EQ(expected_error_type, std::get<3>(final_file_upload_status));
 
-    if (expected_status == ContextUploadStatus::kValidationFailed) {
-      // For client-side validation failures, the file upload request will not
+    if (expected_status == ContextUploadStatus::kValidationFailed ||
+        (!expect_upload_started &&
+         expected_status == ContextUploadStatus::kUploadFailed)) {
+      // For client-side validation failures or early upload failures
+      // (like failing to fetch cluster info), the file upload request will not
       // be sent.
       EXPECT_EQ(controller().num_file_upload_requests_sent(), 0);
     } else {
@@ -622,6 +627,141 @@
       /*expected_state=*/QueryControllerState::kClusterInfoInvalid);
 }
 
+TEST_F(ComposeboxQueryControllerTest, ClusterInfoFailureFailsActiveUploads) {
+  // Arrange: Simulate an error in the cluster info request.
+  controller().set_next_cluster_info_request_should_return_error(true);
+
+  // Act: Start the session.
+  controller().InitializeIfNeeded();
+
+  // Act: Start the file upload flow (this also triggers the cluster info
+  // fetch retry under the hood, but doesn't resolve immediately).
+  const base::UnguessableToken file_token = base::UnguessableToken::Create();
+  StartPdfFileUploadFlow(file_token,
+                         /*file_data=*/std::vector<uint8_t>());
+
+  // Assert: Validate cluster info request and state changes.
+  // The first fetch fails.
+  WaitForClusterInfo(
+      /*expected_state=*/QueryControllerState::kClusterInfoInvalid);
+
+  // The active file upload should fail with kServerError since cluster info
+  // couldn't be fetched.
+  WaitForFileUpload(
+      file_token, lens::MimeType::kPdf,
+      /*expected_status=*/ContextUploadStatus::kUploadFailed,
+      /*expected_error_type=*/ContextUploadErrorType::kServerError,
+      /*expect_suggest_signals_ready=*/false,
+      /*expect_upload_started=*/false);
+}
+
+TEST_F(ComposeboxQueryControllerTest, ClusterInfoFailureRetries) {
+  // Arrange: Simulate errors in the cluster info request.
+  controller().set_next_cluster_info_request_should_return_error(true);
+
+  // Act: Start the session.
+  controller().InitializeIfNeeded();
+  // Assert: Validate cluster info request and state changes.
+  // InitializeIfNeeded sets state to kAwaitingClusterInfoResponse then
+  // kClusterInfoInvalid.
+  WaitForClusterInfo(
+      /*expected_state=*/QueryControllerState::kClusterInfoInvalid,
+      /*fetch_request_count=*/1);
+
+  // Act: Start file uploads to trigger retries.
+  for (int i = 2; i <= 3; ++i) {
+    controller().set_next_cluster_info_request_should_return_error(true);
+    task_environment().FastForwardBy(base::Seconds(10));
+    const base::UnguessableToken file_token = base::UnguessableToken::Create();
+    // StartPdfFileUploadFlow will check if state is kClusterInfoInvalid.
+    // If fewer than 5 retries, it calls FetchClusterInfo, which sets
+    // state to kAwaitingClusterInfoResponse.
+    StartPdfFileUploadFlow(file_token, /*file_data=*/std::vector<uint8_t>());
+
+    // Validate cluster info request retried.
+    WaitForClusterInfo(
+        /*expected_state=*/QueryControllerState::kClusterInfoInvalid,
+        /*fetch_request_count=*/i);
+
+    // Validate the active file upload failed.
+    WaitForFileUpload(
+        file_token, lens::MimeType::kPdf,
+        /*expected_status=*/ContextUploadStatus::kUploadFailed,
+        /*expected_error_type=*/ContextUploadErrorType::kServerError,
+        /*expect_suggest_signals_ready=*/false,
+        /*expect_upload_started=*/false);
+  }
+
+  // Act: Start another file upload. This time, no fetch should be attempted.
+  // Set the next fetch request to succeed, if it happens.
+  controller().set_next_cluster_info_request_should_return_error(false);
+  const base::UnguessableToken last_file_token =
+      base::UnguessableToken::Create();
+  StartPdfFileUploadFlow(last_file_token, /*file_data=*/std::vector<uint8_t>());
+
+  // Validate the active file upload failed immediately.
+  WaitForFileUpload(
+      last_file_token, lens::MimeType::kPdf,
+      /*expected_status=*/ContextUploadStatus::kUploadFailed,
+      /*expected_error_type=*/ContextUploadErrorType::kServerError,
+      /*expect_suggest_signals_ready=*/false,
+      /*expect_upload_started=*/false);
+
+  // Assert: The number of fetch requests should still be 3
+  // (kMaxClusterInfoRetries).
+  EXPECT_EQ(controller().num_cluster_info_fetch_requests_sent(), 3);
+}
+
+TEST_F(ComposeboxQueryControllerTest, ClearClusterInfoResetsRetries) {
+  // Arrange: Simulate an error in the cluster info request.
+  controller().set_next_cluster_info_request_should_return_error(true);
+
+  // Act: Start the session.
+  controller().InitializeIfNeeded();
+
+  // Assert: Validate cluster info request and state changes.
+  WaitForClusterInfo(
+      /*expected_state=*/QueryControllerState::kClusterInfoInvalid,
+      /*fetch_request_count=*/1);
+
+  // Act: Exhaust retries.
+  for (int i = 2; i <= 3; ++i) {
+    controller().set_next_cluster_info_request_should_return_error(true);
+    task_environment().FastForwardBy(base::Seconds(10));
+    const base::UnguessableToken file_token = base::UnguessableToken::Create();
+    StartPdfFileUploadFlow(file_token, /*file_data=*/std::vector<uint8_t>());
+
+    // Validate cluster info request retried.
+    WaitForClusterInfo(
+        /*expected_state=*/QueryControllerState::kClusterInfoInvalid,
+        /*fetch_request_count=*/i);
+
+    // Validate the active file upload failed.
+    WaitForFileUpload(
+        file_token, lens::MimeType::kPdf,
+        /*expected_status=*/ContextUploadStatus::kUploadFailed,
+        /*expected_error_type=*/ContextUploadErrorType::kServerError,
+        /*expect_suggest_signals_ready=*/false,
+        /*expect_upload_started=*/false);
+  }
+
+  // Clear query controller info.
+  controller().ClearClusterInfo();
+  EXPECT_EQ(QueryControllerState::kOff, controller_state_future_.Take());
+
+  // Arrange: Simulate another error.
+  controller().set_next_cluster_info_request_should_return_error(true);
+
+  // Start the session again.
+  // Since retries were reset, a new fetch should be issued and fail.
+  controller().InitializeIfNeeded();
+
+  // Assert: Validate cluster info request and state changes.
+  WaitForClusterInfo(
+      /*expected_state=*/QueryControllerState::kClusterInfoInvalid,
+      /*fetch_request_count=*/4);
+}
+
 TEST_F(ComposeboxQueryControllerTest, UploadFileRequestFailure) {
   // Act: Start the session.
   controller().InitializeIfNeeded();
@@ -1186,6 +1326,17 @@
             std::get<2>(processing_file_upload_status));
   EXPECT_EQ(std::nullopt, std::get<3>(processing_file_upload_status));
 
+  // The active file upload should fail with kServerError since cluster info
+  // couldn't be fetched.
+  ContextUploadStatusTuple final_file_upload_status =
+      file_upload_status_future_.Take();
+  EXPECT_EQ(file_token, std::get<0>(final_file_upload_status));
+  EXPECT_EQ(lens::MimeType::kPdf, std::get<1>(final_file_upload_status));
+  EXPECT_EQ(ContextUploadStatus::kUploadFailed,
+            std::get<2>(final_file_upload_status));
+  EXPECT_EQ(ContextUploadErrorType::kServerError,
+            std::get<3>(final_file_upload_status));
+
   // Assert: file_upload_status_future_ is empty.
   EXPECT_TRUE(file_upload_status_future_.IsEmpty());
 }
@@ -1350,12 +1501,14 @@
   WaitForFileUpload(file_token, lens::MimeType::kPdf);
 
   // Act: Fast forward time to expire the cluster info.
-  // The default cluster info lifetime is 1 hour.
-  task_environment().FastForwardBy(base::Hours(1) + base::Seconds(1));
+  // The default cluster info lifetime is 30 minutes.
+  task_environment().FastForwardBy(base::Minutes(30) + base::Seconds(1));
 
   // Assert: Validate cluster info request and state changes.
   // The cluster info should be re-fetched.
-  // First, the state becomes kClusterInfoInvalid.
+  // First, the cluster info is cleared (state becomes kOff).
+  EXPECT_EQ(QueryControllerState::kOff, controller_state_future_.Take());
+  // Then, the state becomes kClusterInfoInvalid.
   EXPECT_EQ(QueryControllerState::kClusterInfoInvalid,
             controller_state_future_.Take());
   // Then, it starts fetching and becomes kAwaitingClusterInfoResponse.
@@ -1365,7 +1518,7 @@
   EXPECT_EQ(QueryControllerState::kClusterInfoReceived,
             controller_state_future_.Take());
 
-  EXPECT_GE(controller().num_cluster_info_fetch_requests_sent(), 2);
+  EXPECT_EQ(controller().num_cluster_info_fetch_requests_sent(), 2);
 
   // Assert: The file should still be in the active files map.
   ASSERT_TRUE(controller().GetFileInfoForTesting(file_token));
diff --git a/components/contextual_search/internal/test_composebox_query_controller.h b/components/contextual_search/internal/test_composebox_query_controller.h
index c536069b..814ee33 100644
--- a/components/contextual_search/internal/test_composebox_query_controller.h
+++ b/components/contextual_search/internal/test_composebox_query_controller.h
@@ -76,6 +76,8 @@
       bool enable_cluster_info_ttl);
   ~TestComposeboxQueryController() override;
 
+  using ComposeboxQueryController::ClearClusterInfo;
+
   // Mutators.
   void set_fake_cluster_info_response(
       lens::LensOverlayServerClusterInfoResponse response) {
diff --git a/components/contextual_tasks/internal/ai_thread_sync_bridge.cc b/components/contextual_tasks/internal/ai_thread_sync_bridge.cc
index b3feb80..7092d5d 100644
--- a/components/contextual_tasks/internal/ai_thread_sync_bridge.cc
+++ b/components/contextual_tasks/internal/ai_thread_sync_bridge.cc
@@ -5,6 +5,7 @@
 #include "components/contextual_tasks/internal/ai_thread_sync_bridge.h"
 
 #include "base/functional/callback_helpers.h"
+#include "components/sync/base/deletion_origin.h"
 #include "components/sync/model/data_batch.h"
 #include "components/sync/model/in_memory_metadata_change_list.h"
 #include "components/sync/model/mutable_data_batch.h"
@@ -208,6 +209,26 @@
   return threads;
 }
 
+void AiThreadSyncBridge::DeleteThread(const Thread& thread) {
+  if (!change_processor()->IsTrackingMetadata()) {
+    return;
+  }
+  std::unique_ptr<syncer::DataTypeStore::WriteBatch> batch =
+      data_type_store_->CreateWriteBatch();
+
+  change_processor()->Delete(thread.server_id,
+                             syncer::DeletionOrigin::Unspecified(),
+                             batch->GetMetadataChangeList());
+
+  ai_thread_entities_.erase(thread.server_id);
+  batch->DeleteData(thread.server_id);
+
+  data_type_store_->CommitWriteBatch(
+      std::move(batch),
+      base::BindOnce(&AiThreadSyncBridge::OnDataTypeStoreCommit,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
 void AiThreadSyncBridge::AddObserver(Observer* observer) {
   observers_.AddObserver(observer);
 }
diff --git a/components/contextual_tasks/internal/ai_thread_sync_bridge.h b/components/contextual_tasks/internal/ai_thread_sync_bridge.h
index 8ebf3cc..e1a4cd8 100644
--- a/components/contextual_tasks/internal/ai_thread_sync_bridge.h
+++ b/components/contextual_tasks/internal/ai_thread_sync_bridge.h
@@ -81,6 +81,9 @@
   // Returns all threads.
   virtual std::vector<Thread> GetThreads() const;
 
+  // Delete the thread.
+  void DeleteThread(const Thread& thread);
+
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
diff --git a/components/contextual_tasks/internal/ai_thread_sync_bridge_unittest.cc b/components/contextual_tasks/internal/ai_thread_sync_bridge_unittest.cc
index 0e7ff55..dd73a772 100644
--- a/components/contextual_tasks/internal/ai_thread_sync_bridge_unittest.cc
+++ b/components/contextual_tasks/internal/ai_thread_sync_bridge_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/test/task_environment.h"
 #include "components/sync/model/data_batch.h"
 #include "components/sync/model/data_type_store.h"
+#include "components/sync/protocol/ai_thread_specifics.pb.h"
 #include "components/sync/test/data_type_store_test_util.h"
 #include "components/sync/test/mock_data_type_local_change_processor.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -18,6 +19,11 @@
 
 namespace {
 
+const char kInitServerId[] = "00000000-0000-0000-0000-00000000001";
+const char kInitConversationId[] = "init_conversation_id";
+const char kInitTitle[] = "init_title";
+const int64_t kInitLastTurnTimeUnixEpochMillis = 52;
+
 using testing::_;
 using testing::ReturnRef;
 
@@ -26,15 +32,62 @@
   void SetUp() override {
     ON_CALL(mock_processor_, GetPossiblyTrimmedRemoteSpecifics(_))
         .WillByDefault(ReturnRef(sync_pb::EntitySpecifics::default_instance()));
+    ON_CALL(mock_processor_, IsTrackingMetadata())
+        .WillByDefault(testing::Return(true));
     bridge_ = std::make_unique<AiThreadSyncBridge>(
         mock_processor_.CreateForwardingProcessor(),
-        syncer::DataTypeStoreTestUtil::FactoryForInMemoryStoreForTest());
+        syncer::DataTypeStoreTestUtil::FactoryForForwardingStore(store_.get()));
   }
 
  protected:
   base::test::TaskEnvironment task_environment_;
   testing::NiceMock<syncer::MockDataTypeLocalChangeProcessor> mock_processor_;
   std::unique_ptr<AiThreadSyncBridge> bridge_;
+  std::unique_ptr<syncer::DataTypeStore> store_ =
+      syncer::DataTypeStoreTestUtil::CreateInMemoryStoreForTest();
+};
+
+class AiThreadSyncBridgeTestWithInitSpecificsTest
+    : public AiThreadSyncBridgeTest {
+ public:
+  void SetUp() override {
+    AiThreadSyncBridgeTest::SetUp();
+    AddSpecifics(kInitServerId, kInitTitle, kInitConversationId,
+                 kInitLastTurnTimeUnixEpochMillis);
+  }
+
+  void AddSpecifics(const std::string& server_id,
+                    const std::string& title,
+                    const std::string& conversation_turn_id,
+                    int64_t last_turn_time) {
+    syncer::EntityChangeList add_changes;
+    syncer::EntityData entity_data;
+    entity_data.specifics.mutable_ai_thread()->set_server_id(server_id);
+    entity_data.specifics.mutable_ai_thread()->set_title(title);
+    entity_data.specifics.mutable_ai_thread()->set_conversation_turn_id(
+        conversation_turn_id);
+    entity_data.specifics.mutable_ai_thread()
+        ->set_last_turn_time_unix_epoch_millis(last_turn_time);
+
+    add_changes.push_back(
+        syncer::EntityChange::CreateAdd(server_id, std::move(entity_data)));
+
+    std::optional<syncer::ModelError> error =
+        bridge_->ApplyIncrementalSyncChanges(
+            bridge_->CreateMetadataChangeList(), std::move(add_changes));
+    EXPECT_FALSE(error);
+  }
+
+  void VerifyInitSpecifics() {
+    EXPECT_EQ(1u, bridge_->GetThreads().size());
+    EXPECT_EQ(kInitServerId, bridge_->GetThreads()[0].server_id);
+    EXPECT_EQ(kInitConversationId,
+              bridge_->GetThreads()[0].conversation_turn_id);
+    EXPECT_EQ(kInitTitle, bridge_->GetThreads()[0].title);
+    EXPECT_EQ(
+        kInitLastTurnTimeUnixEpochMillis,
+        bridge_->GetThreads()[0].last_turn_time.InMillisecondsSinceUnixEpoch());
+  }
 };
 
 TEST_F(AiThreadSyncBridgeTest, GetDataForCommit) {
@@ -229,6 +282,24 @@
       43);
 }
 
+TEST_F(AiThreadSyncBridgeTestWithInitSpecificsTest, RemoveThread) {
+  VerifyInitSpecifics();
+  AddSpecifics("00000000-0000-0000-0000-00000000002", "new_title",
+               "new_conversation_id", 999);
+  EXPECT_EQ(2u, bridge_->GetThreads().size());
+  int new_idx = bridge_->GetThreads()[0].server_id ==
+                        "00000000-0000-0000-0000-00000000002"
+                    ? 0
+                    : 1;
+  EXPECT_EQ("new_title", bridge_->GetThreads()[new_idx].title);
+  EXPECT_EQ("new_conversation_id",
+            bridge_->GetThreads()[new_idx].conversation_turn_id);
+  EXPECT_EQ(999, bridge_->GetThreads()[new_idx]
+                     .last_turn_time.InMillisecondsSinceUnixEpoch());
+  bridge_->DeleteThread(bridge_->GetThreads()[new_idx]);
+  EXPECT_EQ(1u, bridge_->GetThreads().size());
+}
+
 }  // namespace
 
 }  // namespace contextual_tasks
diff --git a/components/contextual_tasks/internal/contextual_tasks_service_impl.cc b/components/contextual_tasks/internal/contextual_tasks_service_impl.cc
index 7aa5bff6..b32295d6 100644
--- a/components/contextual_tasks/internal/contextual_tasks_service_impl.cc
+++ b/components/contextual_tasks/internal/contextual_tasks_service_impl.cc
@@ -11,6 +11,7 @@
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/notreached.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/uuid.h"
 #include "components/contextual_search/contextual_search_service.h"
@@ -333,6 +334,19 @@
     const std::string& server_id) {
   auto it = tasks_.find(task_id);
   if (it != tasks_.end()) {
+    DCHECK(it->second.GetThread().has_value());
+    switch (type) {
+      case ThreadType::kAiMode:
+        ai_thread_sync_bridge_->DeleteThread(it->second.GetThread().value());
+        break;
+      case ThreadType::kGemini:
+        gemini_thread_sync_bridge_->DeleteThread(
+            it->second.GetThread().value());
+        break;
+      case ThreadType::kUnknown:
+      default:
+        NOTREACHED();
+    }
     it->second.RemoveThread(type, server_id);
     // If the task no longer has any thread, remove it.
     if (!it->second.GetThread()) {
diff --git a/components/contextual_tasks/internal/gemini_thread_sync_bridge.cc b/components/contextual_tasks/internal/gemini_thread_sync_bridge.cc
index d78aeb96..26385ec 100644
--- a/components/contextual_tasks/internal/gemini_thread_sync_bridge.cc
+++ b/components/contextual_tasks/internal/gemini_thread_sync_bridge.cc
@@ -7,6 +7,7 @@
 #include "base/functional/callback_helpers.h"
 #include "base/notimplemented.h"
 #include "base/uuid.h"
+#include "components/sync/base/deletion_origin.h"
 #include "components/sync/model/data_batch.h"
 #include "components/sync/model/in_memory_metadata_change_list.h"
 #include "components/sync/model/mutable_data_batch.h"
@@ -234,6 +235,25 @@
   return threads;
 }
 
+void GeminiThreadSyncBridge::DeleteThread(const Thread& thread) {
+  if (!change_processor()->IsTrackingMetadata()) {
+    return;
+  }
+  std::unique_ptr<syncer::DataTypeStore::WriteBatch> batch =
+      data_type_store_->CreateWriteBatch();
+  change_processor()->Delete(thread.server_id,
+                             syncer::DeletionOrigin::Unspecified(),
+                             batch->GetMetadataChangeList());
+
+  gemini_thread_specifics_.erase(thread.server_id);
+  batch->DeleteData(thread.server_id);
+
+  data_type_store_->CommitWriteBatch(
+      std::move(batch),
+      base::BindOnce(&GeminiThreadSyncBridge::OnDataTypeStoreCommit,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
 void GeminiThreadSyncBridge::AddObserver(Observer* observer) {
   observers_.AddObserver(observer);
 }
diff --git a/components/contextual_tasks/internal/gemini_thread_sync_bridge.h b/components/contextual_tasks/internal/gemini_thread_sync_bridge.h
index 3c9c6ec..bb9761c 100644
--- a/components/contextual_tasks/internal/gemini_thread_sync_bridge.h
+++ b/components/contextual_tasks/internal/gemini_thread_sync_bridge.h
@@ -76,6 +76,9 @@
   // Returns all threads.
   virtual std::vector<Thread> GetThreads() const;
 
+  // Delete the thread.
+  void DeleteThread(const Thread& thread);
+
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
diff --git a/components/contextual_tasks/internal/gemini_thread_sync_bridge_unittest.cc b/components/contextual_tasks/internal/gemini_thread_sync_bridge_unittest.cc
index c8e62fa..10a122b 100644
--- a/components/contextual_tasks/internal/gemini_thread_sync_bridge_unittest.cc
+++ b/components/contextual_tasks/internal/gemini_thread_sync_bridge_unittest.cc
@@ -137,8 +137,10 @@
   void SetUp() override {
     store_ = syncer::DataTypeStoreTestUtil::CreateInMemoryStoreForTest();
     AddInitialSpecifics();
+    ON_CALL(processor_, IsTrackingMetadata())
+        .WillByDefault(testing::Return(true));
     bridge_ = std::make_unique<GeminiThreadSyncBridge>(
-        mock_processor_.CreateForwardingProcessor(),
+        processor_.CreateForwardingProcessor(),
         syncer::DataTypeStoreTestUtil::FactoryForForwardingStore(store_.get()));
     base::RunLoop run_loop;
     bridge_->AddObserver(&observer_);
@@ -175,6 +177,15 @@
     return data;
   }
 
+  bool ThreadExists(const std::string& server_id) {
+    for (const auto& thread : bridge_->GetThreads()) {
+      if (server_id == thread.server_id) {
+        return true;
+      }
+    }
+    return false;
+  }
+
  private:
   void AddInitialSpecifics() {
     std::unique_ptr<syncer::DataTypeStore::WriteBatch> batch =
@@ -204,6 +215,7 @@
     loop.Run();
   }
 
+  testing::NiceMock<syncer::MockDataTypeLocalChangeProcessor> processor_;
   std::unique_ptr<syncer::DataTypeStore> store_;
   testing::NiceMock<MockObserver> observer_;
 };
@@ -303,6 +315,29 @@
             threads[0].last_turn_time.InMillisecondsSinceUnixEpoch());
 }
 
+TEST_F(GeminiThreadSyncBridgeWithInitSpecificsTest, RemoveThread) {
+  EXPECT_FALSE(ThreadExists("00000000-0000-0000-0000-000000000003"));
+  syncer::EntityChangeList entity_change_list;
+  syncer::EntityData data =
+      CreateEntityData("00000000-0000-0000-0000-000000000003", "new_title", 52);
+  std::string storage_key = bridge()->GetStorageKey(data);
+  entity_change_list.push_back(
+      syncer::EntityChange::CreateAdd(storage_key, std::move(data)));
+  bridge()->ApplyIncrementalSyncChanges(bridge()->CreateMetadataChangeList(),
+                                        std::move(entity_change_list));
+  EXPECT_TRUE(ThreadExists("00000000-0000-0000-0000-000000000003"));
+  int new_idx = bridge()->GetThreads()[0].server_id ==
+                        "00000000-0000-0000-0000-000000000003"
+                    ? 0
+                    : 1;
+  EXPECT_EQ(52, bridge()
+                    ->GetThreads()[new_idx]
+                    .last_turn_time.InMillisecondsSinceUnixEpoch());
+  EXPECT_EQ("new_title", bridge()->GetThreads()[new_idx].title);
+  bridge()->DeleteThread(bridge()->GetThreads()[new_idx]);
+  EXPECT_FALSE(ThreadExists("00000000-0000-0000-0000-000000000003"));
+}
+
 }  // namespace
 
 }  // namespace contextual_tasks
diff --git a/components/contextual_tasks/public/features.cc b/components/contextual_tasks/public/features.cc
index 05fcc20..88c9a2ad 100644
--- a/components/contextual_tasks/public/features.cc
+++ b/components/contextual_tasks/public/features.cc
@@ -55,8 +55,6 @@
 BASE_FEATURE(kEnableNotifyZeroStateRenderedCapability,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kContextualTasksExpandButton, base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kContextualTasksSendFullVersionListEnabled,
              base::FEATURE_ENABLED_BY_DEFAULT);
 
@@ -492,12 +490,6 @@
 const char kContextualTasksContextLibraryDescription[] =
     "Enables integration with the server side context library.";
 
-const char kContextualTasksExpandButtonName[] =
-    "Contextual Tasks Expand Button";
-const char kContextualTasksExpandButtonDescription[] =
-    "Replace the overflow menu in the side panel with a button to move the "
-    "thread to a new tab.";
-
 const char kContextualTasksSuggestionsEnabledName[] =
     "Contextual Tasks Suggestions Enabled";
 const char kContextualTasksSuggestionsEnabledDescription[] =
diff --git a/components/contextual_tasks/public/features.h b/components/contextual_tasks/public/features.h
index 243322e..af8592f 100644
--- a/components/contextual_tasks/public/features.h
+++ b/components/contextual_tasks/public/features.h
@@ -43,10 +43,6 @@
 // param.
 BASE_DECLARE_FEATURE(kEnableNotifyZeroStateRenderedCapability);
 
-// Replace the overflow menu in the side panel with an explicit button to move
-// the thread to a new tab.
-BASE_DECLARE_FEATURE(kContextualTasksExpandButton);
-
 // If enabled, adds the Sec-CH-UA-Full-Version-List header to all network
 // requests initiated from within an embedded Co-Browse <webview>.
 BASE_DECLARE_FEATURE(kContextualTasksSendFullVersionListEnabled);
@@ -274,8 +270,6 @@
 extern const char kContextualTasksContextLibraryDescription[];
 extern const char kContextualTasksContextName[];
 extern const char kContextualTasksContextDescription[];
-extern const char kContextualTasksExpandButtonName[];
-extern const char kContextualTasksExpandButtonDescription[];
 extern const char kContextualTasksSuggestionsEnabledName[];
 extern const char kContextualTasksSuggestionsEnabledDescription[];
 
diff --git a/components/crash/android/crash_keys_android.cc b/components/crash/android/crash_keys_android.cc
index b3de9a0..770f433 100644
--- a/components/crash/android/crash_keys_android.cc
+++ b/components/crash/android/crash_keys_android.cc
@@ -5,10 +5,12 @@
 #include "components/crash/android/crash_keys_android.h"
 
 #include <array>
+#include <optional>
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "components/crash/core/common/crash_key.h"
+#include "third_party/jni_zero/default_conversions.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
 #include "components/crash/android/jni_headers/CrashKeys_jni.h"
@@ -56,21 +58,19 @@
   Java_CrashKeys_flushToNative(env, jinstance);
 }
 
-static void JNI_CrashKeys_Set(JNIEnv* env,
-                              int32_t key,
-                              const base::android::JavaRef<jstring>& value) {
-  if (value.is_null()) {
+static void JNI_CrashKeys_Set(int32_t key,
+                              const std::optional<std::string>& value) {
+  if (!value) {
     if (key == static_cast<int32_t>(CrashKeyIndex::INSTALLED_MODULES)) {
       g_installed_modules_key.Clear();
     } else {
       GetCrashKey(key).Clear();
     }
   } else {
-    std::string val = base::android::ConvertJavaStringToUTF8(env, value);
     if (key == static_cast<int32_t>(CrashKeyIndex::INSTALLED_MODULES)) {
-      g_installed_modules_key.Set(val);
+      g_installed_modules_key.Set(*value);
     } else {
-      GetCrashKey(key).Set(val);
+      GetCrashKey(key).Set(*value);
     }
   }
 }
diff --git a/components/crash/android/java/src/org/chromium/components/crash/CrashKeys.java b/components/crash/android/java/src/org/chromium/components/crash/CrashKeys.java
index 8b9d810..e38ecf7e 100644
--- a/components/crash/android/java/src/org/chromium/components/crash/CrashKeys.java
+++ b/components/crash/android/java/src/org/chromium/components/crash/CrashKeys.java
@@ -5,6 +5,7 @@
 package org.chromium.components.crash;
 
 import org.jni_zero.CalledByNative;
+import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
 import org.chromium.base.ThreadUtils;
@@ -106,6 +107,6 @@
 
     @NativeMethods
     interface Natives {
-        void set(int key, @Nullable String value);
+        void set(int key, @JniType("std::optional<std::string>") @Nullable String value);
     }
 }
diff --git a/components/data_sharing/internal/android/data_sharing_service_android.cc b/components/data_sharing/internal/android/data_sharing_service_android.cc
index 1c86b12f..ca2fcb21 100644
--- a/components/data_sharing/internal/android/data_sharing_service_android.cc
+++ b/components/data_sharing/internal/android/data_sharing_service_android.cc
@@ -31,8 +31,6 @@
 #include "components/data_sharing/internal/jni_headers/ObserverBridge_jni.h"
 
 using base::android::AttachCurrentThread;
-using base::android::ConvertJavaStringToUTF8;
-using base::android::ConvertUTF8ToJavaString;
 using base::android::JavaRef;
 using base::android::RunObjectCallbackAndroid;
 using base::android::ScopedJavaGlobalRef;
@@ -130,8 +128,7 @@
 void DataSharingServiceAndroid::GroupDataObserverBridge::OnGroupRemoved(
     const GroupId& group_id, const base::Time& event_time) {
   JNIEnv* env = AttachCurrentThread();
-  Java_ObserverBridge_onGroupRemoved(
-      env, java_obj_, ConvertUTF8ToJavaString(env, group_id.value()));
+  Java_ObserverBridge_onGroupRemoved(env, java_obj_, group_id.value());
 }
 
 // This function is declared in data_sharing_service.h and
@@ -169,79 +166,67 @@
   Java_DataSharingServiceImpl_clearNativePtr(env, java_obj_);
 }
 
-void DataSharingServiceAndroid::ReadGroup(JNIEnv* env,
-                                          const JavaRef<jstring>& group_id,
+void DataSharingServiceAndroid::ReadGroup(const std::string& group_id,
                                           const JavaRef<jobject>& j_callback) {
   // TODO(crbug.com/382033539): migrate android implementation to use
   // synchronous ReadGroup().
   data_sharing_service_->ReadGroupDeprecated(
-      GroupId(ConvertJavaStringToUTF8(env, group_id)),
+      GroupId(group_id),
       base::BindOnce(&RunGroupDataOrFailureOutcomeCallback,
                      ScopedJavaGlobalRef<jobject>(j_callback)));
 }
 
 void DataSharingServiceAndroid::CreateGroup(
-    JNIEnv* env,
-    const JavaRef<jstring>& group_name,
+    const std::string& group_name,
     const JavaRef<jobject>& j_callback) {
   data_sharing_service_->CreateGroup(
-      ConvertJavaStringToUTF8(env, group_name),
-      base::BindOnce(&RunGroupDataOrFailureOutcomeCallback,
-                     ScopedJavaGlobalRef<jobject>(j_callback)));
+      group_name, base::BindOnce(&RunGroupDataOrFailureOutcomeCallback,
+                                 ScopedJavaGlobalRef<jobject>(j_callback)));
 }
 
 void DataSharingServiceAndroid::InviteMember(
-    JNIEnv* env,
-    const JavaRef<jstring>& group_id,
-    const JavaRef<jstring>& invitee_email,
+    const std::string& group_id,
+    const std::string& invitee_email,
     const JavaRef<jobject>& j_callback) {
   data_sharing_service_->InviteMember(
-      GroupId(ConvertJavaStringToUTF8(env, group_id)),
-      ConvertJavaStringToUTF8(env, invitee_email),
+      GroupId(group_id), invitee_email,
       base::BindOnce(&RunPeopleGroupActionOutcomeCallback,
                      ScopedJavaGlobalRef<jobject>(j_callback)));
 }
 
-void DataSharingServiceAndroid::AddMember(JNIEnv* env,
-                                          const JavaRef<jstring>& group_id,
-                                          const JavaRef<jstring>& access_token,
+void DataSharingServiceAndroid::AddMember(const std::string& group_id,
+                                          const std::string& access_token,
                                           const JavaRef<jobject>& j_callback) {
   data_sharing_service_->AddMember(
-      GroupId(ConvertJavaStringToUTF8(env, group_id)),
-      ConvertJavaStringToUTF8(env, access_token),
+      GroupId(group_id), access_token,
       base::BindOnce(&RunPeopleGroupActionOutcomeCallback,
                      ScopedJavaGlobalRef<jobject>(j_callback)));
 }
 
 void DataSharingServiceAndroid::RemoveMember(
-    JNIEnv* env,
-    const JavaRef<jstring>& group_id,
-    const JavaRef<jstring>& member_email,
+    const std::string& group_id,
+    const std::string& member_email,
     const JavaRef<jobject>& j_callback) {
   data_sharing_service_->RemoveMember(
-      GroupId(ConvertJavaStringToUTF8(env, group_id)),
-      ConvertJavaStringToUTF8(env, member_email),
+      GroupId(group_id), member_email,
       base::BindOnce(&RunPeopleGroupActionOutcomeCallback,
                      ScopedJavaGlobalRef<jobject>(j_callback)));
 }
 
-bool DataSharingServiceAndroid::IsEmptyService(JNIEnv* env) {
+bool DataSharingServiceAndroid::IsEmptyService() {
   return data_sharing_service_->IsEmptyService();
 }
 
-ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetNetworkLoader(
-    JNIEnv* env) {
+ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetNetworkLoader() {
   return network_loader_->GetJavaObject();
 }
 
 ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetDataSharingUrl(
     JNIEnv* env,
-    const JavaRef<jstring>& j_group_id,
-    const JavaRef<jstring>& j_access_token) {
+    const std::string& group_id,
+    const std::string& access_token) {
   // Note that this function is only passing the required fields to the native
   // service.
-  std::string group_id = ConvertJavaStringToUTF8(env, j_group_id);
-  std::string access_token = ConvertJavaStringToUTF8(env, j_access_token);
   std::unique_ptr<GURL> url = data_sharing_service_->GetDataSharingUrl(
       GroupData(GroupId(group_id), /*display_name*/ "", /*members*/ {},
                 /*former_members*/ {}, access_token));
@@ -262,39 +247,33 @@
 }
 
 void DataSharingServiceAndroid::EnsureGroupVisibility(
-    JNIEnv* env,
-    const JavaRef<jstring>& group_id,
+    const std::string& group_id,
     const JavaRef<jobject>& j_callback) {
   data_sharing_service_->EnsureGroupVisibility(
-      GroupId(ConvertJavaStringToUTF8(env, group_id)),
+      GroupId(group_id),
       base::BindOnce(&RunGroupDataOrFailureOutcomeCallback,
                      ScopedJavaGlobalRef<jobject>(j_callback)));
 }
 
 void DataSharingServiceAndroid::GetSharedEntitiesPreview(
-    JNIEnv* env,
-    const JavaRef<jstring>& group_id,
-    const JavaRef<jstring>& access_token,
+    const std::string& group_id,
+    const std::string& access_token,
     const JavaRef<jobject>& j_callback) {
   data_sharing_service_->GetSharedEntitiesPreview(
-      GroupToken(GroupId(ConvertJavaStringToUTF8(env, group_id)),
-                 ConvertJavaStringToUTF8(env, access_token)),
+      GroupToken(GroupId(group_id), access_token),
       base::BindOnce(&RunSharedDataPreviewOrFailureOutcomeCallback,
                      ScopedJavaGlobalRef<jobject>(j_callback)));
 }
 
-ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetUiDelegate(
-    JNIEnv* env) {
+ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetUiDelegate() {
   return data_sharing_service_->GetUiDelegate()->GetJavaObject();
 }
 
 void DataSharingServiceAndroid::Log(
-    JNIEnv* env,
     /*logger_common::mojom::LogSource*/ int32_t source,
-    const JavaRef<jstring>& message) {
+    const std::string& message) {
   DATA_SHARING_LOG(static_cast<logger_common::mojom::LogSource>(source),
-                   data_sharing_service_->GetLogger(),
-                   ConvertJavaStringToUTF8(env, message));
+                   data_sharing_service_->GetLogger(), message);
 }
 
 ScopedJavaLocalRef<jobject> DataSharingServiceAndroid::GetJavaObject() {
@@ -309,17 +288,15 @@
 static ScopedJavaLocalRef<jobject>
 JNI_DataSharingServiceImpl_GetDataSharingUrlForTesting(
     JNIEnv* env,
-    const JavaRef<jstring>& j_group_id,
-    const JavaRef<jstring>& j_access_token) {
+    const std::string& group_id,
+    const std::string& access_token) {
   GURL url = *DataSharingServiceImpl::GetDataSharingUrl(
-      GroupToken(GroupId(ConvertJavaStringToUTF8(env, j_group_id)),
-                 ConvertJavaStringToUTF8(env, j_access_token)));
+      GroupToken(GroupId(group_id), access_token));
   return url::GURLAndroid::FromNativeGURL(env, url);
 }
 
 void DataSharingServiceAndroid::SetSharedEntitiesPreviewForTesting(
-    JNIEnv* env,
-    const JavaRef<jstring>& j_group_id) {
+    const std::string& group_id) {
   auto fake_proxy = std::make_unique<FakePreviewServerProxy>();
   data_sharing::SharedTabGroupPreview preview;
   preview.title = "my group title";
@@ -333,8 +310,8 @@
   data_sharing::DataSharingService::SharedDataPreviewOrFailureOutcome outcome =
       std::move(sharedDataPreview);
 
-  fake_proxy->SetSharedEntitiesPreviewForTesting(
-      GroupId(ConvertJavaStringToUTF8(j_group_id)), std::move(outcome));
+  fake_proxy->SetSharedEntitiesPreviewForTesting(GroupId(group_id),
+                                                 std::move(outcome));
 
   data_sharing_service_->SetPreviewServerProxyForTesting(std::move(fake_proxy));
 }
diff --git a/components/data_sharing/internal/android/data_sharing_service_android.h b/components/data_sharing/internal/android/data_sharing_service_android.h
index 1508b95..21e22c5 100644
--- a/components/data_sharing/internal/android/data_sharing_service_android.h
+++ b/components/data_sharing/internal/android/data_sharing_service_android.h
@@ -27,47 +27,38 @@
   ~DataSharingServiceAndroid() override;
 
   // DataSharingService Java API methods, implemented by native service:
-  void ReadGroup(JNIEnv* env,
-                 const JavaRef<jstring>& group_id,
+  void ReadGroup(const std::string& group_id,
                  const JavaRef<jobject>& j_callback);
-  void CreateGroup(JNIEnv* env,
-                   const JavaRef<jstring>& group_name,
+  void CreateGroup(const std::string& group_name,
                    const JavaRef<jobject>& j_callback);
-  void InviteMember(JNIEnv* env,
-                    const JavaRef<jstring>& group_id,
-                    const JavaRef<jstring>& invitee_email,
+  void InviteMember(const std::string& group_id,
+                    const std::string& invitee_email,
                     const JavaRef<jobject>& j_callback);
-  void AddMember(JNIEnv* env,
-                 const JavaRef<jstring>& group_id,
-                 const JavaRef<jstring>& access_token,
+  void AddMember(const std::string& group_id,
+                 const std::string& access_token,
                  const JavaRef<jobject>& j_callback);
-  void RemoveMember(JNIEnv* env,
-                    const JavaRef<jstring>& group_id,
-                    const JavaRef<jstring>& member_email,
+  void RemoveMember(const std::string& group_id,
+                    const std::string& member_email,
                     const JavaRef<jobject>& j_callback);
-  bool IsEmptyService(JNIEnv* env);
-  ScopedJavaLocalRef<jobject> GetNetworkLoader(JNIEnv* env);
+  bool IsEmptyService();
+  ScopedJavaLocalRef<jobject> GetNetworkLoader();
   ScopedJavaLocalRef<jobject> GetDataSharingUrl(
       JNIEnv* env,
-      const JavaRef<jstring>& group_id,
-      const JavaRef<jstring>& access_token);
+      const std::string& group_id,
+      const std::string& access_token);
   ScopedJavaLocalRef<jobject> ParseDataSharingUrl(
       JNIEnv* env,
       const JavaRef<jobject>& j_url);
-  void EnsureGroupVisibility(JNIEnv* env,
-                             const JavaRef<jstring>& group_id,
+  void EnsureGroupVisibility(const std::string& group_id,
                              const JavaRef<jobject>& j_callback);
-  void GetSharedEntitiesPreview(JNIEnv* env,
-                                const JavaRef<jstring>& group_id,
-                                const JavaRef<jstring>& access_token,
+  void GetSharedEntitiesPreview(const std::string& group_id,
+                                const std::string& access_token,
                                 const JavaRef<jobject>& j_callback);
-  ScopedJavaLocalRef<jobject> GetUiDelegate(JNIEnv* env);
-  void Log(JNIEnv* env,
-           /*logger_common::mojom::LogSource*/ int32_t source,
-           const JavaRef<jstring>& message);
+  ScopedJavaLocalRef<jobject> GetUiDelegate();
+  void Log(/*logger_common::mojom::LogSource*/ int32_t source,
+           const std::string& message);
 
-  void SetSharedEntitiesPreviewForTesting(JNIEnv* env,
-                                          const JavaRef<jstring>& j_group_id);
+  void SetSharedEntitiesPreviewForTesting(const std::string& group_id);
 
   // Returns the DataSharingServiceImpl java object.
   ScopedJavaLocalRef<jobject> GetJavaObject();
diff --git a/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/DataSharingServiceImpl.java b/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/DataSharingServiceImpl.java
index d80e4659..8c0c685 100644
--- a/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/DataSharingServiceImpl.java
+++ b/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/DataSharingServiceImpl.java
@@ -8,6 +8,7 @@
 
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
+import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
 import org.chromium.base.Callback;
@@ -182,30 +183,30 @@
     interface Natives {
         void readGroup(
                 long nativeDataSharingServiceAndroid,
-                String groupId,
+                @JniType("std::string") String groupId,
                 Callback<GroupDataOrFailureOutcome> callback);
 
         void createGroup(
                 long nativeDataSharingServiceAndroid,
-                String groupName,
+                @JniType("std::string") String groupName,
                 Callback<GroupDataOrFailureOutcome> callback);
 
         void inviteMember(
                 long nativeDataSharingServiceAndroid,
-                String groupId,
-                String inviteeEmail,
+                @JniType("std::string") String groupId,
+                @JniType("std::string") String inviteeEmail,
                 Callback<Integer> callback);
 
         void addMember(
                 long nativeDataSharingServiceAndroid,
-                String groupId,
-                String accessToken,
+                @JniType("std::string") String groupId,
+                @JniType("std::string") String accessToken,
                 Callback<Integer> callback);
 
         void removeMember(
                 long nativeDataSharingServiceAndroid,
-                String groupId,
-                String memberEmail,
+                @JniType("std::string") String groupId,
+                @JniType("std::string") String memberEmail,
                 Callback<Integer> callback);
 
         boolean isEmptyService(long nativeDataSharingServiceAndroid);
@@ -213,29 +214,35 @@
         DataSharingNetworkLoader getNetworkLoader(long nativeDataSharingServiceAndroid);
 
         GURL getDataSharingUrl(
-                long nativeDataSharingServiceAndroid, String groupId, String accessToken);
+                long nativeDataSharingServiceAndroid,
+                @JniType("std::string") String groupId,
+                @JniType("std::string") String accessToken);
 
         DataSharingService.ParseUrlResult parseDataSharingUrl(
                 long nativeDataSharingServiceAndroid, GURL url);
 
         void ensureGroupVisibility(
                 long nativeDataSharingServiceAndroid,
-                String groupId,
+                @JniType("std::string") String groupId,
                 Callback<GroupDataOrFailureOutcome> callback);
 
         void getSharedEntitiesPreview(
                 long nativeDataSharingServiceAndroid,
-                String groupId,
-                String accessToken,
+                @JniType("std::string") String groupId,
+                @JniType("std::string") String accessToken,
                 Callback<SharedDataPreviewOrFailureOutcome> callback);
 
         DataSharingUIDelegate getUiDelegate(long nativeDataSharingServiceAndroid);
 
-        void log(long nativeDataSharingServiceAndroid, int source, String message);
+        void log(
+                long nativeDataSharingServiceAndroid,
+                int source,
+                @JniType("std::string") String message);
 
-        GURL getDataSharingUrlForTesting(String groupId, String accessToken);
+        GURL getDataSharingUrlForTesting(
+                @JniType("std::string") String groupId, @JniType("std::string") String accessToken);
 
         void setSharedEntitiesPreviewForTesting(
-                long nativeDataSharingServiceAndroid, String groupId);
+                long nativeDataSharingServiceAndroid, @JniType("std::string") String groupId);
     }
 }
diff --git a/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/ObserverBridge.java b/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/ObserverBridge.java
index 0cb291a..7be4a18 100644
--- a/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/ObserverBridge.java
+++ b/components/data_sharing/internal/android/java/src/org/chromium/components/data_sharing/ObserverBridge.java
@@ -5,6 +5,7 @@
 package org.chromium.components.data_sharing;
 
 import org.jni_zero.CalledByNative;
+import org.jni_zero.JniType;
 
 import org.chromium.base.ObserverList;
 import org.chromium.build.annotations.NullMarked;
@@ -51,7 +52,7 @@
 
     @CalledByNative
     @Override
-    public void onGroupRemoved(String groupId) {
+    public void onGroupRemoved(@JniType("std::string") String groupId) {
         for (DataSharingService.Observer javaObserver : mJavaObservers) {
             javaObserver.onGroupRemoved(groupId);
         }
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc
index 2ae6d72..b73f6b2 100644
--- a/components/feature_engagement/public/feature_constants.cc
+++ b/components/feature_engagement/public/feature_constants.cc
@@ -1011,10 +1011,10 @@
              base::FEATURE_ENABLED_BY_DEFAULT);
 BASE_FEATURE(kIPHiOSTabGroupsDesktopFeature,
              "IPH_iOSTabGroupsDesktop",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 BASE_FEATURE(kIPHiOSPriceTrackingDesktopFeature,
              "IPH_iOSPriceTrackingDesktop",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 #if !BUILDFLAG(IS_IOS)
diff --git a/components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java b/components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java
index b919eb2..370ae28 100644
--- a/components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java
+++ b/components/gcm_driver/instance_id/android/java/src/org/chromium/components/gcm_driver/instance_id/InstanceIDBridge.java
@@ -6,6 +6,7 @@
 
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
+import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
 import org.chromium.base.task.AsyncTask;
@@ -45,7 +46,8 @@
      * share an underlying InstanceIDWithSubtype.
      */
     @CalledByNative
-    public static InstanceIDBridge create(long nativeInstanceIDAndroid, String subtype) {
+    public static InstanceIDBridge create(
+            long nativeInstanceIDAndroid, @JniType("std::string") String subtype) {
         return new InstanceIDBridge(nativeInstanceIDAndroid, subtype);
     }
 
@@ -104,14 +106,16 @@
     }
 
     /**
-     * Async wrapper for {@link InstanceID#getToken(String, String)}.
-     * |isLazy| isn't part of the InstanceID.getToken() call and not sent to the
-     * FCM server. It's used to mark the subscription as lazy such that incoming
-     * messages are deferred until there are visible activities.
+     * Async wrapper for {@link InstanceID#getToken(String, String)}. |isLazy| isn't part of the
+     * InstanceID.getToken() call and not sent to the FCM server. It's used to mark the subscription
+     * as lazy such that incoming messages are deferred until there are visible activities.
      */
     @CalledByNative
     private void getToken(
-            final int requestId, final String authorizedEntity, final String scope, int flags) {
+            final int requestId,
+            final @JniType("std::string") String authorizedEntity,
+            final @JniType("std::string") String scope,
+            int flags) {
         new BridgeAsyncTask<String>() {
             @Override
             protected String doBackgroundWork() {
@@ -142,7 +146,9 @@
     /** Async wrapper for {@link InstanceID#deleteToken(String, String)}. */
     @CalledByNative
     private void deleteToken(
-            final int requestId, final String authorizedEntity, final String scope) {
+            final int requestId,
+            final @JniType("std::string") String authorizedEntity,
+            final @JniType("std::string") String scope) {
         new BridgeAsyncTask<Boolean>() {
             @Override
             protected Boolean doBackgroundWork() {
@@ -248,11 +254,13 @@
 
     @NativeMethods
     interface Natives {
-        void didGetID(long nativeInstanceIDAndroid, int requestId, String id);
+        void didGetID(
+                long nativeInstanceIDAndroid, int requestId, @JniType("std::string") String id);
 
         void didGetCreationTime(long nativeInstanceIDAndroid, int requestId, long creationTime);
 
-        void didGetToken(long nativeInstanceIDAndroid, int requestId, String token);
+        void didGetToken(
+                long nativeInstanceIDAndroid, int requestId, @JniType("std::string") String token);
 
         void didDeleteToken(long nativeInstanceIDAndroid, int requestId, boolean success);
 
diff --git a/components/gcm_driver/instance_id/instance_id_android.cc b/components/gcm_driver/instance_id/instance_id_android.cc
index 1fc51cb..a2bf1dd 100644
--- a/components/gcm_driver/instance_id/instance_id_android.cc
+++ b/components/gcm_driver/instance_id/instance_id_android.cc
@@ -20,8 +20,6 @@
 #include "components/gcm_driver/instance_id/android/jni_headers/InstanceIDBridge_jni.h"
 
 using base::android::AttachCurrentThread;
-using base::android::ConvertJavaStringToUTF8;
-using base::android::ConvertUTF8ToJavaString;
 
 namespace instance_id {
 
@@ -57,9 +55,8 @@
   std::string subtype = app_id;
 
   JNIEnv* env = AttachCurrentThread();
-  java_ref_.Reset(
-      Java_InstanceIDBridge_create(env, reinterpret_cast<intptr_t>(this),
-                                   ConvertUTF8ToJavaString(env, subtype)));
+  java_ref_.Reset(Java_InstanceIDBridge_create(
+      env, reinterpret_cast<intptr_t>(this), subtype));
 }
 
 InstanceIDAndroid::~InstanceIDAndroid() {
@@ -110,10 +107,8 @@
       [](int sum, Flags flag) { return sum + static_cast<int>(flag); });
 
   JNIEnv* env = AttachCurrentThread();
-  Java_InstanceIDBridge_getToken(
-      env, java_ref_, request_id,
-      ConvertUTF8ToJavaString(env, authorized_entity),
-      ConvertUTF8ToJavaString(env, scope), java_flags);
+  Java_InstanceIDBridge_getToken(env, java_ref_, request_id, authorized_entity,
+                                 scope, java_flags);
 }
 
 void InstanceIDAndroid::ValidateToken(const std::string& authorized_entity,
@@ -134,10 +129,8 @@
       std::make_unique<DeleteTokenCallback>(std::move(callback)));
 
   JNIEnv* env = AttachCurrentThread();
-  Java_InstanceIDBridge_deleteToken(
-      env, java_ref_, request_id,
-      ConvertUTF8ToJavaString(env, authorized_entity),
-      ConvertUTF8ToJavaString(env, scope));
+  Java_InstanceIDBridge_deleteToken(env, java_ref_, request_id,
+                                    authorized_entity, scope);
 }
 
 void InstanceIDAndroid::DeleteIDImpl(DeleteIDCallback callback) {
@@ -152,12 +145,12 @@
 
 void InstanceIDAndroid::DidGetID(JNIEnv* env,
                                  int32_t request_id,
-                                 const base::android::JavaRef<jstring>& jid) {
+                                 const std::string& id) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   GetIDCallback* callback = get_id_callbacks_.Lookup(request_id);
   DCHECK(callback);
-  std::move(*callback).Run(ConvertJavaStringToUTF8(jid));
+  std::move(*callback).Run(id);
   get_id_callbacks_.Remove(request_id);
 }
 
@@ -181,15 +174,13 @@
   get_creation_time_callbacks_.Remove(request_id);
 }
 
-void InstanceIDAndroid::DidGetToken(
-    JNIEnv* env,
-    int32_t request_id,
-    const base::android::JavaRef<jstring>& jtoken) {
+void InstanceIDAndroid::DidGetToken(JNIEnv* env,
+                                    int32_t request_id,
+                                    const std::string& token) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   GetTokenCallback* callback = get_token_callbacks_.Lookup(request_id);
   DCHECK(callback);
-  std::string token = ConvertJavaStringToUTF8(jtoken);
   std::move(*callback).Run(
       token, token.empty() ? InstanceID::UNKNOWN_ERROR : InstanceID::SUCCESS);
   get_token_callbacks_.Remove(request_id);
diff --git a/components/gcm_driver/instance_id/instance_id_android.h b/components/gcm_driver/instance_id/instance_id_android.h
index 280c7e1..60d0940 100644
--- a/components/gcm_driver/instance_id/instance_id_android.h
+++ b/components/gcm_driver/instance_id/instance_id_android.h
@@ -65,15 +65,11 @@
   void DeleteIDImpl(DeleteIDCallback callback) override;
 
   // Methods called from Java via JNI:
-  void DidGetID(JNIEnv* env,
-                int32_t request_id,
-                const base::android::JavaRef<jstring>& jid);
+  void DidGetID(JNIEnv* env, int32_t request_id, const std::string& id);
   void DidGetCreationTime(JNIEnv* env,
                           int32_t request_id,
                           int64_t creation_time_unix_ms);
-  void DidGetToken(JNIEnv* env,
-                   int32_t request_id,
-                   const base::android::JavaRef<jstring>& jtoken);
+  void DidGetToken(JNIEnv* env, int32_t request_id, const std::string& token);
   void DidDeleteToken(JNIEnv* env, int32_t request_id, bool success);
   void DidDeleteID(JNIEnv* env, int32_t request_id, bool success);
 
diff --git a/components/javascript_dialogs/android/app_modal_dialog_view_android.cc b/components/javascript_dialogs/android/app_modal_dialog_view_android.cc
index 992453df..bc230fb4 100644
--- a/components/javascript_dialogs/android/app_modal_dialog_view_android.cc
+++ b/components/javascript_dialogs/android/app_modal_dialog_view_android.cc
@@ -49,10 +49,8 @@
   }
 
   ScopedJavaLocalRef<jobject> dialog_object;
-  ScopedJavaLocalRef<jstring> title =
-      ConvertUTF16ToJavaString(env, controller_->title());
-  ScopedJavaLocalRef<jstring> message =
-      ConvertUTF16ToJavaString(env, controller_->message_text());
+  const std::u16string& title = controller_->title();
+  const std::u16string& message = controller_->message_text();
 
   switch (controller_->javascript_dialog_type()) {
     case content::JAVASCRIPT_DIALOG_TYPE_ALERT: {
@@ -72,11 +70,9 @@
       break;
     }
     case content::JAVASCRIPT_DIALOG_TYPE_PROMPT: {
-      ScopedJavaLocalRef<jstring> default_prompt_text =
-          ConvertUTF16ToJavaString(env, controller_->default_prompt_text());
       dialog_object = Java_JavascriptAppModalDialog_createPromptDialog(
           env, title, message, controller_->display_suppress_checkbox(),
-          default_prompt_text);
+          controller_->default_prompt_text());
       break;
     }
     default:
@@ -109,12 +105,9 @@
 }
 
 void AppModalDialogViewAndroid::DidAcceptAppModalDialog(
-    JNIEnv* env,
-    const JavaRef<jstring>& prompt,
+    const std::u16string& prompt,
     bool should_suppress_js_dialogs) {
-  std::u16string prompt_text =
-      base::android::ConvertJavaStringToUTF16(env, prompt);
-  controller_->OnAccept(prompt_text, should_suppress_js_dialogs);
+  controller_->OnAccept(prompt, should_suppress_js_dialogs);
   delete this;
 }
 
@@ -128,7 +121,6 @@
 }
 
 void AppModalDialogViewAndroid::DidCancelAppModalDialog(
-    JNIEnv* env,
     bool should_suppress_js_dialogs) {
   controller_->OnCancel(should_suppress_js_dialogs);
   delete this;
diff --git a/components/javascript_dialogs/android/app_modal_dialog_view_android.h b/components/javascript_dialogs/android/app_modal_dialog_view_android.h
index 0737a9e2..4e98731 100644
--- a/components/javascript_dialogs/android/app_modal_dialog_view_android.h
+++ b/components/javascript_dialogs/android/app_modal_dialog_view_android.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_JAVASCRIPT_DIALOGS_ANDROID_APP_MODAL_DIALOG_VIEW_ANDROID_H_
 
 #include <memory>
+#include <string>
 
 #include "base/android/jni_weak_ref.h"
 #include "base/android/scoped_java_ref.h"
@@ -37,12 +38,9 @@
   bool IsShowing() const override;
 
   // Called when java confirms or cancels the dialog.
-  void DidAcceptAppModalDialog(
-      JNIEnv* env,
-      const base::android::JavaRef<jstring>& prompt_text,
-      bool suppress_js_dialogs);
-  void DidCancelAppModalDialog(JNIEnv* env,
+  void DidAcceptAppModalDialog(const std::u16string& prompt_text,
                                bool suppress_js_dialogs);
+  void DidCancelAppModalDialog(bool suppress_js_dialogs);
 
   const base::android::ScopedJavaGlobalRef<jobject>& GetDialogObject() const;
 
diff --git a/components/javascript_dialogs/android/java/src/org/chromium/components/javascript_dialogs/JavascriptAppModalDialog.java b/components/javascript_dialogs/android/java/src/org/chromium/components/javascript_dialogs/JavascriptAppModalDialog.java
index f806b94..b792b4fe 100644
--- a/components/javascript_dialogs/android/java/src/org/chromium/components/javascript_dialogs/JavascriptAppModalDialog.java
+++ b/components/javascript_dialogs/android/java/src/org/chromium/components/javascript_dialogs/JavascriptAppModalDialog.java
@@ -8,6 +8,7 @@
 
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
+import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
 import org.chromium.build.annotations.NullMarked;
@@ -44,21 +45,28 @@
 
     @CalledByNative
     public static JavascriptAppModalDialog createAlertDialog(
-            String title, String message, boolean shouldShowSuppressCheckBox) {
+            @JniType("std::u16string") String title,
+            @JniType("std::u16string") String message,
+            boolean shouldShowSuppressCheckBox) {
         return new JavascriptAppModalDialog(
                 title, message, null, shouldShowSuppressCheckBox, R.string.ok, 0);
     }
 
     @CalledByNative
     public static JavascriptAppModalDialog createConfirmDialog(
-            String title, String message, boolean shouldShowSuppressCheckBox) {
+            @JniType("std::u16string") String title,
+            @JniType("std::u16string") String message,
+            boolean shouldShowSuppressCheckBox) {
         return new JavascriptAppModalDialog(
                 title, message, null, shouldShowSuppressCheckBox, R.string.ok, R.string.cancel);
     }
 
     @CalledByNative
     public static JavascriptAppModalDialog createBeforeUnloadDialog(
-            String title, String message, boolean isReload, boolean shouldShowSuppressCheckBox) {
+            @JniType("std::u16string") String title,
+            @JniType("std::u16string") String message,
+            boolean isReload,
+            boolean shouldShowSuppressCheckBox) {
         return new JavascriptAppModalDialog(
                 title,
                 message,
@@ -70,10 +78,10 @@
 
     @CalledByNative
     public static JavascriptAppModalDialog createPromptDialog(
-            String title,
-            String message,
+            @JniType("std::u16string") String title,
+            @JniType("std::u16string") String message,
             boolean shouldShowSuppressCheckBox,
-            String defaultPromptText) {
+            @JniType("std::u16string") String defaultPromptText) {
         return new JavascriptAppModalDialog(
                 title,
                 message,
@@ -128,7 +136,9 @@
     @NativeMethods
     interface Natives {
         void didAcceptAppModalDialog(
-                long nativeAppModalDialogViewAndroid, String prompt, boolean suppress);
+                long nativeAppModalDialogViewAndroid,
+                @JniType("std::u16string") String prompt,
+                boolean suppress);
 
         void didCancelAppModalDialog(long nativeAppModalDialogViewAndroid, boolean suppress);
 
diff --git a/components/omnibox/browser/aim_eligibility_service.cc b/components/omnibox/browser/aim_eligibility_service.cc
index 5c8dca5..cad7531 100644
--- a/components/omnibox/browser/aim_eligibility_service.cc
+++ b/components/omnibox/browser/aim_eligibility_service.cc
@@ -529,7 +529,7 @@
 bool AimEligibilityService::IsFuseboxEligible() const {
   if (!base::FeatureList::IsEnabled(
           omnibox::kAimFuseboxEligibilityCheckEnabled)) {
-    return true;
+    return IsAimEligible();
   }
   return IsAimEligible() && GetMostRecentResponse().is_fusebox_eligible();
 }
diff --git a/components/omnibox/browser/aim_eligibility_service_unittest.cc b/components/omnibox/browser/aim_eligibility_service_unittest.cc
index da8725d..647582b 100644
--- a/components/omnibox/browser/aim_eligibility_service_unittest.cc
+++ b/components/omnibox/browser/aim_eligibility_service_unittest.cc
@@ -475,13 +475,15 @@
 
 TEST_F(AimEligibilityServiceTest, IsFuseboxEligible_FeatureDisabled) {
   base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndDisableFeature(
-      omnibox::kAimFuseboxEligibilityCheckEnabled);
+  feature_list.InitWithFeatures({},
+                                {omnibox::kAimFuseboxEligibilityCheckEnabled,
+                                 omnibox::kAimServerEligibilityEnabled});
 
   omnibox::AimEligibilityResponse response;
+  response.set_is_eligible(true);
   response.set_is_fusebox_eligible(false);
   aim_eligibility_service_->SetAimEligibilityResponse(std::move(response));
 
   // Should be true regardless of response if feature is disabled.
-  EXPECT_TRUE(aim_eligibility_service_->IsFuseboxEligible());
+  EXPECT_EQ(true, aim_eligibility_service_->IsFuseboxEligible());
 }
diff --git a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java
index d1c517ee..a956d88 100644
--- a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java
+++ b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java
@@ -197,34 +197,34 @@
     private static AutocompleteMatch build(
             long nativeObject,
             int nativeType,
-            int[] nativeSubtypes,
+            @JniType("std::vector<int32_t>") int[] nativeSubtypes,
             boolean isSearchType,
             int iconType,
             int transition,
-            String contents,
-            int[] contentClassificationOffsets,
-            int[] contentClassificationStyles,
-            String description,
-            int[] descriptionClassificationOffsets,
-            int[] descriptionClassificationStyles,
+            @JniType("std::u16string") String contents,
+            @JniType("std::vector<int32_t>") int[] contentClassificationOffsets,
+            @JniType("std::vector<int32_t>") int[] contentClassificationStyles,
+            @JniType("std::u16string") String description,
+            @JniType("std::vector<int32_t>") int[] descriptionClassificationOffsets,
+            @JniType("std::vector<int32_t>") int[] descriptionClassificationStyles,
             byte[] serializedAnswerTemplate,
             int answerType,
-            String fillIntoEdit,
-            GURL url,
-            GURL imageUrl,
-            String imageDominantColor,
+            @JniType("std::u16string") String fillIntoEdit,
+            @JniType("GURL") GURL url,
+            @JniType("GURL") GURL imageUrl,
+            @JniType("std::string") String imageDominantColor,
             boolean isDeletable,
-            String postContentType,
+            @JniType("std::string") String postContentType,
             byte[] postData,
             int groupId,
             byte[] clipboardImageData,
             boolean hasTabMatch,
             @JniType("std::vector") List<OmniboxAction> actions,
             boolean allowedToBeDefaultMatch,
-            String inlineAutocompletion,
-            String additionalText,
-            String localTabGroupId,
-            String associatedKeyword,
+            @JniType("std::u16string") String inlineAutocompletion,
+            @JniType("std::u16string") String additionalText,
+            @JniType("base::Uuid") String localTabGroupId,
+            @JniType("std::u16string") String associatedKeyword,
             byte[] serializedSuggestTemplate) {
         assert contentClassificationOffsets.length == contentClassificationStyles.length;
         List<MatchClassification> contentClassifications = new ArrayList<>();
@@ -298,9 +298,9 @@
      */
     @CalledByNative
     private void updateClipboardContent(
-            String contents,
-            GURL url,
-            @Nullable String postContentType,
+            @JniType("std::u16string") String contents,
+            @JniType("GURL") GURL url,
+            @JniType("std::string") @Nullable String postContentType,
             byte @Nullable [] postData,
             byte @Nullable [] clipboardImageData) {
         mDisplayText = contents;
@@ -317,7 +317,10 @@
 
     @CalledByNative
     @VisibleForTesting
-    void updateNavigationDetails(GURL url, String[] headerKeys, String[] headerVals) {
+    void updateNavigationDetails(
+            @JniType("GURL") GURL url,
+            @JniType("std::vector<std::string>") String[] headerKeys,
+            @JniType("std::vector<std::string>") String[] headerVals) {
         mUrl = url;
         assert headerKeys.length == headerVals.length;
 
@@ -344,9 +347,9 @@
 
     @CalledByNative
     private void setDescription(
-            String description,
-            int[] descriptionClassificationOffsets,
-            int[] descriptionClassificationStyles) {
+            @JniType("std::u16string") String description,
+            @JniType("std::vector<int32_t>") int[] descriptionClassificationOffsets,
+            @JniType("std::vector<int32_t>") int[] descriptionClassificationStyles) {
         assert descriptionClassificationOffsets.length == descriptionClassificationStyles.length;
         mDescription = description;
         mDescriptionClassifications.clear();
diff --git a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/OmniboxUrlEmphasizer.java b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/OmniboxUrlEmphasizer.java
index 5830fa911..a7ebd03d 100644
--- a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/OmniboxUrlEmphasizer.java
+++ b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/OmniboxUrlEmphasizer.java
@@ -13,6 +13,7 @@
 import androidx.annotation.ColorRes;
 import androidx.annotation.VisibleForTesting;
 
+import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
 import org.chromium.build.annotations.NullMarked;
@@ -360,7 +361,9 @@
 
     @NativeMethods
     public interface Natives {
+        @JniType("std::vector<int32_t>")
         int[] parseForEmphasizeComponents(
-                String text, AutocompleteSchemeClassifier autocompleteSchemeClassifier);
+                @JniType("std::u16string") String text,
+                AutocompleteSchemeClassifier autocompleteSchemeClassifier);
     }
 }
diff --git a/components/omnibox/browser/autocomplete_match_android.cc b/components/omnibox/browser/autocomplete_match_android.cc
index 5f2b531..926b28c 100644
--- a/components/omnibox/browser/autocomplete_match_android.cc
+++ b/components/omnibox/browser/autocomplete_match_android.cc
@@ -19,6 +19,7 @@
 #include "components/omnibox/common/omnibox_feature_configs.h"
 #include "components/saved_tab_groups/public/android/tab_group_sync_conversions_bridge.h"
 #include "components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h"
+#include "third_party/jni_zero/default_conversions.h"
 #include "third_party/omnibox_proto/suggest_template_info.pb.h"
 #include "url/android/gurl_android.h"
 
@@ -44,15 +45,15 @@
   if (java_match_)
     return ScopedJavaLocalRef<jobject>(*java_match_);
 
-  std::vector<int> contents_class_offsets;
-  std::vector<int> contents_class_styles;
+  std::vector<int32_t> contents_class_offsets;
+  std::vector<int32_t> contents_class_styles;
   for (auto contents_class_item : contents_class) {
     contents_class_offsets.push_back(contents_class_item.offset);
     contents_class_styles.push_back(contents_class_item.style);
   }
 
-  std::vector<int> description_class_offsets;
-  std::vector<int> description_class_styles;
+  std::vector<int32_t> description_class_offsets;
+  std::vector<int32_t> description_class_styles;
   for (auto description_class_item : description_class) {
     description_class_offsets.push_back(description_class_item.offset);
     description_class_styles.push_back(description_class_item.style);
@@ -67,26 +68,20 @@
     }
   }
 
-  ScopedJavaLocalRef<jstring> j_image_dominant_color;
-  ScopedJavaLocalRef<jstring> j_post_content_type;
-  ScopedJavaLocalRef<jbyteArray> j_post_content;
-  std::string clipboard_image_data;
-
-  if (!image_dominant_color.empty()) {
-    j_image_dominant_color = ConvertUTF8ToJavaString(env, image_dominant_color);
+  std::string post_content_type;
+  ScopedJavaLocalRef<jbyteArray> j_post_data;
+  if (post_content) {
+    post_content_type = post_content->first;
+    j_post_data = base::android::ToJavaByteArray(env, post_content->second);
   }
 
-  if (post_content && !post_content->first.empty() &&
-      !post_content->second.empty()) {
-    j_post_content_type = ConvertUTF8ToJavaString(env, post_content->first);
-    j_post_content = ToJavaByteArray(env, post_content->second);
-  }
-
+  ScopedJavaLocalRef<jbyteArray> j_clipboard_image_data;
   if (search_terms_args.get()) {
-    clipboard_image_data = search_terms_args->image_thumbnail_content;
+    j_clipboard_image_data = base::android::ToJavaByteArray(
+        env, search_terms_args->image_thumbnail_content);
   }
 
-  std::vector<int> temp_subtypes(subtypes.begin(), subtypes.end());
+  std::vector<int32_t> temp_subtypes(subtypes.begin(), subtypes.end());
 
   std::vector<jni_zero::ScopedJavaLocalRef<jobject>> actions_list;
   if (actions.empty() && takeover_action) {
@@ -112,28 +107,18 @@
 
   java_match_ = std::make_unique<ScopedJavaGlobalRef<jobject>>(
       Java_AutocompleteMatch_build(
-          env, reinterpret_cast<intptr_t>(this), type,
-          ToJavaIntArray(env, temp_subtypes), IsSearchType(type), icon_type,
-          transition, ConvertUTF16ToJavaString(env, contents),
-          ToJavaIntArray(env, contents_class_offsets),
-          ToJavaIntArray(env, contents_class_styles),
-          ConvertUTF16ToJavaString(env, description),
-          ToJavaIntArray(env, description_class_offsets),
-          ToJavaIntArray(env, description_class_styles), j_answer_template,
-          answer_type, ConvertUTF16ToJavaString(env, fill_into_edit),
-          url::GURLAndroid::FromNativeGURL(env, destination_url),
-          url::GURLAndroid::FromNativeGURL(env, image_url),
-          j_image_dominant_color, SupportsDeletion(), j_post_content_type,
-          j_post_content, suggestion_group_id.value_or(omnibox::GROUP_INVALID),
-          ToJavaByteArray(env, clipboard_image_data),
-          has_tab_match.value_or(false), actions_list,
-          allowed_to_be_default_match,
-          ConvertUTF16ToJavaString(env, inline_autocompletion),
-          ConvertUTF16ToJavaString(env, additional_text),
-          tab_groups::UuidToJavaString(
-              env, matching_tab_group_uuid.value_or(base::Uuid())),
-          ConvertUTF16ToJavaString(env, associated_keyword),
-          j_suggest_template));
+          env, reinterpret_cast<intptr_t>(this), type, temp_subtypes,
+          IsSearchType(type), icon_type, transition, contents,
+          contents_class_offsets, contents_class_styles, description,
+          description_class_offsets, description_class_styles,
+          j_answer_template, answer_type, fill_into_edit, destination_url,
+          image_url, image_dominant_color, SupportsDeletion(),
+          post_content_type, j_post_data,
+          suggestion_group_id.value_or(omnibox::GROUP_INVALID),
+          j_clipboard_image_data, has_tab_match.value_or(false), actions_list,
+          allowed_to_be_default_match, inline_autocompletion, additional_text,
+          matching_tab_group_uuid.value_or(base::Uuid()),
+          associated_keyword, j_suggest_template));
 
   return ScopedJavaLocalRef<jobject>(*java_match_);
 }
@@ -183,24 +168,21 @@
   if (!java_match_)
     return;
 
-  std::string clipboard_image_data;
+  ScopedJavaLocalRef<jbyteArray> j_clipboard_image_data;
   if (search_terms_args.get()) {
-    clipboard_image_data = search_terms_args->image_thumbnail_content;
+    j_clipboard_image_data = base::android::ToJavaByteArray(
+        env, search_terms_args->image_thumbnail_content);
   }
 
-  ScopedJavaLocalRef<jstring> j_post_content_type;
-  ScopedJavaLocalRef<jbyteArray> j_post_content;
-  if (post_content && !post_content->first.empty() &&
-      !post_content->second.empty()) {
-    j_post_content_type = ConvertUTF8ToJavaString(env, post_content->first);
-    j_post_content = ToJavaByteArray(env, post_content->second);
+  ScopedJavaLocalRef<jbyteArray> j_post_data;
+  if (post_content) {
+    j_post_data = base::android::ToJavaByteArray(env, post_content->second);
   }
 
   Java_AutocompleteMatch_updateClipboardContent(
-      env, *java_match_, ConvertUTF16ToJavaString(env, contents),
-      url::GURLAndroid::FromNativeGURL(env, destination_url),
-      j_post_content_type, j_post_content,
-      ToJavaByteArray(env, clipboard_image_data));
+      env, *java_match_, contents, destination_url,
+      post_content ? post_content->first : "", j_post_data,
+      j_clipboard_image_data);
 }
 
 void AutocompleteMatch::UpdateJavaNavigationDetails() {
@@ -215,10 +197,7 @@
     }
 
     Java_AutocompleteMatch_updateNavigationDetails(
-        env, *java_match_,
-        url::GURLAndroid::FromNativeGURL(env, destination_url),
-        ToJavaArrayOfStrings(env, header_keys),
-        ToJavaArrayOfStrings(env, header_vals));
+        env, *java_match_, destination_url, header_keys, header_vals);
   }
 }
 
@@ -241,17 +220,16 @@
 
 void AutocompleteMatch::UpdateJavaDescription() {
   if (java_match_) {
-    std::vector<int> description_class_offsets;
-    std::vector<int> description_class_styles;
+    std::vector<int32_t> description_class_offsets;
+    std::vector<int32_t> description_class_styles;
     for (auto description_class_item : description_class) {
       description_class_offsets.push_back(description_class_item.offset);
       description_class_styles.push_back(description_class_item.style);
     }
     JNIEnv* env = base::android::AttachCurrentThread();
-    Java_AutocompleteMatch_setDescription(
-        env, *java_match_, ConvertUTF16ToJavaString(env, description),
-        ToJavaIntArray(env, description_class_offsets),
-        ToJavaIntArray(env, description_class_styles));
+    Java_AutocompleteMatch_setDescription(env, *java_match_, description,
+                                          description_class_offsets,
+                                          description_class_styles);
   }
 }
 
diff --git a/components/omnibox/browser/mock_aim_eligibility_service.cc b/components/omnibox/browser/mock_aim_eligibility_service.cc
index cbb8b03..7681313 100644
--- a/components/omnibox/browser/mock_aim_eligibility_service.cc
+++ b/components/omnibox/browser/mock_aim_eligibility_service.cc
@@ -18,6 +18,8 @@
                             identity_manager,
                             "en-US",
                             std::move(configuration)) {
+  ON_CALL(*this, IsAimEligible()).WillByDefault(testing::Return(true));
+  ON_CALL(*this, IsFuseboxEligible()).WillByDefault(testing::Return(true));
   ON_CALL(*this, GetSearchboxConfig())
       .WillByDefault(testing::Return(&mock_config));
 }
diff --git a/components/omnibox/browser/omnibox_url_emphasizer.cc b/components/omnibox/browser/omnibox_url_emphasizer.cc
index c4a639a..70169c1 100644
--- a/components/omnibox/browser/omnibox_url_emphasizer.cc
+++ b/components/omnibox/browser/omnibox_url_emphasizer.cc
@@ -8,6 +8,7 @@
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_scheme_classifier.h"
 #include "components/omnibox/browser/autocomplete_scheme_classifier_android.h"
+#include "third_party/jni_zero/default_conversions.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
 #include "components/omnibox/browser/scheme_classifier_jni/OmniboxUrlEmphasizer_jni.h"
@@ -16,24 +17,20 @@
 using base::android::ScopedJavaLocalRef;
 
 // static
-static ScopedJavaLocalRef<jintArray>
+static std::vector<int32_t>
 JNI_OmniboxUrlEmphasizer_ParseForEmphasizeComponents(
-    JNIEnv* env,
-    const JavaRef<jstring>& jtext,
+    const std::u16string& text,
     const base::android::JavaRef<jobject>& jautocomplete_scheme_classifier) {
   AutocompleteSchemeClassifier* autocomplete_scheme_classifier =
       AutocompleteSchemeClassifierAndroid::FromJavaObj(
           jautocomplete_scheme_classifier);
   DCHECK(autocomplete_scheme_classifier);
 
-  std::u16string text(base::android::ConvertJavaStringToUTF16(env, jtext));
-
   url::Component scheme, host;
   AutocompleteInput::ParseForEmphasizeComponents(
       text, *autocomplete_scheme_classifier, &scheme, &host);
 
-  int emphasize_values[] = {scheme.begin, scheme.len, host.begin, host.len};
-  return base::android::ToJavaIntArray(env, emphasize_values);
+  return {scheme.begin, scheme.len, host.begin, host.len};
 }
 
 DEFINE_JNI(OmniboxUrlEmphasizer)
diff --git a/components/optimization_guide/core/model_execution/model_broker_client_unittest.cc b/components/optimization_guide/core/model_execution/model_broker_client_unittest.cc
index 00f913be..062a6f5 100644
--- a/components/optimization_guide/core/model_execution/model_broker_client_unittest.cc
+++ b/components/optimization_guide/core/model_execution/model_broker_client_unittest.cc
@@ -133,4 +133,21 @@
   ASSERT_FALSE(session);
 }
 
+// Verify that CreateSession works for the classifier feature.
+TEST(ModelBrokerClientTest, ClassifierSession) {
+  base::test::TaskEnvironment task_environment_;
+  OptimizationGuideLogger logger;
+  FakeModelBroker fake_broker({});
+  fake_broker.InstallClassifierModel(FakeBaseModelAsset::Content{});
+
+  ModelBrokerClient client(fake_broker.BindAndPassRemote(),
+                           logger.GetWeakPtr());
+  base::test::TestFuture<ModelBrokerClient::CreateSessionResult> future;
+
+  // Requesting the classifier feature should succeed.
+  client.CreateSession(mojom::OnDeviceFeature::kClassifier,
+                       SessionConfigParams{}, future.GetCallback());
+  ASSERT_TRUE(future.Take());
+}
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/model_broker_state.cc b/components/optimization_guide/core/model_execution/model_broker_state.cc
index 9d2e076..3df08490 100644
--- a/components/optimization_guide/core/model_execution/model_broker_state.cc
+++ b/components/optimization_guide/core/model_execution/model_broker_state.cc
@@ -13,6 +13,7 @@
 #include "components/optimization_guide/core/model_execution/on_device_asset_manager.h"
 #include "components/optimization_guide/core/model_execution/on_device_features.h"
 #include "components/optimization_guide/core/model_execution/on_device_model_access_controller.h"
+#include "components/optimization_guide/core/model_execution/on_device_model_classifier_controller.h"
 #include "components/optimization_guide/core/model_execution/on_device_model_service_controller.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/optimization_guide/public/mojom/model_broker.mojom.h"
@@ -41,8 +42,17 @@
     on_device_model::ServiceClient::LaunchFn launch_fn,
     component_updater::ComponentUpdateService* component_update_service)
     : service_client_(std::move(launch_fn)),
-      download_progress_manager_(component_update_service,
-                                 {base_delegate->GetComponentId()}),
+      // Note: tracking multiple components makes the download progress relative
+      // to the total size of all components. Since we only download the
+      // perspective model when it is used, this can result in the progress
+      // jumping if only one model is being downloaded.
+      // TODO(crbug.com/491858797): Clean up observer for classifier model.
+      download_progress_manager_(
+          component_update_service,
+          classifier_delegate
+              ? std::vector<std::string>{base_delegate->GetComponentId(),
+                                         classifier_delegate->GetComponentId()}
+              : std::vector<std::string>{base_delegate->GetComponentId()}),
       usage_tracker_(&local_state),
       model_broker_impl_(
           usage_tracker_,
@@ -53,8 +63,9 @@
       component_state_manager_(&local_state,
                                performance_classifier_.GetSafeRef(),
                                usage_tracker_,
-                               std::move(base_delegate)),
-      service_controller_(
+                               std::move(base_delegate),
+                               OnDeviceModelServiceController::kModelType),
+      base_model_controller_(
           service_client_,
           usage_tracker_,
           model_broker_impl_,
@@ -63,8 +74,15 @@
       asset_manager_(local_state,
                      usage_tracker_,
                      component_state_manager_,
-                     service_controller_,
-                     model_provider) {}
+                     base_model_controller_,
+                     model_provider) {
+  if (classifier_delegate) {
+    classifier_controller_.emplace(
+        local_state, performance_classifier_.GetSafeRef(), usage_tracker_,
+        service_client_.GetSafeRef(), model_broker_impl_,
+        std::move(classifier_delegate));
+  }
+}
 ModelBrokerState::~ModelBrokerState() = default;
 
 void ModelBrokerState::BindModelBroker(
@@ -113,7 +131,11 @@
               base::ToString(feature));
   // Ensure a solution is constructed for this feature, to avoid returning
   // kUnknown when this is called too early.
-  service_controller_.UpdateSolutionProvider(feature);
+  base_model_controller_.UpdateSolutionProvider(feature);
+  if (classifier_controller_) {
+    classifier_controller_->UpdateSolution();
+  }
+
   return model_broker_impl_.GetSolutionProvider(feature).solution().error_or(
       OnDeviceModelEligibilityReason::kSuccess);
 }
@@ -208,7 +230,7 @@
   if (!features::IsOnDeviceExecutionEnabled()) {
     return {};
   }
-  auto capabilities = service_controller_.GetCapabilities();
+  auto capabilities = base_model_controller_.GetCapabilities();
   capabilities.RetainAll(
       performance_classifier_.GetPossibleOnDeviceCapabilities());
   return capabilities;
diff --git a/components/optimization_guide/core/model_execution/model_broker_state.h b/components/optimization_guide/core/model_execution/model_broker_state.h
index 11e4ae600c..cf6b7da 100644
--- a/components/optimization_guide/core/model_execution/model_broker_state.h
+++ b/components/optimization_guide/core/model_execution/model_broker_state.h
@@ -11,6 +11,7 @@
 #include "components/optimization_guide/core/delivery/optimization_guide_model_provider.h"
 #include "components/optimization_guide/core/model_execution/on_device_asset_manager.h"
 #include "components/optimization_guide/core/model_execution/on_device_capability.h"
+#include "components/optimization_guide/core/model_execution/on_device_model_classifier_controller.h"
 #include "components/optimization_guide/core/model_execution/on_device_model_download_progress_manager.h"
 #include "components/optimization_guide/core/model_execution/on_device_model_service_controller.h"
 #include "components/optimization_guide/core/model_execution/performance_class.h"
@@ -48,8 +49,8 @@
     return component_state_manager_;
   }
 
-  OnDeviceModelServiceController& service_controller() {
-    return service_controller_;
+  OnDeviceModelServiceController& base_model_controller() {
+    return base_model_controller_;
   }
 
   // OnDeviceCapability
@@ -94,10 +95,9 @@
   ModelBrokerImpl model_broker_impl_;
   PerformanceClassifier performance_classifier_;
   OnDeviceModelComponentStateManager component_state_manager_;
-  OnDeviceModelServiceController service_controller_;
+  OnDeviceModelServiceController base_model_controller_;
+  std::optional<OnDeviceModelClassifierController> classifier_controller_;
   OnDeviceAssetManager asset_manager_;
-  std::unique_ptr<OnDeviceModelComponentStateManager::Delegate>
-      classifier_delegate_;
   base::WeakPtrFactory<ModelBrokerState> weak_ptr_factory_{this};
 };
 
diff --git a/components/optimization_guide/core/model_execution/on_device_asset_manager_unittest.cc b/components/optimization_guide/core/model_execution/on_device_asset_manager_unittest.cc
index e5008c2..ca0221d 100644
--- a/components/optimization_guide/core/model_execution/on_device_asset_manager_unittest.cc
+++ b/components/optimization_guide/core/model_execution/on_device_asset_manager_unittest.cc
@@ -112,7 +112,7 @@
                fake_safety.model_info());
 
   EXPECT_FALSE(broker_.GetOrCreateBrokerState()
-                   .service_controller()
+                   .base_model_controller()
                    .GetSafetyClientForTesting()
                    .safety_model_info());
 }
@@ -124,7 +124,7 @@
   UpdateTarget(proto::OPTIMIZATION_TARGET_TEXT_SAFETY,
                fake_safety.model_info());
   ASSERT_TRUE(broker_.GetOrCreateBrokerState()
-                  .service_controller()
+                  .base_model_controller()
                   .GetSafetyClientForTesting()
                   .safety_model_info());
 }
@@ -136,7 +136,7 @@
   UpdateTarget(proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
                fake_language.model_info());
   EXPECT_EQ(fake_language.model_path(), broker_.GetOrCreateBrokerState()
-                                            .service_controller()
+                                            .base_model_controller()
                                             .GetSafetyClientForTesting()
                                             .language_detection_model_path());
 }
diff --git a/components/optimization_guide/core/model_execution/on_device_features.cc b/components/optimization_guide/core/model_execution/on_device_features.cc
index c508b42..930bfc0 100644
--- a/components/optimization_guide/core/model_execution/on_device_features.cc
+++ b/components/optimization_guide/core/model_execution/on_device_features.cc
@@ -38,6 +38,25 @@
   }
 }
 
+OnDeviceModelType GetOnDeviceModelType(mojom::OnDeviceFeature feature) {
+  switch (feature) {
+    case mojom::OnDeviceFeature::kClassifier:
+      return OnDeviceModelType::kClassifierModel;
+    case mojom::OnDeviceFeature::kCompose:
+    case mojom::OnDeviceFeature::kTest:
+    case mojom::OnDeviceFeature::kPromptApi:
+    case mojom::OnDeviceFeature::kHistorySearch:
+    case mojom::OnDeviceFeature::kSummarize:
+    case mojom::OnDeviceFeature::kHistoryQueryIntent:
+    case mojom::OnDeviceFeature::kScamDetection:
+    case mojom::OnDeviceFeature::kPermissionsAi:
+    case mojom::OnDeviceFeature::kProofreaderApi:
+    case mojom::OnDeviceFeature::kWritingAssistanceApi:
+    case mojom::OnDeviceFeature::kOnDeviceSpeechRecognition:
+      return OnDeviceModelType::kBaseModel;
+  }
+}
+
 // To enable on-device execution for a feature, update this to return a
 // non-null target.
 proto::OptimizationTarget GetOptimizationTargetForFeature(
diff --git a/components/optimization_guide/core/model_execution/on_device_features.h b/components/optimization_guide/core/model_execution/on_device_features.h
index eb905ed..61d08ff8 100644
--- a/components/optimization_guide/core/model_execution/on_device_features.h
+++ b/components/optimization_guide/core/model_execution/on_device_features.h
@@ -20,10 +20,19 @@
                                          mojom::OnDeviceFeature::kMinValue,
                                          mojom::OnDeviceFeature::kMaxValue>;
 
+enum class OnDeviceModelType {
+  kBaseModel,
+  kClassifierModel,
+};
+
 // Return the name to use in histogram variants for this feature key.
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
 std::string_view GetVariantName(mojom::OnDeviceFeature feature);
 
+// Returns the model type required for the feature.
+COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
+OnDeviceModelType GetOnDeviceModelType(mojom::OnDeviceFeature feature);
+
 // Returns which ModelExecutionFeature is used for this feature key.
 COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
 proto::ModelExecutionFeature ToModelExecutionFeatureProto(
diff --git a/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.cc b/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.cc
index 46aa0ad..4ef7410 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.cc
@@ -15,6 +15,7 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/trace_event/trace_event.h"
+#include "components/optimization_guide/core/model_execution/on_device_features.h"
 #include "components/optimization_guide/core/model_execution/on_device_model_component.h"
 #include "components/optimization_guide/core/model_execution/on_device_model_feature_adapter.h"
 #include "components/optimization_guide/core/model_execution/on_device_model_metadata.h"
@@ -155,7 +156,8 @@
       component_state_manager_(&local_state,
                                performance_classifier,
                                usage_tracker,
-                               std::move(delegate)) {
+                               std::move(delegate),
+                               kModelType) {
   component_state_manager_.AddObserver(this);
 }
 
diff --git a/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.h b/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.h
index d36849c0d..f400405 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.h
+++ b/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.h
@@ -29,6 +29,10 @@
 class OnDeviceModelClassifierController
     : public OnDeviceModelComponentStateManager::Observer {
  public:
+  // The model type managed by this controller.
+  static constexpr OnDeviceModelType kModelType =
+      OnDeviceModelType::kClassifierModel;
+
   OnDeviceModelClassifierController(
       PrefService& local_state,
       base::SafeRef<PerformanceClassifier> performance_classifier,
diff --git a/components/optimization_guide/core/model_execution/on_device_model_component.cc b/components/optimization_guide/core/model_execution/on_device_model_component.cc
index 7a45e3f..a751b8d 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_component.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_component.cc
@@ -184,6 +184,15 @@
   }
 }
 
+bool WasOnDeviceModelRecentlyUsed(UsageTracker* usage_tracker,
+                                  OnDeviceModelType model_type) {
+  return std::ranges::any_of(
+      OnDeviceFeatureSet::All(), [&](mojom::OnDeviceFeature feature) {
+        return GetOnDeviceModelType(feature) == model_type &&
+               usage_tracker->WasOnDeviceEligibleFeatureRecentlyUsed(feature);
+      });
+}
+
 }  // namespace
 
 std::ostream& operator<<(std::ostream& out, OnDeviceModelStatus status) {
@@ -271,11 +280,13 @@
     PrefService* local_state,
     base::SafeRef<PerformanceClassifier> performance_classifier,
     UsageTracker& usage_tracker,
-    std::unique_ptr<Delegate> delegate)
+    std::unique_ptr<Delegate> delegate,
+    OnDeviceModelType model_type)
     : local_state_(local_state),
       performance_classifier_(std::move(performance_classifier)),
       delegate_(std::move(delegate)),
-      usage_tracker_(usage_tracker) {
+      usage_tracker_(usage_tracker),
+      model_type_(model_type) {
   CHECK(local_state);  // Useful to catch poor test setup.
   usage_tracker_observation_.Observe(&usage_tracker);
   pref_change_registrar_.Init(local_state);
@@ -419,6 +430,10 @@
     mojom::OnDeviceFeature feature) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  if (GetOnDeviceModelType(feature) != model_type_) {
+    return;
+  }
+
   base::UmaHistogramEnumeration(
       "OptimizationGuide.ModelExecution.OnDeviceModelStatusAtUseTime",
       GetOnDeviceModelStatus());
@@ -480,7 +495,7 @@
   result.disk_space_free = disk_space_free_bytes;
   result.device_capable = performance_classifier_->IsDeviceCapable();
   result.on_device_feature_recently_used =
-      usage_tracker_->WasAnyOnDeviceEligibleFeatureRecentlyUsed();
+      WasOnDeviceModelRecentlyUsed(&usage_tracker_.get(), model_type_);
   result.enabled_by_feature = features::IsOnDeviceExecutionEnabled();
   result.enabled_by_enterprise_policy =
       GetGenAILocalFoundationalModelEnterprisePolicySettings(local_state_) ==
diff --git a/components/optimization_guide/core/model_execution/on_device_model_component.h b/components/optimization_guide/core/model_execution/on_device_model_component.h
index 3cf5dc4..7b46b8f8 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_component.h
+++ b/components/optimization_guide/core/model_execution/on_device_model_component.h
@@ -26,6 +26,7 @@
 #include "base/types/pass_key.h"
 #include "base/values.h"
 #include "base/version.h"
+#include "components/optimization_guide/core/model_execution/on_device_features.h"
 #include "components/optimization_guide/core/model_execution/performance_class.h"
 #include "components/optimization_guide/core/model_execution/usage_tracker.h"
 #include "components/optimization_guide/core/optimization_guide_enums.h"
@@ -307,7 +308,8 @@
       PrefService* local_state,
       base::SafeRef<PerformanceClassifier> performance_classifier,
       UsageTracker& usage_tracker,
-      std::unique_ptr<Delegate> delegate);
+      std::unique_ptr<Delegate> delegate,
+      OnDeviceModelType model_type);
   ~OnDeviceModelComponentStateManager() override;
 
   // Returns whether the component installation is valid.
@@ -424,6 +426,9 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
+  // The model type managed by this state manager.
+  const OnDeviceModelType model_type_;
+
   base::WeakPtrFactory<OnDeviceModelComponentStateManager> weak_ptr_factory_{
       this};
 };
diff --git a/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc b/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc
index 496b634a..b29edf63 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_component_unittest.cc
@@ -144,6 +144,7 @@
   FakeModelBroker broker_{{
       .performance_class = OnDeviceModelPerformanceClass::kUnknown,
       .preinstall_base_model = false,
+      .include_classifier = false,
   }};
   base::HistogramTester histograms_;
 };
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc b/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc
index bc8ca02..c97feee 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc
@@ -306,6 +306,9 @@
 
 void OnDeviceModelServiceController::UpdateSolutionProvider(
     mojom::OnDeviceFeature feature) {
+  if (GetOnDeviceModelType(feature) != kModelType) {
+    return;
+  }
   // Note: This always constructs the Solution, even if the provider was not
   // constructed yet, to update supported_adaptation_ranks_ on the base model.
   model_broker_impl_->GetSolutionProvider(feature).Update(GetSolution(feature));
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller.h b/components/optimization_guide/core/model_execution/on_device_model_service_controller.h
index 964df88..7e8fb2f 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_service_controller.h
+++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller.h
@@ -70,6 +70,9 @@
 // a single instance of this object.
 class OnDeviceModelServiceController final {
  public:
+  // The model type managed by this controller.
+  static constexpr OnDeviceModelType kModelType = OnDeviceModelType::kBaseModel;
+
   OnDeviceModelServiceController(
       on_device_model::ServiceClient& service_client,
       UsageTracker& usage_tracker,
diff --git a/components/optimization_guide/core/model_execution/test/fake_model_broker.cc b/components/optimization_guide/core/model_execution/test/fake_model_broker.cc
index 8e02924..3b06cf8 100644
--- a/components/optimization_guide/core/model_execution/test/fake_model_broker.cc
+++ b/components/optimization_guide/core/model_execution/test/fake_model_broker.cc
@@ -39,7 +39,7 @@
 }
 ModelBrokerPrefService::~ModelBrokerPrefService() = default;
 
-FakeModelBroker::FakeModelBroker(const Options& options) {
+FakeModelBroker::FakeModelBroker(const Options& options) : options_(options) {
   if (options.performance_class != OnDeviceModelPerformanceClass::kUnknown) {
     UpdatePerformanceClassPref(&local_state_.local_state(),
                                options.performance_class);
@@ -66,6 +66,17 @@
   component_state_.Install(std::move(asset));
 }
 
+void FakeModelBroker::InstallClassifierModel(
+    FakeBaseModelAsset::Content content) {
+  InstallClassifierModel(
+      std::make_unique<FakeBaseModelAsset>(std::move(content)));
+}
+
+void FakeModelBroker::InstallClassifierModel(
+    std::unique_ptr<FakeBaseModelAsset> asset) {
+  classifier_component_state_.Install(std::move(asset));
+}
+
 void FakeModelBroker::UpdateTarget(proto::OptimizationTarget target,
                                    const ModelInfo& model_info) {
   model_provider_.UpdateModelImmediatelyForTesting(
@@ -89,11 +100,14 @@
 
 ModelBrokerState& FakeModelBroker::GetOrCreateBrokerState() {
   if (!model_broker_state_) {
-    model_broker_state_.emplace(local_state_.local_state(), model_provider_,
-                                component_state_.CreateDelegate(),
-                                component_state_.CreateDelegate(),
-                                fake_launcher_.LaunchFn(),
-                                &component_state_.component_update_service());
+    model_broker_state_.emplace(
+        local_state_.local_state(), model_provider_,
+        component_state_.CreateDelegate(),
+        options_.include_classifier
+            ? classifier_component_state_.CreateDelegate()
+            : nullptr,
+        fake_launcher_.LaunchFn(),
+        &component_state_.component_update_service());
   }
   return *model_broker_state_;
 }
diff --git a/components/optimization_guide/core/model_execution/test/fake_model_broker.h b/components/optimization_guide/core/model_execution/test/fake_model_broker.h
index f072772..28a4cb7 100644
--- a/components/optimization_guide/core/model_execution/test/fake_model_broker.h
+++ b/components/optimization_guide/core/model_execution/test/fake_model_broker.h
@@ -57,6 +57,8 @@
         OnDeviceModelPerformanceClass::kHigh;
     // If true, installs a base model to the component_state_.
     bool preinstall_base_model = true;
+    // If true, initializes the classifier controller.
+    bool include_classifier = true;
   };
   explicit FakeModelBroker(const Options& options);
   ~FakeModelBroker();
@@ -70,11 +72,14 @@
   void SimulateShutdown() {
     model_broker_state_.reset();
     component_state_.SimulateShutdown();
+    classifier_component_state_.SimulateShutdown();
   }
   void CrashService() { fake_launcher_.CrashService(); }
 
   void InstallBaseModel(FakeBaseModelAsset::Content content);
   void InstallBaseModel(std::unique_ptr<FakeBaseModelAsset> asset);
+  void InstallClassifierModel(FakeBaseModelAsset::Content content);
+  void InstallClassifierModel(std::unique_ptr<FakeBaseModelAsset> asset);
   void UpdateTarget(proto::OptimizationTarget target,
                     const ModelInfo& model_info);
   void UpdateModelAdaptation(const FakeAdaptationAsset& asset);
@@ -83,6 +88,9 @@
 
   PrefService& local_state() { return local_state_.local_state(); }
   TestComponentState& component_state() { return component_state_; }
+  TestComponentState& classifier_component_state() {
+    return classifier_component_state_;
+  }
   ModelProviderRegistry& model_provider() { return model_provider_; }
 
   // Lazily instantiates model_broker_state_
@@ -94,11 +102,13 @@
   on_device_model::FakeServiceLauncher& launcher() { return fake_launcher_; }
 
  private:
+  Options options_;
   ScopedModelBrokerFeatureList feature_list_;
   ModelBrokerPrefService local_state_;
   on_device_model::FakeOnDeviceServiceSettings fake_settings_;
   on_device_model::FakeServiceLauncher fake_launcher_{&fake_settings_};
   TestComponentState component_state_;
+  TestComponentState classifier_component_state_;
   OptimizationGuideLogger logger_;
   ModelProviderRegistry model_provider_{&logger_};
   std::optional<ModelBrokerState> model_broker_state_;
diff --git a/components/optimization_guide/core/model_execution/usage_tracker.cc b/components/optimization_guide/core/model_execution/usage_tracker.cc
index 4b8f4ff..4b3c7ba 100644
--- a/components/optimization_guide/core/model_execution/usage_tracker.cc
+++ b/components/optimization_guide/core/model_execution/usage_tracker.cc
@@ -47,14 +47,6 @@
                                                         feature);
 }
 
-bool UsageTracker::WasAnyOnDeviceEligibleFeatureRecentlyUsed() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return std::ranges::any_of(
-      OnDeviceFeatureSet::All(), [&](mojom::OnDeviceFeature feature) {
-        return WasOnDeviceEligibleFeatureRecentlyUsed(feature);
-      });
-}
-
 void UsageTracker::AddObserver(Observer* observer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   observers_.AddObserver(observer);
diff --git a/components/optimization_guide/core/model_execution/usage_tracker.h b/components/optimization_guide/core/model_execution/usage_tracker.h
index 31096b1..824e6f4 100644
--- a/components/optimization_guide/core/model_execution/usage_tracker.h
+++ b/components/optimization_guide/core/model_execution/usage_tracker.h
@@ -9,6 +9,7 @@
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
 #include "base/sequence_checker.h"
+#include "components/optimization_guide/core/model_execution/on_device_features.h"
 #include "components/optimization_guide/public/mojom/model_broker.mojom-shared.h"
 
 class PrefService;
@@ -44,9 +45,6 @@
   bool WasOnDeviceEligibleFeatureRecentlyUsed(
       mojom::OnDeviceFeature feature) const;
 
-  // Returns whether any on-device eligible feature was recently used.
-  bool WasAnyOnDeviceEligibleFeatureRecentlyUsed() const;
-
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
diff --git a/components/page_content_annotations/content/page_category_classifier_bridge_impl.cc b/components/page_content_annotations/content/page_category_classifier_bridge_impl.cc
index ba657611..3ba5d280 100644
--- a/components/page_content_annotations/content/page_category_classifier_bridge_impl.cc
+++ b/components/page_content_annotations/content/page_category_classifier_bridge_impl.cc
@@ -4,7 +4,10 @@
 
 #include "components/page_content_annotations/content/page_category_classifier_bridge_impl.h"
 
+#include "base/metrics/histogram_functions.h"
+#include "base/numerics/safe_conversions.h"
 #include "components/page_content_annotations/core/on_device_category_classifier.h"
+#include "components/page_content_annotations/core/page_content_annotation_type.h"
 #include "content/public/browser/page.h"
 
 namespace page_content_annotations {
@@ -32,10 +35,33 @@
   for (const auto& embedding : embeddings) {
     if (embedding.passage.second == EmbeddingPassageType::kTitleAndUrl) {
       category_classifier_->OnPageEmbeddingAvailable(
-          page.GetMainDocument().GetLastCommittedURL(), embedding.embedding);
+          page.GetMainDocument().GetLastCommittedURL(), embedding.embedding,
+          base::BindOnce(
+              &PageCategoryClassifierBridgeImpl::OnCategoryClassifiersCompleted,
+              weak_ptr_factory_.GetWeakPtr()));
       return;
     }
   }
 }
 
+void PageCategoryClassifierBridgeImpl::OnCategoryClassifiersCompleted(
+    const std::vector<Category>& outputs) {
+  for (const Category& category : outputs) {
+    switch (category.category_type) {
+      case CategoryType::kEducation:
+        base::UmaHistogramPercentage(
+            "OptimizationGuide.PageContentAnnotations.CategoryClassifier."
+            "EducationScore",
+            base::ClampRound(category.score * 100));
+        break;
+      case CategoryType::kShopping:
+        base::UmaHistogramPercentage(
+            "OptimizationGuide.PageContentAnnotations.CategoryClassifier."
+            "ShoppingScore",
+            base::ClampRound(category.score * 100));
+        break;
+    }
+  }
+}
+
 }  // namespace page_content_annotations
diff --git a/components/page_content_annotations/content/page_category_classifier_bridge_impl.h b/components/page_content_annotations/content/page_category_classifier_bridge_impl.h
index b904374..d313c47b 100644
--- a/components/page_content_annotations/content/page_category_classifier_bridge_impl.h
+++ b/components/page_content_annotations/content/page_category_classifier_bridge_impl.h
@@ -5,10 +5,14 @@
 #ifndef COMPONENTS_PAGE_CONTENT_ANNOTATIONS_CONTENT_PAGE_CATEGORY_CLASSIFIER_BRIDGE_IMPL_H_
 #define COMPONENTS_PAGE_CONTENT_ANNOTATIONS_CONTENT_PAGE_CATEGORY_CLASSIFIER_BRIDGE_IMPL_H_
 
+#include <vector>
+
 #include "base/memory/raw_ref.h"
+#include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
 #include "components/page_content_annotations/content/page_embeddings_service.h"
 #include "components/page_content_annotations/core/page_category_classifier_bridge.h"
+#include "components/page_content_annotations/core/page_content_annotation_type.h"
 
 namespace page_content_annotations {
 
@@ -34,12 +38,17 @@
   void OnPageEmbeddingsAvailable(content::Page& page) override;
 
  private:
+  void OnCategoryClassifiersCompleted(const std::vector<Category>& outputs);
+
   const raw_ref<PageEmbeddingsService> page_embeddings_service_;
   const raw_ref<OnDeviceCategoryClassifier> category_classifier_;
 
   base::ScopedObservation<PageEmbeddingsService,
                           PageEmbeddingsService::Observer>
       scoped_observation_{this};
+
+  base::WeakPtrFactory<PageCategoryClassifierBridgeImpl> weak_ptr_factory_{
+      this};
 };
 
 }  // namespace page_content_annotations
diff --git a/components/page_content_annotations/core/on_device_category_classifier.cc b/components/page_content_annotations/core/on_device_category_classifier.cc
index 6e356c9..573d15d9 100644
--- a/components/page_content_annotations/core/on_device_category_classifier.cc
+++ b/components/page_content_annotations/core/on_device_category_classifier.cc
@@ -42,14 +42,15 @@
 
 void OnDeviceCategoryClassifier::OnPageEmbeddingAvailable(
     const GURL& url,
-    const passage_embeddings::Embedding& embedding) {
+    const passage_embeddings::Embedding& embedding,
+    base::OnceCallback<void(const std::vector<Category>&)> callback) {
   // Run each of the category classifiers on the returned embedding.
   auto barrier_callback =
       base::BarrierCallback<std::pair<CategoryType, std::optional<float>>>(
           category_classifier_model_handlers_.size(),
           base::BindOnce(
               &OnDeviceCategoryClassifier::OnCategoryClassifiersCompleted,
-              weak_ptr_factory_.GetWeakPtr(), url));
+              weak_ptr_factory_.GetWeakPtr(), std::move(callback), url));
   for (const auto& [category_type, model_handler] :
        category_classifier_model_handlers_) {
     model_handler->ExecuteModelWithInput(
@@ -60,6 +61,7 @@
 }
 
 void OnDeviceCategoryClassifier::OnCategoryClassifiersCompleted(
+    base::OnceCallback<void(const std::vector<Category>&)> callback,
     const GURL& url,
     const std::vector<std::pair<CategoryType, std::optional<float>>>&
         classifier_outputs) {
@@ -70,6 +72,9 @@
           {.category_type = category_type, .score = *maybe_score});
     }
   }
+  if (!callback.is_null()) {
+    std::move(callback).Run(outputs);
+  }
 }
 
 }  // namespace page_content_annotations
diff --git a/components/page_content_annotations/core/on_device_category_classifier.h b/components/page_content_annotations/core/on_device_category_classifier.h
index d331234..62fe522 100644
--- a/components/page_content_annotations/core/on_device_category_classifier.h
+++ b/components/page_content_annotations/core/on_device_category_classifier.h
@@ -14,6 +14,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "components/optimization_guide/core/inference/model_handler.h"
+#include "components/page_content_annotations/core/page_content_annotation_type.h"
 #include "components/passage_embeddings/core/passage_embeddings_types.h"
 
 namespace optimization_guide {
@@ -39,12 +40,15 @@
       delete;
 
   // Invoked when an embedding has been successfully computed for the page.
-  void OnPageEmbeddingAvailable(const GURL& url,
-                                const passage_embeddings::Embedding& embedding);
+  void OnPageEmbeddingAvailable(
+      const GURL& url,
+      const passage_embeddings::Embedding& embedding,
+      base::OnceCallback<void(const std::vector<Category>&)> callback);
 
  private:
   // Callback invoked when all category classifiers have completed execution.
   void OnCategoryClassifiersCompleted(
+      base::OnceCallback<void(const std::vector<Category>&)> callback,
       const GURL& url,
       const std::vector<std::pair<CategoryType, std::optional<float>>>&
           classifier_outputs);
diff --git a/components/page_content_annotations/core/page_content_annotation_type.h b/components/page_content_annotations/core/page_content_annotation_type.h
index 8bc50775..55a2cf73 100644
--- a/components/page_content_annotations/core/page_content_annotation_type.h
+++ b/components/page_content_annotations/core/page_content_annotation_type.h
@@ -37,9 +37,10 @@
 
 enum class CategoryType {
   kEducation = 0,
+  kShopping = 1,
 
   // Add new types above this line.
-  kMaxValue = kEducation,
+  kMaxValue = kShopping,
 };
 
 struct Category {
diff --git a/components/permissions/android/android_permission_util.cc b/components/permissions/android/android_permission_util.cc
index 4c2c799..e0200e816 100644
--- a/components/permissions/android/android_permission_util.cc
+++ b/components/permissions/android/android_permission_util.cc
@@ -7,6 +7,7 @@
 #include <variant>
 
 #include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
 #include "base/auto_reset.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
@@ -18,6 +19,7 @@
 #include "components/permissions/permission_uma_util.h"
 #include "components/permissions/permission_util.h"
 #include "content/public/browser/web_contents.h"
+#include "third_party/jni_zero/default_conversions.h"
 #include "ui/android/window_android.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
@@ -45,22 +47,22 @@
     ContentSettingsType content_settings_type,
     std::vector<std::string>* out) {
   JNIEnv* env = base::android::AttachCurrentThread();
-  base::android::AppendJavaStringArrayToStringVector(
-      env,
+  std::vector<std::string> result =
       Java_PermissionUtil_getRequiredAndroidPermissionsForContentSetting(
-          env, static_cast<int>(content_settings_type)),
-      out);
+          env, static_cast<int>(content_settings_type));
+  out->insert(out->end(), std::make_move_iterator(result.begin()),
+              std::make_move_iterator(result.end()));
 }
 
 void AppendOptionalAndroidPermissionsForContentSetting(
     ContentSettingsType content_settings_type,
     std::vector<std::string>* out) {
   JNIEnv* env = base::android::AttachCurrentThread();
-  base::android::AppendJavaStringArrayToStringVector(
-      env,
+  std::vector<std::string> result =
       Java_PermissionUtil_getOptionalAndroidPermissionsForContentSetting(
-          env, static_cast<int>(content_settings_type)),
-      out);
+          env, static_cast<int>(content_settings_type));
+  out->insert(out->end(), std::make_move_iterator(result.begin()),
+              std::make_move_iterator(result.end()));
 }
 
 bool HasRequiredAndroidPermissionsForContentSetting(
@@ -68,8 +70,7 @@
     ContentSettingsType content_settings_type) {
   JNIEnv* env = base::android::AttachCurrentThread();
   return Java_AndroidPermissionRequester_hasRequiredAndroidPermissionsForContentSetting(
-      env, window_android->GetJavaObject(),
-      static_cast<int>(content_settings_type));
+      env, window_android, static_cast<int>(content_settings_type));
 }
 
 PermissionRepromptState ShouldRepromptUserForPermissions(
@@ -139,7 +140,7 @@
   auto* window_android = web_contents->GetNativeView()->GetWindowAndroid();
   DCHECK(window_android);
   return Java_PermissionUtil_needsLocationPermissionForBluetooth(
-      env, window_android->GetJavaObject());
+      env, window_android);
 }
 
 bool NeedsNearbyDevicesPermissionForBluetooth(
@@ -148,7 +149,7 @@
   auto* window_android = web_contents->GetNativeView()->GetWindowAndroid();
   DCHECK(window_android);
   return Java_PermissionUtil_needsNearbyDevicesPermissionForBluetooth(
-      env, window_android->GetJavaObject());
+      env, window_android);
 }
 
 bool NeedsLocationServicesForBluetooth() {
@@ -162,7 +163,7 @@
   auto* window_android = web_contents->GetNativeView()->GetWindowAndroid();
   DCHECK(window_android);
   return Java_PermissionUtil_canRequestSystemPermissionsForBluetooth(
-      env, window_android->GetJavaObject());
+      env, window_android);
 }
 
 bool HasSystemPermission(ContentSettingsType type,
@@ -193,7 +194,7 @@
   auto* window_android = web_contents->GetNativeView()->GetWindowAndroid();
   DCHECK(window_android);
   return Java_PermissionUtil_canRequestSystemPermission(
-      env, static_cast<int>(type), window_android->GetJavaObject());
+      env, static_cast<int>(type), window_android);
 }
 
 void RequestSystemPermissionsForBluetooth(content::WebContents* web_contents) {
@@ -201,16 +202,15 @@
   auto* window_android = web_contents->GetNativeView()->GetWindowAndroid();
   DCHECK(window_android);
   // TODO(crbug.com/40255210): Pass the callback from native layer.
-  return Java_PermissionUtil_requestSystemPermissionsForBluetooth(
-      env, window_android->GetJavaObject(), nullptr);
+  Java_PermissionUtil_requestSystemPermissionsForBluetooth(env, window_android,
+                                                           nullptr);
 }
 
 void RequestLocationServices(content::WebContents* web_contents) {
   JNIEnv* env = base::android::AttachCurrentThread();
   auto* window_android = web_contents->GetNativeView()->GetWindowAndroid();
   DCHECK(window_android);
-  return Java_PermissionUtil_requestLocationServices(
-      env, window_android->GetJavaObject());
+  Java_PermissionUtil_requestLocationServices(env, window_android);
 }
 
 base::AutoReset<bool> EnableSystemLocationSettingForTesting() {
@@ -226,7 +226,7 @@
 
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_PermissionUtil_handlePermissionPromptAllow(
-      env, window_android->GetJavaObject(), web_contents->GetJavaWebContents(),
+      env, window_android, web_contents,
       static_cast<int>(content_settings_type));
 }
 
@@ -310,18 +310,14 @@
 // valid.
 static void JNI_PermissionUtil_DismissNotificationsPermissionRequest(
     JNIEnv* env,
-    const base::android::JavaRef<jobject>& jweb_contents) {
-  content::WebContents* web_contents =
-      content::WebContents::FromJavaWebContents(jweb_contents);
+    content::WebContents* web_contents) {
   permissions::internal::DismissNotificationsPermissionRequest(web_contents);
 }
 
 static void JNI_PermissionUtil_ResolveNotificationsPermissionRequest(
     JNIEnv* env,
-    const base::android::JavaRef<jobject>& jweb_contents,
+    content::WebContents* web_contents,
     int32_t content_setting) {
-  content::WebContents* web_contents =
-      content::WebContents::FromJavaWebContents(jweb_contents);
   ContentSetting setting = static_cast<ContentSetting>(content_setting);
   permissions::internal::ResolveNotificationsPermissionRequest(web_contents,
                                                                setting);
@@ -333,9 +329,7 @@
 // omnibox.
 static void JNI_PermissionUtil_NotifyQuietIconDismissed(
     JNIEnv* env,
-    const base::android::JavaRef<jobject>& jweb_contents) {
-  content::WebContents* web_contents =
-      content::WebContents::FromJavaWebContents(jweb_contents);
+    content::WebContents* web_contents) {
   if (!web_contents) {
     return;
   }
diff --git a/components/permissions/android/bluetooth_chooser_android.cc b/components/permissions/android/bluetooth_chooser_android.cc
index 50b5ee6..ac5a916a 100644
--- a/components/permissions/android/bluetooth_chooser_android.cc
+++ b/components/permissions/android/bluetooth_chooser_android.cc
@@ -140,17 +140,15 @@
 }
 
 void BluetoothChooserAndroid::OnDialogFinished(
-    JNIEnv* env,
     content::BluetoothChooserEvent event,
-    const JavaRef<jstring>& device_id) {
+    const std::string& device_id) {
   switch (event) {
     case content::BluetoothChooserEvent::DENIED_PERMISSION:
     case content::BluetoothChooserEvent::CANCELLED:
       event_handler_.Run(event, "");
       return;
     case content::BluetoothChooserEvent::SELECTED:
-      event_handler_.Run(
-          event, base::android::ConvertJavaStringToUTF8(env, device_id));
+      event_handler_.Run(event, device_id);
       return;
     default:
       NOTREACHED();
@@ -165,17 +163,17 @@
   RestartSearch();
 }
 
-void BluetoothChooserAndroid::ShowBluetoothOverviewLink(JNIEnv* env) {
+void BluetoothChooserAndroid::ShowBluetoothOverviewLink() {
   OpenURL(kChooserBluetoothOverviewURL);
   event_handler_.Run(content::BluetoothChooserEvent::SHOW_OVERVIEW_HELP, "");
 }
 
-void BluetoothChooserAndroid::ShowBluetoothAdapterOffLink(JNIEnv* env) {
+void BluetoothChooserAndroid::ShowBluetoothAdapterOffLink() {
   OpenURL(kChooserBluetoothOverviewURL);
   event_handler_.Run(content::BluetoothChooserEvent::SHOW_ADAPTER_OFF_HELP, "");
 }
 
-void BluetoothChooserAndroid::ShowNeedLocationPermissionLink(JNIEnv* env) {
+void BluetoothChooserAndroid::ShowNeedLocationPermissionLink() {
   OpenURL(kChooserBluetoothOverviewURL);
   event_handler_.Run(content::BluetoothChooserEvent::SHOW_NEED_LOCATION_HELP,
                      "");
diff --git a/components/permissions/android/bluetooth_chooser_android.h b/components/permissions/android/bluetooth_chooser_android.h
index aa43fed..061f108 100644
--- a/components/permissions/android/bluetooth_chooser_android.h
+++ b/components/permissions/android/bluetooth_chooser_android.h
@@ -54,18 +54,17 @@
                          int signal_strength_level) override;
 
   // Report the dialog's result.
-  void OnDialogFinished(JNIEnv* env,
-                        content::BluetoothChooserEvent event,
-                        const base::android::JavaRef<jstring>& device_id);
+  void OnDialogFinished(content::BluetoothChooserEvent event,
+                        const std::string& device_id);
 
   // Notify bluetooth stack that the search needs to be re-issued.
   void RestartSearch();
   // Calls RestartSearch(). Unused JNI parameters enable calls from Java.
   void RestartSearch(JNIEnv*);
 
-  void ShowBluetoothOverviewLink(JNIEnv* env);
-  void ShowBluetoothAdapterOffLink(JNIEnv* env);
-  void ShowNeedLocationPermissionLink(JNIEnv* env);
+  void ShowBluetoothOverviewLink();
+  void ShowBluetoothAdapterOffLink();
+  void ShowNeedLocationPermissionLink();
 
   static std::unique_ptr<BluetoothChooserAndroid> CreateForTesting(
       content::RenderFrameHost* frame,
diff --git a/components/permissions/android/java/src/org/chromium/components/permissions/AndroidPermissionRequester.java b/components/permissions/android/java/src/org/chromium/components/permissions/AndroidPermissionRequester.java
index f5d7a710..324b69c 100644
--- a/components/permissions/android/java/src/org/chromium/components/permissions/AndroidPermissionRequester.java
+++ b/components/permissions/android/java/src/org/chromium/components/permissions/AndroidPermissionRequester.java
@@ -15,6 +15,7 @@
 import android.widget.TextView;
 
 import org.jni_zero.CalledByNative;
+import org.jni_zero.JniType;
 
 import org.chromium.base.ApkInfo;
 import org.chromium.base.metrics.RecordHistogram;
@@ -85,7 +86,7 @@
      */
     @CalledByNative
     public static boolean hasRequiredAndroidPermissionsForContentSetting(
-            AndroidPermissionDelegate permissionDelegate,
+            @JniType("ui::WindowAndroid*") AndroidPermissionDelegate permissionDelegate,
             @ContentSettingsType.EnumType int contentSettingsType) {
         Set<String> missingPermissions =
                 filterPermissionsKeepMissing(
@@ -163,10 +164,10 @@
                         for (int i = 0; i < grantResults.length; i++) {
                             String histogramName = null;
                             switch (permissions[i]) {
-                                    // Even in the case of Fine location, Coarse
-                                    // location permission is also granted. We don't
-                                    // need to record histogram for both of them
-                                    // separately.
+                                // Even in the case of Fine location, Coarse
+                                // location permission is also granted. We don't
+                                // need to record histogram for both of them
+                                // separately.
                                 case "android.permission.ACCESS_COARSE_LOCATION":
                                     histogramName =
                                             "Permissions.AndroidSystemLevel.Location.Prompt.Shown";
diff --git a/components/permissions/android/java/src/org/chromium/components/permissions/BluetoothChooserDialog.java b/components/permissions/android/java/src/org/chromium/components/permissions/BluetoothChooserDialog.java
index b312abf4..ff86ea7e 100644
--- a/components/permissions/android/java/src/org/chromium/components/permissions/BluetoothChooserDialog.java
+++ b/components/permissions/android/java/src/org/chromium/components/permissions/BluetoothChooserDialog.java
@@ -541,7 +541,7 @@
         void onDialogFinished(
                 long nativeBluetoothChooserAndroid,
                 @JniType("content::BluetoothChooserEvent") int eventType,
-                String deviceId);
+                @JniType("std::string") String deviceId);
 
         void restartSearch(long nativeBluetoothChooserAndroid);
 
diff --git a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionUtil.java b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionUtil.java
index 6e66b2c..172106b4 100644
--- a/components/permissions/android/java/src/org/chromium/components/permissions/PermissionUtil.java
+++ b/components/permissions/android/java/src/org/chromium/components/permissions/PermissionUtil.java
@@ -10,6 +10,7 @@
 import android.os.Build;
 
 import org.jni_zero.CalledByNative;
+import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
 import org.chromium.base.metrics.RecordHistogram;
@@ -111,6 +112,7 @@
      *     returned for different content setting types are disjunct.
      */
     @CalledByNative
+    @JniType("std::vector<std::string>")
     public static String[] getRequiredAndroidPermissionsForContentSetting(int contentSettingType) {
         switch (contentSettingType) {
             case ContentSettingsType.GEOLOCATION, ContentSettingsType.GEOLOCATION_WITH_OPTIONS:
@@ -162,6 +164,7 @@
      *     returned for different content setting types are disjunct.
      */
     @CalledByNative
+    @JniType("std::vector<std::string>")
     public static String[] getOptionalAndroidPermissionsForContentSetting(int contentSettingType) {
         switch (contentSettingType) {
             case ContentSettingsType.GEOLOCATION, ContentSettingsType.GEOLOCATION_WITH_OPTIONS:
@@ -192,7 +195,7 @@
 
     @CalledByNative
     public static boolean canRequestSystemPermission(
-            int contentSettingType, WindowAndroid windowAndroid) {
+            int contentSettingType, @JniType("ui::WindowAndroid*") WindowAndroid windowAndroid) {
         String[] permissions = getRequiredAndroidPermissionsForContentSetting(contentSettingType);
         for (String permission : permissions) {
             if (!windowAndroid.canRequestPermission(permission)) {
@@ -203,13 +206,15 @@
     }
 
     @CalledByNative
-    public static boolean needsLocationPermissionForBluetooth(WindowAndroid windowAndroid) {
+    public static boolean needsLocationPermissionForBluetooth(
+            @JniType("ui::WindowAndroid*") WindowAndroid windowAndroid) {
         return Build.VERSION.SDK_INT < Build.VERSION_CODES.S
                 && !windowAndroid.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION);
     }
 
     @CalledByNative
-    public static boolean needsNearbyDevicesPermissionForBluetooth(WindowAndroid windowAndroid) {
+    public static boolean needsNearbyDevicesPermissionForBluetooth(
+            @JniType("ui::WindowAndroid*") WindowAndroid windowAndroid) {
         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
                 && (!windowAndroid.hasPermission(Manifest.permission.BLUETOOTH_SCAN)
                         || !windowAndroid.hasPermission(Manifest.permission.BLUETOOTH_CONNECT));
@@ -225,7 +230,8 @@
     }
 
     @CalledByNative
-    public static boolean canRequestSystemPermissionsForBluetooth(WindowAndroid windowAndroid) {
+    public static boolean canRequestSystemPermissionsForBluetooth(
+            @JniType("ui::WindowAndroid*") WindowAndroid windowAndroid) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
             return windowAndroid.canRequestPermission(Manifest.permission.BLUETOOTH_SCAN)
                     && windowAndroid.canRequestPermission(Manifest.permission.BLUETOOTH_CONNECT);
@@ -236,7 +242,8 @@
 
     @CalledByNative
     public static void requestSystemPermissionsForBluetooth(
-            WindowAndroid windowAndroid, PermissionCallback callback) {
+            @JniType("ui::WindowAndroid*") WindowAndroid windowAndroid,
+            PermissionCallback callback) {
         String[] requiredPermissions;
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
             requiredPermissions =
@@ -254,7 +261,8 @@
     }
 
     @CalledByNative
-    public static void requestLocationServices(WindowAndroid windowAndroid) {
+    public static void requestLocationServices(
+            @JniType("ui::WindowAndroid*") WindowAndroid windowAndroid) {
         assumeNonNull(windowAndroid.getActivity().get())
                 .startActivity(LocationUtils.getInstance().getSystemLocationSettingsIntent());
     }
@@ -278,8 +286,8 @@
      */
     @CalledByNative
     public static void handlePermissionPromptAllow(
-            WindowAndroid window,
-            WebContents webContents,
+            @JniType("ui::WindowAndroid*") WindowAndroid window,
+            @JniType("content::WebContents*") WebContents webContents,
             @ContentSettingsType.EnumType int contentSettingsType) {
         requestAndResolveNotificationsPermissionRequest(
                 window,
@@ -369,11 +377,13 @@
     @NativeMethods
     public interface Natives {
         void resolveNotificationsPermissionRequest(
-                WebContents webContents, @ContentSetting int contentSetting);
+                @JniType("content::WebContents*") WebContents webContents,
+                @ContentSetting int contentSetting);
 
-        void dismissNotificationsPermissionRequest(WebContents webContents);
+        void dismissNotificationsPermissionRequest(
+                @JniType("content::WebContents*") WebContents webContents);
 
-        void notifyQuietIconDismissed(WebContents webContents);
+        void notifyQuietIconDismissed(@JniType("content::WebContents*") WebContents webContents);
     }
 
     /**
diff --git a/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.cc b/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.cc
index 3634a072..f2a173cb 100644
--- a/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.cc
+++ b/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.cc
@@ -45,12 +45,10 @@
 
 EitherGroupID JavaSyncOrLocalGroupIdToEitherGroupId(
     JNIEnv* env,
-    const JavaRef<jstring>& j_sync_group_id,
+    const std::string& sync_group_id,
     const JavaRef<jobject>& j_local_group_id) {
   if (j_local_group_id.is_null()) {
-    std::string sync_group_id_str =
-        ConvertJavaStringToUTF8(env, j_sync_group_id);
-    return base::Uuid::ParseLowercase(sync_group_id_str);
+    return base::Uuid::ParseLowercase(sync_group_id);
   } else {
     LocalTabGroupID local_group_id =
         TabGroupSyncConversionsBridge::FromJavaTabGroupId(env,
diff --git a/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h b/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h
index 51a284e..37573e5 100644
--- a/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h
+++ b/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h
@@ -34,7 +34,7 @@
 // Converts a Java sync or local group ID to EitherGroupID.
 EitherGroupID JavaSyncOrLocalGroupIdToEitherGroupId(
     JNIEnv* env,
-    const JavaRef<jstring>& j_sync_group_id,
+    const std::string& sync_group_id,
     const JavaRef<jobject>& j_local_group_id);
 
 }  // namespace tab_groups
diff --git a/components/security_interstitials/content/insecure_form_navigation_throttle.cc b/components/security_interstitials/content/insecure_form_navigation_throttle.cc
index 632204a7..e74d435 100644
--- a/components/security_interstitials/content/insecure_form_navigation_throttle.cc
+++ b/components/security_interstitials/content/insecure_form_navigation_throttle.cc
@@ -20,6 +20,7 @@
 #include "net/http/http_response_headers.h"
 #include "net/http/http_status_code.h"
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
+#include "third_party/blink/public/common/features.h"
 #include "url/origin.h"
 #include "url/url_constants.h"
 
@@ -89,21 +90,31 @@
     return content::NavigationThrottle::PROCEED;
   }
 
-  // If the form is in a prerendered page, cancel it. Even though the form
-  // submission wouldn't include user data (a prerender cannot provide any
-  // input), the prerendered form submission could still leak data over the
-  // network (e.g. the path).
-  // There's an exception to this: Reloading a GET form will proceed since a
-  // prerender shouldn't check the InsecureFormTabStorage, which is a per-tab
-  // object. This is done in the check above.
-  if (handle->IsInPrerenderedMainFrame()) {
-    return content::NavigationThrottle::CANCEL;
+  if (!base::FeatureList::IsEnabled(
+          blink::features::kPrerenderActivationByFormSubmission)) {
+    // If the form is in a prerendered page, cancel it. Even though the form
+    // submission wouldn't include user data (a prerender cannot provide any
+    // input), the prerendered form submission could still leak data over the
+    // network (e.g. the path).
+    // There's an exception to this: Reloading a GET form will proceed since a
+    // prerender shouldn't check the InsecureFormTabStorage, which is a per-tab
+    // object. This is done in the check above.
+    if (handle->IsInPrerenderedMainFrame()) {
+      return content::NavigationThrottle::CANCEL;
+    }
   }
 
-  // If user has just chosen to proceed on an interstitial, we don't show
-  // another one.
-  if (tab_storage && tab_storage->IsProceeding()) {
-    return content::NavigationThrottle::PROCEED;
+  // `InsecureFormTabStorage` is stored on WebContents, and the state is
+  // configured when non-prerendering, so the prerendering shouldn't refer to
+  // the state to determine the outcome of the `InsecureFormNavigationThrottle`.
+  if (!base::FeatureList::IsEnabled(
+          blink::features::kPrerenderActivationByFormSubmission) ||
+      !handle->IsInPrerenderedMainFrame()) {
+    // If user has just chosen to proceed on an interstitial, we don't show
+    // another one.
+    if (tab_storage && tab_storage->IsProceeding()) {
+      return content::NavigationThrottle::PROCEED;
+    }
   }
 
   // Do not set special error page HTML for insecure forms in subframes; those
@@ -139,6 +150,14 @@
     return content::NavigationThrottle::PROCEED;
   }
 
+  if (base::FeatureList::IsEnabled(
+          blink::features::kPrerenderActivationByFormSubmission) &&
+      handle->IsInPrerenderedMainFrame()) {
+    // Cancel prerendering to avoid logging any metrics or showing an
+    // interstitial for an invisible page.
+    return content::NavigationThrottle::CANCEL;
+  }
+
   std::unique_ptr<InsecureFormBlockingPage> blocking_page =
       blocking_page_factory_->CreateInsecureFormBlockingPage(contents,
                                                              handle->GetURL());
diff --git a/components/sync/android/java/src/org/chromium/components/sync/SyncServiceImpl.java b/components/sync/android/java/src/org/chromium/components/sync/SyncServiceImpl.java
index 19fd49a..b9e2c780 100644
--- a/components/sync/android/java/src/org/chromium/components/sync/SyncServiceImpl.java
+++ b/components/sync/android/java/src/org/chromium/components/sync/SyncServiceImpl.java
@@ -461,14 +461,14 @@
 
     @CalledByNative
     private static void onGetTypesWithUnsyncedDataResult(
-            Callback<Set<Integer>> callback, int[] types) {
+            Callback<Set<Integer>> callback, @JniType("std::vector<int32_t>") int[] types) {
         callback.onResult(dataTypeArrayToSet(types));
     }
 
     @CalledByNative
     private static void onGetLocalDataDescriptionsResult(
             Callback<HashMap<Integer, LocalDataDescription>> callback,
-            @JniType("std::vector<int>") int[] dataTypes,
+            @JniType("std::vector<int32_t>") int[] dataTypes,
             @JniType("std::vector<syncer::LocalDataDescription>")
                     LocalDataDescription[] localDataDescriptions) {
         HashMap<Integer, LocalDataDescription> localDataDescription =
@@ -481,7 +481,8 @@
 
     /** Invokes the onResult method of the callback from native code. */
     @CalledByNative
-    private static void onGetAllNodesResult(Callback<JSONArray> callback, String serializedNodes) {
+    private static void onGetAllNodesResult(
+            Callback<JSONArray> callback, @JniType("std::string") String serializedNodes) {
         try {
             callback.onResult(new JSONArray(serializedNodes));
         } catch (JSONException e) {
@@ -548,8 +549,10 @@
         void setInitialSyncFeatureSetupComplete(
                 long nativeSyncServiceAndroidBridge, int syncFirstSetupCompleteSource);
 
+        @JniType("std::vector<int32_t>")
         int[] getActiveDataTypes(long nativeSyncServiceAndroidBridge);
 
+        @JniType("std::vector<int32_t>")
         int[] getSelectedTypes(long nativeSyncServiceAndroidBridge);
 
         void getTypesWithUnsyncedData(
@@ -557,10 +560,11 @@
 
         void getLocalDataDescriptions(
                 long nativeSyncServiceAndroidBridge,
-                int[] types,
+                @JniType("std::vector<int32_t>") int[] types,
                 Callback<HashMap<Integer, LocalDataDescription>> callback);
 
-        void triggerLocalDataMigration(long nativeSyncServiceAndroidBridge, int[] types);
+        void triggerLocalDataMigration(
+                long nativeSyncServiceAndroidBridge, @JniType("std::vector<int32_t>") int[] types);
 
         boolean isTypeManagedByPolicy(long nativeSyncServiceAndroidBridge, int type);
 
@@ -569,7 +573,7 @@
         void setSelectedTypes(
                 long nativeSyncServiceAndroidBridge,
                 boolean syncEverything,
-                int[] userSelectableTypeArray);
+                @JniType("std::vector<int32_t>") int[] userSelectableTypeArray);
 
         void setSelectedType(
                 long nativeSyncServiceAndroidBridge,
@@ -596,9 +600,11 @@
 
         int getUserActionableError(long nativeSyncServiceAndroidBridge);
 
-        void setEncryptionPassphrase(long nativeSyncServiceAndroidBridge, String passphrase);
+        void setEncryptionPassphrase(
+                long nativeSyncServiceAndroidBridge, @JniType("std::string") String passphrase);
 
-        boolean setDecryptionPassphrase(long nativeSyncServiceAndroidBridge, String passphrase);
+        boolean setDecryptionPassphrase(
+                long nativeSyncServiceAndroidBridge, @JniType("std::string") String passphrase);
 
         long getExplicitPassphraseTime(long nativeSyncServiceAndroidBridge);
 
@@ -625,6 +631,7 @@
         long getLastSyncedTimeForDebugging(long nativeSyncServiceAndroidBridge);
 
         void keepAccountSettingsPrefsOnlyForUsers(
-                long nativeSyncServiceAndroidBridge, String[] gaiaIds);
+                long nativeSyncServiceAndroidBridge,
+                @JniType("std::vector<std::string>") String[] gaiaIds);
     }
 }
diff --git a/components/sync/android/sync_service_android_bridge.cc b/components/sync/android/sync_service_android_bridge.cc
index e02f7a3..787f37c 100644
--- a/components/sync/android/sync_service_android_bridge.cc
+++ b/components/sync/android/sync_service_android_bridge.cc
@@ -30,6 +30,7 @@
 #include "components/sync/service/sync_user_settings.h"
 #include "google_apis/gaia/gaia_id.h"
 #include "google_apis/gaia/google_service_auth_error.h"
+#include "third_party/jni_zero/default_conversions.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
 #include "components/sync/android/jni_headers/SyncServiceImpl_jni.h"
@@ -52,32 +53,30 @@
   return static_cast<DataType>(type);
 }
 
-ScopedJavaLocalRef<jintArray> DataTypeSetToJavaIntArray(JNIEnv* env,
-                                                        DataTypeSet types) {
-  std::vector<int> type_vector(types.begin(), types.end());
-  return base::android::ToJavaIntArray(env, type_vector);
+std::vector<int32_t> DataTypeSetToVector(DataTypeSet types) {
+  std::vector<int32_t> type_vector;
+  for (DataType type : types) {
+    type_vector.push_back(static_cast<int32_t>(type));
+  }
+  return type_vector;
 }
 
-DataTypeSet JavaIntArrayToDataTypeSet(JNIEnv* env,
-                                      const JavaRef<jintArray>& types) {
-  std::vector<int> types_vector;
-  base::android::JavaIntArrayToIntVector(env, types, &types_vector);
+DataTypeSet VectorToDataTypeSet(const std::vector<int32_t>& types_vector) {
   DataTypeSet data_type_set;
-  for (int type : types_vector) {
+  for (int32_t type : types_vector) {
     data_type_set.Put(IntToDataTypeChecked(type));
   }
   return data_type_set;
 }
 
-ScopedJavaLocalRef<jintArray> UserSelectableTypeSetToJavaIntArray(
-    JNIEnv* env,
+std::vector<int32_t> UserSelectableTypeSetToVector(
     UserSelectableTypeSet types) {
-  std::vector<int> type_vector;
+  std::vector<int32_t> type_vector;
   type_vector.reserve(types.size());
   for (UserSelectableType type : types) {
-    type_vector.push_back(static_cast<int>(type));
+    type_vector.push_back(static_cast<int32_t>(type));
   }
-  return base::android::ToJavaIntArray(env, type_vector);
+  return type_vector;
 }
 
 // Native callback for the JNI GetTypesWithUnsyncedData method. When
@@ -92,19 +91,19 @@
     types.Put(type);
   }
   Java_SyncServiceImpl_onGetTypesWithUnsyncedDataResult(
-      env, callback, DataTypeSetToJavaIntArray(env, types));
+      env, callback, DataTypeSetToVector(types));
 }
 
 void NativeGetLocalDataDescriptionsCallback(
     JNIEnv* env,
     const base::android::ScopedJavaGlobalRef<jobject>& callback,
     std::map<DataType, LocalDataDescription> localDataDescription) {
-  std::vector<int> data_types;
+  std::vector<int32_t> data_types;
   data_types.reserve(localDataDescription.size());
   std::vector<LocalDataDescription> local_data_descriptions;
   local_data_descriptions.reserve(localDataDescription.size());
   for (const auto& [data_type, description] : localDataDescription) {
-    data_types.push_back(data_type);
+    data_types.push_back(static_cast<int32_t>(data_type));
     local_data_descriptions.push_back(description);
   }
 
@@ -125,8 +124,7 @@
     json_string = std::string();
   }
 
-  Java_SyncServiceImpl_onGetAllNodesResult(
-      env, callback, ConvertUTF8ToJavaString(env, json_string));
+  Java_SyncServiceImpl_onGetAllNodesResult(env, callback, json_string);
 }
 
 UserSelectableType IntToUserSelectableTypeChecked(int type) {
@@ -180,32 +178,30 @@
 }
 
 void SyncServiceAndroidBridge::AcknowledgeBookmarksLimitExceededError(
-    JNIEnv* env,
     int32_t source) {
   native_sync_service_->AcknowledgeBookmarksLimitExceededError(
       static_cast<SyncService::BookmarksLimitExceededHelpClickedSource>(
           source));
 }
 
-bool SyncServiceAndroidBridge::IsSyncFeatureEnabled(JNIEnv* env) {
+bool SyncServiceAndroidBridge::IsSyncFeatureEnabled() {
   return native_sync_service_->IsSyncFeatureEnabled();
 }
 
-bool SyncServiceAndroidBridge::IsSyncFeatureActive(JNIEnv* env) {
+bool SyncServiceAndroidBridge::IsSyncFeatureActive() {
   return native_sync_service_->IsSyncFeatureActive();
 }
 
-bool SyncServiceAndroidBridge::IsSyncDisabledByEnterprisePolicy(JNIEnv* env) {
+bool SyncServiceAndroidBridge::IsSyncDisabledByEnterprisePolicy() {
   return native_sync_service_->HasDisableReason(
       SyncService::DISABLE_REASON_ENTERPRISE_POLICY);
 }
 
-bool SyncServiceAndroidBridge::IsEngineInitialized(JNIEnv* env) {
+bool SyncServiceAndroidBridge::IsEngineInitialized() {
   return native_sync_service_->IsEngineInitialized();
 }
 
-void SyncServiceAndroidBridge::SetSetupInProgress(JNIEnv* env,
-                                                  bool in_progress) {
+void SyncServiceAndroidBridge::SetSetupInProgress(bool in_progress) {
   if (!in_progress) {
     sync_blocker_.reset();
     return;
@@ -216,30 +212,26 @@
   }
 }
 
-bool SyncServiceAndroidBridge::IsInitialSyncFeatureSetupComplete(JNIEnv* env) {
+bool SyncServiceAndroidBridge::IsInitialSyncFeatureSetupComplete() {
   return native_sync_service_->GetUserSettings()
       ->IsInitialSyncFeatureSetupComplete();
 }
 
 void SyncServiceAndroidBridge::SetInitialSyncFeatureSetupComplete(
-    JNIEnv* env,
     int32_t source) {
   native_sync_service_->GetUserSettings()->SetInitialSyncFeatureSetupComplete(
       static_cast<SyncFirstSetupCompleteSource>(source));
 }
 
-ScopedJavaLocalRef<jintArray> SyncServiceAndroidBridge::GetActiveDataTypes(
-    JNIEnv* env) {
-  return DataTypeSetToJavaIntArray(env,
-                                   native_sync_service_->GetActiveDataTypes());
+std::vector<int32_t> SyncServiceAndroidBridge::GetActiveDataTypes() {
+  return DataTypeSetToVector(native_sync_service_->GetActiveDataTypes());
 }
 
-ScopedJavaLocalRef<jintArray> SyncServiceAndroidBridge::GetSelectedTypes(
-    JNIEnv* env) {
+std::vector<int32_t> SyncServiceAndroidBridge::GetSelectedTypes() {
   UserSelectableTypeSet user_selectable_types;
   user_selectable_types =
       native_sync_service_->GetUserSettings()->GetSelectedTypes();
-  return UserSelectableTypeSetToJavaIntArray(env, user_selectable_types);
+  return UserSelectableTypeSetToVector(user_selectable_types);
 }
 
 void SyncServiceAndroidBridge::GetTypesWithUnsyncedData(
@@ -255,40 +247,35 @@
 
 void SyncServiceAndroidBridge::GetLocalDataDescriptions(
     JNIEnv* env,
-    const base::android::JavaRef<jintArray>& types,
+    const std::vector<int32_t>& types,
     const base::android::JavaRef<jobject>& callback) {
   base::android::ScopedJavaGlobalRef<jobject> java_callback;
   java_callback.Reset(env, callback);
 
   native_sync_service_->GetLocalDataDescriptions(
-      JavaIntArrayToDataTypeSet(env, types),
+      VectorToDataTypeSet(types),
       base::BindOnce(&NativeGetLocalDataDescriptionsCallback, env,
                      java_callback));
 }
 
 void SyncServiceAndroidBridge::TriggerLocalDataMigration(
-    JNIEnv* env,
-    const JavaRef<jintArray>& types) {
-  native_sync_service_->TriggerLocalDataMigration(
-      JavaIntArrayToDataTypeSet(env, types));
+    const std::vector<int32_t>& types) {
+  native_sync_service_->TriggerLocalDataMigration(VectorToDataTypeSet(types));
 }
 
-bool SyncServiceAndroidBridge::IsTypeManagedByPolicy(JNIEnv* env,
-                                                     int32_t type) {
+bool SyncServiceAndroidBridge::IsTypeManagedByPolicy(int32_t type) {
   return native_sync_service_->GetUserSettings()->IsTypeManagedByPolicy(
       IntToUserSelectableTypeChecked(type));
 }
 
-bool SyncServiceAndroidBridge::IsTypeManagedByCustodian(JNIEnv* env,
-                                                        int32_t type) {
+bool SyncServiceAndroidBridge::IsTypeManagedByCustodian(int32_t type) {
   return native_sync_service_->GetUserSettings()->IsTypeManagedByCustodian(
       IntToUserSelectableTypeChecked(type));
 }
 
 void SyncServiceAndroidBridge::SetSelectedTypes(
-    JNIEnv* env,
     bool sync_everything,
-    const JavaRef<jintArray>& user_selectable_type_array) {
+    const std::vector<int32_t>& user_selectable_types_vector) {
   if (native_sync_service_->GetAccountInfo().account_id.empty()) {
     // This function shouldn't be called while signed out, but evidence suggests
     // it sometimes does get called.
@@ -299,12 +286,8 @@
     return;
   }
 
-  std::vector<int> types_vector;
-  base::android::JavaIntArrayToIntVector(env, user_selectable_type_array,
-                                         &types_vector);
-
   UserSelectableTypeSet user_selectable_types;
-  for (int type : types_vector) {
+  for (int32_t type : user_selectable_types_vector) {
     user_selectable_types.Put(IntToUserSelectableTypeChecked(type));
   }
 
@@ -312,9 +295,7 @@
       sync_everything, user_selectable_types);
 }
 
-void SyncServiceAndroidBridge::SetSelectedType(JNIEnv* env,
-                                               int32_t type,
-                                               bool is_type_on) {
+void SyncServiceAndroidBridge::SetSelectedType(int32_t type, bool is_type_on) {
   if (native_sync_service_->GetAccountInfo().account_id.empty()) {
     // This function shouldn't be called while signed out, but evidence suggests
     // it sometimes does get called.
@@ -329,41 +310,39 @@
       IntToUserSelectableTypeChecked(type), is_type_on);
 }
 
-bool SyncServiceAndroidBridge::IsCustomPassphraseAllowed(JNIEnv* env) {
+bool SyncServiceAndroidBridge::IsCustomPassphraseAllowed() {
   return native_sync_service_->GetUserSettings()->IsCustomPassphraseAllowed();
 }
 
-bool SyncServiceAndroidBridge::IsEncryptEverythingEnabled(JNIEnv* env) {
+bool SyncServiceAndroidBridge::IsEncryptEverythingEnabled() {
   return native_sync_service_->GetUserSettings()->IsEncryptEverythingEnabled();
 }
 
-bool SyncServiceAndroidBridge::IsPassphraseRequiredForPreferredDataTypes(
-    JNIEnv* env) {
+bool SyncServiceAndroidBridge::IsPassphraseRequiredForPreferredDataTypes() {
   return native_sync_service_->GetUserSettings()
       ->IsPassphraseRequiredForPreferredDataTypes();
 }
 
-bool SyncServiceAndroidBridge::IsTrustedVaultKeyRequired(JNIEnv* env) {
+bool SyncServiceAndroidBridge::IsTrustedVaultKeyRequired() {
   return native_sync_service_->GetUserSettings()->IsTrustedVaultKeyRequired();
 }
 
-bool SyncServiceAndroidBridge::IsTrustedVaultKeyRequiredForPreferredDataTypes(
-    JNIEnv* env) {
+bool SyncServiceAndroidBridge::
+    IsTrustedVaultKeyRequiredForPreferredDataTypes() {
   return native_sync_service_->GetUserSettings()
       ->IsTrustedVaultKeyRequiredForPreferredDataTypes();
 }
 
-bool SyncServiceAndroidBridge::IsTrustedVaultRecoverabilityDegraded(
-    JNIEnv* env) {
+bool SyncServiceAndroidBridge::IsTrustedVaultRecoverabilityDegraded() {
   return native_sync_service_->GetUserSettings()
       ->IsTrustedVaultRecoverabilityDegraded();
 }
 
-bool SyncServiceAndroidBridge::IsUsingExplicitPassphrase(JNIEnv* env) {
+bool SyncServiceAndroidBridge::IsUsingExplicitPassphrase() {
   return native_sync_service_->GetUserSettings()->IsUsingExplicitPassphrase();
 }
 
-int32_t SyncServiceAndroidBridge::GetPassphraseType(JNIEnv* env) {
+int32_t SyncServiceAndroidBridge::GetPassphraseType() {
   // TODO(crbug.com/40923935): Mapping nullopt -> kImplicitPassphrase preserves
   // the historic behavior, but ideally we should propagate the nullopt state to
   // Java.
@@ -372,29 +351,26 @@
           PassphraseType::kImplicitPassphrase));
 }
 
-int32_t SyncServiceAndroidBridge::GetTransportState(JNIEnv* env) {
+int32_t SyncServiceAndroidBridge::GetTransportState() {
   return static_cast<int32_t>(native_sync_service_->GetTransportState());
 }
 
-int32_t SyncServiceAndroidBridge::GetUserActionableError(JNIEnv* env) {
+int32_t SyncServiceAndroidBridge::GetUserActionableError() {
   return static_cast<int32_t>(native_sync_service_->GetUserActionableError());
 }
 
 void SyncServiceAndroidBridge::SetEncryptionPassphrase(
-    JNIEnv* env,
-    const JavaRef<jstring>& passphrase) {
-  native_sync_service_->GetUserSettings()->SetEncryptionPassphrase(
-      ConvertJavaStringToUTF8(env, passphrase));
+    const std::string& passphrase) {
+  native_sync_service_->GetUserSettings()->SetEncryptionPassphrase(passphrase);
 }
 
 bool SyncServiceAndroidBridge::SetDecryptionPassphrase(
-    JNIEnv* env,
-    const JavaRef<jstring>& passphrase) {
+    const std::string& passphrase) {
   return native_sync_service_->GetUserSettings()->SetDecryptionPassphrase(
-      ConvertJavaStringToUTF8(env, passphrase));
+      passphrase);
 }
 
-int64_t SyncServiceAndroidBridge::GetExplicitPassphraseTime(JNIEnv* env) {
+int64_t SyncServiceAndroidBridge::GetExplicitPassphraseTime() {
   return native_sync_service_->GetUserSettings()
       ->GetExplicitPassphraseTime()
       .InMillisecondsSinceUnixEpoch();
@@ -408,7 +384,7 @@
       base::BindOnce(&NativeGetAllNodesCallback, env, java_callback));
 }
 
-GoogleServiceAuthError SyncServiceAndroidBridge::GetAuthError(JNIEnv* env) {
+GoogleServiceAuthError SyncServiceAndroidBridge::GetAuthError() {
   return native_sync_service_->GetAuthError();
 }
 
@@ -420,37 +396,37 @@
              : ConvertToJavaCoreAccountInfo(env, account_info);
 }
 
-bool SyncServiceAndroidBridge::HasSyncConsent(JNIEnv* env) {
+bool SyncServiceAndroidBridge::HasSyncConsent() {
   return native_sync_service_->HasSyncConsent();
 }
 
-bool SyncServiceAndroidBridge::IsPassphrasePromptMutedForCurrentProductVersion(
-    JNIEnv* env) {
+bool SyncServiceAndroidBridge::
+    IsPassphrasePromptMutedForCurrentProductVersion() {
   return native_sync_service_->GetUserSettings()
       ->IsPassphrasePromptMutedForCurrentProductVersion();
 }
 
 void SyncServiceAndroidBridge::
-    MarkPassphrasePromptMutedForCurrentProductVersion(JNIEnv* env) {
+    MarkPassphrasePromptMutedForCurrentProductVersion() {
   native_sync_service_->GetUserSettings()
       ->MarkPassphrasePromptMutedForCurrentProductVersion();
 }
 
-bool SyncServiceAndroidBridge::HasKeepEverythingSynced(JNIEnv* env) {
+bool SyncServiceAndroidBridge::HasKeepEverythingSynced() {
   return native_sync_service_->GetUserSettings()->IsSyncEverythingEnabled();
 }
 
-bool SyncServiceAndroidBridge::ShouldOfferTrustedVaultOptIn(JNIEnv* env) {
+bool SyncServiceAndroidBridge::ShouldOfferTrustedVaultOptIn() {
   return syncer::ShouldOfferTrustedVaultOptIn(native_sync_service_);
 }
 
-void SyncServiceAndroidBridge::TriggerRefresh(JNIEnv* env) {
+void SyncServiceAndroidBridge::TriggerRefresh() {
   native_sync_service_->TriggerRefresh(
       SyncService::TriggerRefreshSource::kAndroidSyncServiceBridge,
       DataTypeSet::All());
 }
 
-int64_t SyncServiceAndroidBridge::GetLastSyncedTimeForDebugging(JNIEnv* env) {
+int64_t SyncServiceAndroidBridge::GetLastSyncedTimeForDebugging() {
   base::Time last_sync_time =
       native_sync_service_->GetLastSyncedTimeForDebugging();
   return static_cast<int64_t>(
@@ -458,10 +434,7 @@
 }
 
 void SyncServiceAndroidBridge::KeepAccountSettingsPrefsOnlyForUsers(
-    JNIEnv* env,
-    const base::android::JavaRef<jobjectArray>& gaia_ids_array) {
-  std::vector<std::string> gaia_id_strings;
-  AppendJavaStringArrayToStringVector(env, gaia_ids_array, &gaia_id_strings);
+    const std::vector<std::string>& gaia_id_strings) {
   std::vector<GaiaId> gaia_ids;
   for (const std::string& gaia_id_string : gaia_id_strings) {
     gaia_ids.emplace_back(gaia_id_string);
diff --git a/components/sync/android/sync_service_android_bridge.h b/components/sync/android/sync_service_android_bridge.h
index 373e98d..a194124 100644
--- a/components/sync/android/sync_service_android_bridge.h
+++ b/components/sync/android/sync_service_android_bridge.h
@@ -7,6 +7,8 @@
 
 #include <cstdint>
 #include <memory>
+#include <string>
+#include <vector>
 
 #include "base/android/scoped_java_ref.h"
 #include "base/memory/raw_ptr.h"
@@ -45,67 +47,59 @@
 
   // Please keep all methods below in the same order as the @NativeMethods in
   // SyncServiceImpl.java.
-  void AcknowledgeBookmarksLimitExceededError(JNIEnv* env, int32_t source);
-  bool IsSyncFeatureEnabled(JNIEnv* env);
-  bool IsSyncFeatureActive(JNIEnv* env);
-  bool IsSyncDisabledByEnterprisePolicy(JNIEnv* env);
-  bool IsEngineInitialized(JNIEnv* env);
-  void SetSetupInProgress(JNIEnv* env, bool in_progress);
-  bool IsInitialSyncFeatureSetupComplete(JNIEnv* env);
-  void SetInitialSyncFeatureSetupComplete(JNIEnv* env, int32_t source);
-  base::android::ScopedJavaLocalRef<jintArray> GetActiveDataTypes(JNIEnv* env);
-  base::android::ScopedJavaLocalRef<jintArray> GetSelectedTypes(JNIEnv* env);
+  void AcknowledgeBookmarksLimitExceededError(int32_t source);
+  bool IsSyncFeatureEnabled();
+  bool IsSyncFeatureActive();
+  bool IsSyncDisabledByEnterprisePolicy();
+  bool IsEngineInitialized();
+  void SetSetupInProgress(bool in_progress);
+  bool IsInitialSyncFeatureSetupComplete();
+  void SetInitialSyncFeatureSetupComplete(int32_t source);
+  std::vector<int32_t> GetActiveDataTypes();
+  std::vector<int32_t> GetSelectedTypes();
   void GetTypesWithUnsyncedData(
       JNIEnv* env,
       const base::android::JavaRef<jobject>& callback);
   void GetLocalDataDescriptions(
       JNIEnv* env,
-      const base::android::JavaRef<jintArray>& types,
+      const std::vector<int32_t>& types,
       const base::android::JavaRef<jobject>& callback);
-  void TriggerLocalDataMigration(
-      JNIEnv* env,
-      const base::android::JavaRef<jintArray>& types);
-  bool IsTypeManagedByPolicy(JNIEnv* env, int32_t type);
-  bool IsTypeManagedByCustodian(JNIEnv* env, int32_t type);
+  void TriggerLocalDataMigration(const std::vector<int32_t>& types);
+  bool IsTypeManagedByPolicy(int32_t type);
+  bool IsTypeManagedByCustodian(int32_t type);
   void SetSelectedTypes(
-      JNIEnv* env,
       bool sync_everything,
-      const base::android::JavaRef<jintArray>& user_selectable_type_selection);
-  void SetSelectedType(JNIEnv* env, int32_t type, bool is_type_on);
-  bool IsCustomPassphraseAllowed(JNIEnv* env);
-  bool IsEncryptEverythingEnabled(JNIEnv* env);
-  bool IsPassphraseRequiredForPreferredDataTypes(JNIEnv* env);
-  bool IsTrustedVaultKeyRequired(JNIEnv* env);
-  bool IsTrustedVaultKeyRequiredForPreferredDataTypes(JNIEnv* env);
-  bool IsTrustedVaultRecoverabilityDegraded(JNIEnv* env);
-  bool IsUsingExplicitPassphrase(JNIEnv* env);
-  int32_t GetPassphraseType(JNIEnv* env);
-  int32_t GetTransportState(JNIEnv* env);
-  int32_t GetUserActionableError(JNIEnv* env);
-  void SetEncryptionPassphrase(
-      JNIEnv* env,
-      const base::android::JavaRef<jstring>& passphrase);
-  bool SetDecryptionPassphrase(
-      JNIEnv* env,
-      const base::android::JavaRef<jstring>& passphrase);
+      const std::vector<int32_t>& user_selectable_types_vector);
+  void SetSelectedType(int32_t type, bool is_type_on);
+  bool IsCustomPassphraseAllowed();
+  bool IsEncryptEverythingEnabled();
+  bool IsPassphraseRequiredForPreferredDataTypes();
+  bool IsTrustedVaultKeyRequired();
+  bool IsTrustedVaultKeyRequiredForPreferredDataTypes();
+  bool IsTrustedVaultRecoverabilityDegraded();
+  bool IsUsingExplicitPassphrase();
+  int32_t GetPassphraseType();
+  int32_t GetTransportState();
+  int32_t GetUserActionableError();
+  void SetEncryptionPassphrase(const std::string& passphrase);
+  bool SetDecryptionPassphrase(const std::string& passphrase);
   // Returns 0 if there's no passphrase time.
-  int64_t GetExplicitPassphraseTime(JNIEnv* env);
+  int64_t GetExplicitPassphraseTime();
   void GetAllNodes(JNIEnv* env,
                    const base::android::JavaRef<jobject>& callback);
-  GoogleServiceAuthError GetAuthError(JNIEnv* env);
+  GoogleServiceAuthError GetAuthError();
   base::android::ScopedJavaLocalRef<jobject> GetAccountInfo(JNIEnv* env);
-  bool HasSyncConsent(JNIEnv* env);
-  bool IsPassphrasePromptMutedForCurrentProductVersion(JNIEnv* env);
-  void MarkPassphrasePromptMutedForCurrentProductVersion(JNIEnv* env);
-  bool HasKeepEverythingSynced(JNIEnv* env);
-  bool ShouldOfferTrustedVaultOptIn(JNIEnv* env);
-  void TriggerRefresh(JNIEnv* env);
+  bool HasSyncConsent();
+  bool IsPassphrasePromptMutedForCurrentProductVersion();
+  void MarkPassphrasePromptMutedForCurrentProductVersion();
+  bool HasKeepEverythingSynced();
+  bool ShouldOfferTrustedVaultOptIn();
+  void TriggerRefresh();
   // Returns a timestamp for when a sync was last executed. The return value is
   // the internal value of base::Time.
-  int64_t GetLastSyncedTimeForDebugging(JNIEnv* env);
+  int64_t GetLastSyncedTimeForDebugging();
   void KeepAccountSettingsPrefsOnlyForUsers(
-      JNIEnv* env,
-      const base::android::JavaRef<jobjectArray>& gaia_ids);
+      const std::vector<std::string>& gaia_id_strings);
 
  private:
   // A reference to the sync service for this profile.
diff --git a/components/url_formatter/android/java/src/org/chromium/components/url_formatter/UrlFormatter.java b/components/url_formatter/android/java/src/org/chromium/components/url_formatter/UrlFormatter.java
index 68a1c1b..db1a052 100644
--- a/components/url_formatter/android/java/src/org/chromium/components/url_formatter/UrlFormatter.java
+++ b/components/url_formatter/android/java/src/org/chromium/components/url_formatter/UrlFormatter.java
@@ -9,6 +9,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.jni_zero.JNINamespace;
+import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
 import org.chromium.build.annotations.NullMarked;
@@ -240,24 +241,38 @@
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
     @NativeMethods
     public interface Natives {
-        GURL fixupUrl(String url);
+        @JniType("GURL")
+        GURL fixupUrl(@JniType("std::string") String url);
 
-        String formatUrlForDisplayOmitScheme(String url);
+        @JniType("std::u16string")
+        String formatUrlForDisplayOmitScheme(@JniType("std::string") String url);
 
-        String formatUrlForDisplayOmitHTTPScheme(String url);
+        @JniType("std::u16string")
+        String formatUrlForDisplayOmitHTTPScheme(@JniType("std::string") String url);
 
-        String formatUrlForDisplayOmitSchemeOmitTrivialSubdomains(String url);
+        @JniType("std::u16string")
+        String formatUrlForDisplayOmitSchemeOmitTrivialSubdomains(
+                @JniType("std::string") String url);
 
-        String formatUrlForDisplayOmitSchemePathAndTrivialSubdomains(GURL url);
+        @JniType("std::u16string")
+        String formatUrlForDisplayOmitSchemePathAndTrivialSubdomains(@JniType("GURL") GURL url);
 
-        String formatUrlForDisplayOmitUsernamePassword(String url);
+        @JniType("std::u16string")
+        String formatUrlForDisplayOmitUsernamePassword(@JniType("std::string") String url);
 
-        String formatUrlForCopy(String url);
+        @JniType("std::u16string")
+        String formatUrlForCopy(@JniType("std::string") String url);
 
-        String formatUrlForSecurityDisplay(GURL url, @SchemeDisplay int schemeDisplay);
+        @JniType("std::u16string")
+        String formatUrlForSecurityDisplay(
+                @JniType("GURL") GURL url, @SchemeDisplay int schemeDisplay);
 
-        String formatOriginForSecurityDisplay(Origin origin, @SchemeDisplay int schemeDisplay);
+        @JniType("std::u16string")
+        String formatOriginForSecurityDisplay(
+                @JniType("url::Origin") Origin origin, @SchemeDisplay int schemeDisplay);
 
-        String formatStringUrlForSecurityDisplay(@Nullable String url, @SchemeDisplay int schemeDisplay);
+        @JniType("std::u16string")
+        String formatStringUrlForSecurityDisplay(
+                @JniType("std::string") @Nullable String url, @SchemeDisplay int schemeDisplay);
     }
 }
diff --git a/components/url_formatter/url_formatter_android.cc b/components/url_formatter/url_formatter_android.cc
index 50c8588..f454f3fa 100644
--- a/components/url_formatter/url_formatter_android.cc
+++ b/components/url_formatter/url_formatter_android.cc
@@ -17,134 +17,83 @@
 // Must come after all headers that specialize FromJniType() / ToJniType().
 #include "components/url_formatter/android/jni_headers/UrlFormatter_jni.h"
 
-using base::android::JavaRef;
-using base::android::ScopedJavaLocalRef;
-
-namespace {
-
-static GURL JNI_UrlFormatter_ConvertJavaStringToGURL(
-    JNIEnv* env,
-    const base::android::JavaRef<jstring>& url) {
-  return url ? GURL(base::android::ConvertJavaStringToUTF8(env, url)) : GURL();
-}
-
-}  // namespace
-
 namespace url_formatter {
 
 namespace android {
 
-static ScopedJavaLocalRef<jobject> JNI_UrlFormatter_FixupUrl(
-    JNIEnv* env,
-    const base::android::JavaRef<jstring>& url) {
-  DCHECK(url);
-  GURL fixed_url =
-      url_formatter::FixupURL(base::android::ConvertJavaStringToUTF8(env, url));
-
-  return url::GURLAndroid::FromNativeGURL(env, fixed_url);
+static GURL JNI_UrlFormatter_FixupUrl(const std::string& url) {
+  return url_formatter::FixupURL(url);
 }
 
-static ScopedJavaLocalRef<jstring>
-JNI_UrlFormatter_FormatUrlForDisplayOmitScheme(
-    JNIEnv* env,
-    const base::android::JavaRef<jstring>& url) {
-  return base::android::ConvertUTF16ToJavaString(
-      env, url_formatter::FormatUrl(
-               JNI_UrlFormatter_ConvertJavaStringToGURL(env, url),
-               url_formatter::kFormatUrlOmitDefaults |
-                   url_formatter::kFormatUrlOmitHTTPS,
-               base::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
+static std::u16string JNI_UrlFormatter_FormatUrlForDisplayOmitScheme(
+    const std::string& url) {
+  return url_formatter::FormatUrl(GURL(url),
+                                  url_formatter::kFormatUrlOmitDefaults |
+                                      url_formatter::kFormatUrlOmitHTTPS,
+                                  base::UnescapeRule::SPACES, nullptr, nullptr,
+                                  nullptr);
 }
 
-static ScopedJavaLocalRef<jstring>
-JNI_UrlFormatter_FormatUrlForDisplayOmitHTTPScheme(
-    JNIEnv* env,
-    const base::android::JavaRef<jstring>& url) {
-  return base::android::ConvertUTF16ToJavaString(
-      env, url_formatter::FormatUrl(
-               JNI_UrlFormatter_ConvertJavaStringToGURL(env, url),
-               url_formatter::kFormatUrlOmitDefaults,
-               base::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
+static std::u16string JNI_UrlFormatter_FormatUrlForDisplayOmitHTTPScheme(
+    const std::string& url) {
+  return url_formatter::FormatUrl(
+      GURL(url), url_formatter::kFormatUrlOmitDefaults,
+      base::UnescapeRule::SPACES, nullptr, nullptr, nullptr);
 }
 
-static ScopedJavaLocalRef<jstring>
-JNI_UrlFormatter_FormatUrlForDisplayOmitUsernamePassword(
-    JNIEnv* env,
-    const base::android::JavaRef<jstring>& url) {
-  return base::android::ConvertUTF16ToJavaString(
-      env, url_formatter::FormatUrl(
-               JNI_UrlFormatter_ConvertJavaStringToGURL(env, url),
-               url_formatter::kFormatUrlOmitUsernamePassword |
-                   kFormatUrlOmitTrailingSlashOnBareHostname,
-               base::UnescapeRule::NONE, nullptr, nullptr, nullptr));
+static std::u16string JNI_UrlFormatter_FormatUrlForDisplayOmitUsernamePassword(
+    const std::string& url) {
+  return url_formatter::FormatUrl(
+      GURL(url),
+      url_formatter::kFormatUrlOmitUsernamePassword |
+          kFormatUrlOmitTrailingSlashOnBareHostname,
+      base::UnescapeRule::NONE, nullptr, nullptr, nullptr);
 }
 
-static ScopedJavaLocalRef<jstring> JNI_UrlFormatter_FormatUrlForCopy(
-    JNIEnv* env,
-    const base::android::JavaRef<jstring>& url) {
-  return base::android::ConvertUTF16ToJavaString(
-      env, url_formatter::FormatUrl(
-               JNI_UrlFormatter_ConvertJavaStringToGURL(env, url),
-               url_formatter::kFormatUrlOmitNothing, base::UnescapeRule::NORMAL,
-               nullptr, nullptr, nullptr));
+static std::u16string JNI_UrlFormatter_FormatUrlForCopy(
+    const std::string& url) {
+  return url_formatter::FormatUrl(
+      GURL(url), url_formatter::kFormatUrlOmitNothing,
+      base::UnescapeRule::NORMAL, nullptr, nullptr, nullptr);
 }
 
-static ScopedJavaLocalRef<jstring>
-JNI_UrlFormatter_FormatStringUrlForSecurityDisplay(
-    JNIEnv* env,
-    const base::android::JavaRef<jstring>& url,
+static std::u16string JNI_UrlFormatter_FormatStringUrlForSecurityDisplay(
+    const std::string& url,
     int32_t scheme_display) {
-  return base::android::ConvertUTF16ToJavaString(
-      env, url_formatter::FormatUrlForSecurityDisplay(
-               JNI_UrlFormatter_ConvertJavaStringToGURL(env, url),
-               static_cast<SchemeDisplay>(scheme_display)));
+  return url_formatter::FormatUrlForSecurityDisplay(
+      GURL(url), static_cast<SchemeDisplay>(scheme_display));
 }
 
-static ScopedJavaLocalRef<jstring> JNI_UrlFormatter_FormatUrlForSecurityDisplay(
-    JNIEnv* env,
-    const base::android::JavaRef<jobject>& j_gurl,
+static std::u16string JNI_UrlFormatter_FormatUrlForSecurityDisplay(
+    const GURL& gurl,
     int32_t scheme_display) {
-  DCHECK(j_gurl);
-  GURL gurl = url::GURLAndroid::ToNativeGURL(env, j_gurl);
-  return base::android::ConvertUTF16ToJavaString(
-      env, url_formatter::FormatUrlForSecurityDisplay(
-               gurl, static_cast<SchemeDisplay>(scheme_display)));
+  return url_formatter::FormatUrlForSecurityDisplay(
+      gurl, static_cast<SchemeDisplay>(scheme_display));
 }
 
-static ScopedJavaLocalRef<jstring>
-JNI_UrlFormatter_FormatOriginForSecurityDisplay(
-    JNIEnv* env,
-    const base::android::JavaRef<jobject>& j_origin,
+static std::u16string JNI_UrlFormatter_FormatOriginForSecurityDisplay(
+    const url::Origin& origin,
     int32_t scheme_display) {
-  DCHECK(j_origin);
-  url::Origin origin = url::Origin::FromJavaObject(env, j_origin);
-  return base::android::ConvertUTF16ToJavaString(
-      env, url_formatter::FormatOriginForSecurityDisplay(
-               origin, static_cast<SchemeDisplay>(scheme_display)));
+  return url_formatter::FormatOriginForSecurityDisplay(
+      origin, static_cast<SchemeDisplay>(scheme_display));
 }
 
-static ScopedJavaLocalRef<jstring>
+static std::u16string
 JNI_UrlFormatter_FormatUrlForDisplayOmitSchemeOmitTrivialSubdomains(
-    JNIEnv* env,
-    const base::android::JavaRef<jstring>& url) {
-  return base::android::ConvertUTF16ToJavaString(
-      env, url_formatter::FormatUrl(
-               JNI_UrlFormatter_ConvertJavaStringToGURL(env, url),
-               url_formatter::kFormatUrlOmitDefaults |
-                   url_formatter::kFormatUrlOmitHTTPS |
-                   url_formatter::kFormatUrlOmitTrivialSubdomains,
-               base::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
+    const std::string& url) {
+  return url_formatter::FormatUrl(
+      GURL(url),
+      url_formatter::kFormatUrlOmitDefaults |
+          url_formatter::kFormatUrlOmitHTTPS |
+          url_formatter::kFormatUrlOmitTrivialSubdomains,
+      base::UnescapeRule::SPACES, nullptr, nullptr, nullptr);
 }
 
-static ScopedJavaLocalRef<jstring>
+static std::u16string
 JNI_UrlFormatter_FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains(
-    JNIEnv* env,
-    const base::android::JavaRef<jobject>& j_gurl) {
-  DCHECK(j_gurl);
-  GURL gurl = url::GURLAndroid::ToNativeGURL(env, j_gurl);
-  return base::android::ConvertUTF16ToJavaString(
-      env, url_formatter::FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains(
-               gurl));
+    const GURL& gurl) {
+  return url_formatter::FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains(
+      gurl);
 }
 
 }  // namespace android
diff --git a/components/variations/pref_names.h b/components/variations/pref_names.h
index f524a984..33411fd 100644
--- a/components/variations/pref_names.h
+++ b/components/variations/pref_names.h
@@ -24,6 +24,10 @@
 // studies.
 inline constexpr char kVariationsCountry[] = "variations_country";
 
+// The latest administrative area code received by the VariationsService for
+// evaluating studies.
+inline constexpr char kVariationsGeoLevel1[] = "variations_geo_level1";
+
 // The number of times that Chrome has crashed before successfully fetching a
 // new seed. Used to determine whether to fall back to a "safe" seed.
 inline constexpr char kVariationsCrashStreak[] = "variations_crash_streak";
diff --git a/components/variations/proto/stored_seed_info.proto b/components/variations/proto/stored_seed_info.proto
index 472c9bcb..0c5b245 100644
--- a/components/variations/proto/stored_seed_info.proto
+++ b/components/variations/proto/stored_seed_info.proto
@@ -10,7 +10,7 @@
 
 // Wraps a stored VariationsSeed and other seed-related info.
 //
-// Next tag: 9
+// Next tag: 10
 message StoredSeedInfo {
   // Raw seed data.
   // As a memory optimization, the seed data may be cleared from memory after
@@ -45,4 +45,9 @@
   // Chrome version used for evaluating studies. The version stored at the same
   // time as `permanent_country_code`.
   optional string permanent_version = 8;
+
+  // The administrative area code of the client fetched from the server. Mirrors
+  // the `session_country_code` field, but is not currently used for evaluating
+  // studies.
+  optional string session_geo_level1 = 9;
 }
diff --git a/components/variations/seed_reader_writer.cc b/components/variations/seed_reader_writer.cc
index 56f9136..b188606 100644
--- a/components/variations/seed_reader_writer.cc
+++ b/components/variations/seed_reader_writer.cc
@@ -267,6 +267,25 @@
          channel == version_info::Channel::BETA;
 }
 
+// Sets the Geo Level1 pref value if its pref name exists.
+void SetGeoLevel1Pref(const SeedFieldsPrefs& prefs,
+                      PrefService& local_state,
+                      std::string_view value) {
+  if (prefs.session_geo_level1) {
+    local_state.SetString(prefs.session_geo_level1, value);
+  }
+}
+
+// Gets the Geo Level1 pref value if its pref name exists. Returns an empty
+// string otherwise.
+std::string GetGeoLevel1Pref(const SeedFieldsPrefs& prefs,
+                             PrefService& local_state) {
+  if (prefs.session_geo_level1) {
+    return local_state.GetString(prefs.session_geo_level1);
+  }
+  return "";
+}
+
 }  // namespace
 
 const SeedFieldsPrefs kRegularSeedFieldsPrefs = {
@@ -276,6 +295,7 @@
     .seed_date = prefs::kVariationsSeedDate,
     .client_fetch_time = prefs::kVariationsLastFetchTime,
     .session_country_code = prefs::kVariationsCountry,
+    .session_geo_level1 = prefs::kVariationsGeoLevel1,
     .permanent_country_code_version =
         prefs::kVariationsPermanentConsistencyCountry,
 };
@@ -287,6 +307,7 @@
     .seed_date = prefs::kVariationsSafeSeedDate,
     .client_fetch_time = prefs::kVariationsSafeSeedFetchTime,
     .session_country_code = prefs::kVariationsSafeSeedSessionConsistencyCountry,
+    .session_geo_level1 = nullptr,
     .permanent_country_code_version =
         prefs::kVariationsSafeSeedPermanentConsistencyCountry,
 };
@@ -296,6 +317,7 @@
                    base::Time seed_date,
                    base::Time client_fetch_time,
                    std::string_view session_country_code,
+                   std::string_view session_geo_level1,
                    std::string_view permanent_country_code,
                    std::string_view permanent_country_version)
     : signature(signature),
@@ -303,6 +325,7 @@
       seed_date(seed_date),
       client_fetch_time(client_fetch_time),
       session_country_code(session_country_code),
+      session_geo_level1(session_geo_level1),
       permanent_country_code(permanent_country_code),
       permanent_country_version(permanent_country_version) {}
 
@@ -397,8 +420,12 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (ShouldUseSeedFile()) {
     stored_seed_info_.clear_session_country_code();
+    stored_seed_info_.clear_session_geo_level1();
   }
   local_state_->ClearPref(fields_prefs_->session_country_code);
+  if (fields_prefs_->session_geo_level1) {
+    local_state_->ClearPref(fields_prefs_->session_geo_level1);
+  }
 }
 
 SeedInfo SeedReaderWriter::GetSeedInfo() const {
@@ -411,6 +438,7 @@
         /*client_fetch_time=*/
         ProtoTimeToTime(stored_seed_info_.client_fetch_time()),
         /*session_country_code=*/stored_seed_info_.session_country_code(),
+        /*session_geo_level1=*/stored_seed_info_.session_geo_level1(),
         /*permanent_country_code=*/stored_seed_info_.permanent_country_code(),
         /*permanent_country_version=*/stored_seed_info_.permanent_version());
   } else {
@@ -425,6 +453,8 @@
         local_state_->GetTime(fields_prefs_->client_fetch_time),
         /*session_country_code=*/
         local_state_->GetString(fields_prefs_->session_country_code),
+        /*session_geo_level1=*/
+        GetGeoLevel1Pref(*fields_prefs_, *local_state_),
         /*permanent_country_code=*/permanent_country_version.country,
         /*permanent_country_version=*/permanent_country_version.version);
   }
@@ -667,9 +697,11 @@
   stored_seed_info_.set_seed_date(TimeToProtoTime(seed_info.seed_date));
   stored_seed_info_.set_client_fetch_time(
       TimeToProtoTime(seed_info.client_fetch_time));
-  // Only update the latest country code if it is not empty.
+  // Only update the latest country code and geo level if country code is not
+  // empty.
   if (!seed_info.session_country_code.empty()) {
     stored_seed_info_.set_session_country_code(seed_info.session_country_code);
+    stored_seed_info_.set_session_geo_level1(seed_info.session_geo_level1);
   }
   if (!seed_info.permanent_country_code.empty()) {
     stored_seed_info_.set_permanent_country_code(
@@ -689,10 +721,13 @@
   // being scheduled.
   seed_writer_->ScheduleWriteWithBackgroundDataSerializer(this);
   // We still need to update the session country code in local state as it is
-  // used by hash_realtime_utils::GetHashRealTimeSelectionConfiguringPrefs().
+  // used by hash_realtime_utils::GetHashRealTimeSelectionConfiguringPrefs() and
+  // for some platform experience features.
   if (!seed_info.session_country_code.empty()) {
     local_state_->SetString(fields_prefs_->session_country_code,
                             stored_seed_info_.session_country_code());
+    SetGeoLevel1Pref(*fields_prefs_, *local_state_,
+                     stored_seed_info_.session_geo_level1());
   }
   return StoreSeedResult::kSuccess;
 }
@@ -794,6 +829,7 @@
               local_state_->GetTime(fields_prefs_->client_fetch_time),
           .session_country_code =
               local_state_->GetString(fields_prefs_->session_country_code),
+          .session_geo_level1 = GetGeoLevel1Pref(*fields_prefs_, *local_state_),
           .permanent_country_code = permanent_country_version.country,
           .permanent_country_version = permanent_country_version.version,
       });
@@ -887,6 +923,8 @@
   if (!seed_info.session_country_code.empty()) {
     local_state_->SetString(fields_prefs_->session_country_code,
                             seed_info.session_country_code);
+    SetGeoLevel1Pref(*fields_prefs_, *local_state_,
+                     seed_info.session_geo_level1);
   }
   // Version could be empty in case of the SafeSeed.
   if (!seed_info.permanent_country_code.empty()) {
diff --git a/components/variations/seed_reader_writer.h b/components/variations/seed_reader_writer.h
index ece1900..ed3d90f 100644
--- a/components/variations/seed_reader_writer.h
+++ b/components/variations/seed_reader_writer.h
@@ -47,6 +47,7 @@
            base::Time seed_date,
            base::Time client_fetch_time,
            std::string_view session_country_code,
+           std::string_view session_geo_level1,
            std::string_view permanent_country_code,
            std::string_view permanent_country_version);
   ~SeedInfo();
@@ -67,6 +68,10 @@
   // Latest country code fetched from the server. Used for evaluating session
   // consistency studies.
   const std::string session_country_code;
+  // Latest administrative area code fetched from the server. Mirrors the
+  // `session_country_code` field, but is not currently used for evaluating
+  // studies.
+  const std::string session_geo_level1;
   // Country code used for evaluating permanent consistency studies.
   const std::string permanent_country_code;
   // Chrome version at the time `permanent_country_code` was updated.
@@ -83,6 +88,7 @@
   const base::Time seed_date;
   const base::Time client_fetch_time;
   const std::string_view session_country_code;
+  const std::string_view session_geo_level1;
   const std::string_view permanent_country_code;
   const std::string_view permanent_country_version;
 };
@@ -94,6 +100,8 @@
   const char* seed_date;
   const char* client_fetch_time;
   const char* session_country_code;
+  // `session_geo_level1` may be null.
+  const char* session_geo_level1;
   const char* permanent_country_code_version;
 };
 
diff --git a/components/variations/seed_reader_writer_unittest.cc b/components/variations/seed_reader_writer_unittest.cc
index 5a75f42..8707ee49 100644
--- a/components/variations/seed_reader_writer_unittest.cc
+++ b/components/variations/seed_reader_writer_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/base64.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
 #include "base/strings/strcat.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -79,6 +80,7 @@
   stored_seed_info.set_signature("signature");
   stored_seed_info.set_milestone(92);
   stored_seed_info.set_session_country_code("us");
+  stored_seed_info.set_session_geo_level1("us-ny");
   stored_seed_info.set_seed_date(1234);
   return stored_seed_info;
 }
@@ -104,6 +106,9 @@
       : SeedReaderWriterTestParams(std::get<0>(t),
                                    std::get<1>(t),
                                    std::get<2>(t)) {}
+  bool HasGeoLevel1Pref() const {
+    return seed_fields_prefs.session_geo_level1 != nullptr;
+  }
 
   SeedFieldsPrefs seed_fields_prefs;
   std::string_view field_trial_group;
@@ -121,6 +126,10 @@
   explicit ExpectedFieldTrialGroupTestParams(const TupleT& t)
       : ExpectedFieldTrialGroupTestParams(std::get<0>(t), std::get<1>(t)) {}
 
+  bool HasGeoLevel1Pref() const {
+    return seed_fields_prefs.session_geo_level1 != nullptr;
+  }
+
   variations::SeedFieldsPrefs seed_fields_prefs;
   version_info::Channel channel;
 };
@@ -293,6 +302,7 @@
       .seed_date = seed_date,
       .client_fetch_time = fetch_time,
       .session_country_code = "us",
+      .session_geo_level1 = "us-ny",
   });
 
   // Force write.
@@ -349,6 +359,7 @@
   ASSERT_NE(seed_info.milestone, 0);
   ASSERT_FALSE(seed_info.seed_date.is_null());
   ASSERT_THAT(seed_info.session_country_code, Not(IsEmpty()));
+  ASSERT_THAT(seed_info.session_geo_level1, Not(IsEmpty()));
 
   // Clear seed and force write.
   seed_reader_writer.ClearSeedInfo();
@@ -365,6 +376,8 @@
   // Session country code is not cleared.
   EXPECT_THAT(seed_reader_writer.GetSeedInfo().session_country_code,
               Not(IsEmpty()));
+  EXPECT_THAT(seed_reader_writer.GetSeedInfo().session_geo_level1,
+              Not(IsEmpty()));
 
   // Verify that the seed info in the seed file is cleared.
   StoredSeedInfo cleared_seed_info = ReadStoredSeedInfo();
@@ -375,6 +388,7 @@
   EXPECT_EQ(cleared_seed_info.client_fetch_time(), 0);
   // Session country code is not cleared.
   EXPECT_THAT(cleared_seed_info.session_country_code(), Not(IsEmpty()));
+  EXPECT_THAT(cleared_seed_info.session_geo_level1(), Not(IsEmpty()));
 }
 
 // Verifies that session country code is cleared from a seed file for clients in
@@ -384,6 +398,10 @@
             GetParam().field_trial_group);
   local_state_.SetString(GetParam().seed_fields_prefs.session_country_code,
                          "us");
+  if (GetParam().HasGeoLevel1Pref()) {
+    local_state_.SetString(GetParam().seed_fields_prefs.session_geo_level1,
+                           "us-ny");
+  }
   // Initialize seed_reader_writer with test thread and timer.
   SeedReaderWriter seed_reader_writer(
       &local_state_, /*seed_file_dir=*/temp_dir_.GetPath(), kSeedFilename,
@@ -393,15 +411,25 @@
 
   ASSERT_THAT(seed_reader_writer.GetSeedInfo().session_country_code,
               Not(IsEmpty()));
+  if (GetParam().HasGeoLevel1Pref()) {
+    ASSERT_THAT(seed_reader_writer.GetSeedInfo().session_geo_level1,
+                Not(IsEmpty()));
+  }
 
   seed_reader_writer.ClearSessionCountry();
 
   // Session country code is cleared.
   EXPECT_THAT(seed_reader_writer.GetSeedInfo().session_country_code, IsEmpty());
+  EXPECT_THAT(seed_reader_writer.GetSeedInfo().session_geo_level1, IsEmpty());
   // Local state pref should be cleared.
   EXPECT_THAT(
       local_state_.GetString(GetParam().seed_fields_prefs.session_country_code),
       IsEmpty());
+  if (GetParam().HasGeoLevel1Pref()) {
+    EXPECT_THAT(
+        local_state_.GetString(GetParam().seed_fields_prefs.session_geo_level1),
+        IsEmpty());
+  }
 }
 
 // Verifies clients in SeedFiles group read seeds from the seed file.
@@ -956,6 +984,7 @@
       .seed_date = seed_date,
       .client_fetch_time = fetch_time,
       .session_country_code = "us",
+      .session_geo_level1 = "us-ny",
   });
 
   // Ensure there's no pending write.
@@ -1002,6 +1031,11 @@
   local_state_.SetString(GetParam().seed_fields_prefs.session_country_code,
                          "us");
 
+  if (GetParam().HasGeoLevel1Pref()) {
+    local_state_.SetString(GetParam().seed_fields_prefs.session_geo_level1,
+                           "us-ny");
+  }
+
   // Clear seed and force file delete.
   seed_reader_writer.ClearSeedInfo();
   file_writer_thread_.FlushForTesting();
@@ -1017,6 +1051,10 @@
   // Session country code is not cleared.
   EXPECT_THAT(seed_reader_writer.GetSeedInfo().session_country_code,
               Not(IsEmpty()));
+  if (GetParam().HasGeoLevel1Pref()) {
+    EXPECT_THAT(seed_reader_writer.GetSeedInfo().session_geo_level1,
+                Not(IsEmpty()));
+  }
 
   // Verify seed cleared correctly in Local State prefs and that seed file is
   // deleted.
@@ -1034,6 +1072,12 @@
   EXPECT_THAT(
       local_state_.GetString(GetParam().seed_fields_prefs.session_country_code),
       Not(IsEmpty()));
+  if (GetParam().HasGeoLevel1Pref()) {
+    EXPECT_THAT(
+        local_state_.GetString(GetParam().seed_fields_prefs.session_geo_level1),
+        Not(IsEmpty()));
+  }
+
   EXPECT_FALSE(base::PathExists(temp_seed_file_path_));
 }
 
@@ -1052,6 +1096,10 @@
   // Create and store seed.
   local_state_.SetString(GetParam().seed_fields_prefs.session_country_code,
                          "us");
+  if (GetParam().HasGeoLevel1Pref()) {
+    local_state_.SetString(GetParam().seed_fields_prefs.session_geo_level1,
+                           "us-ny");
+  }
 
   // Clear seed and force file delete.
   seed_reader_writer.ClearSessionCountry();
@@ -1061,6 +1109,13 @@
   EXPECT_THAT(
       local_state_.GetString(GetParam().seed_fields_prefs.session_country_code),
       IsEmpty());
+
+  EXPECT_THAT(seed_reader_writer.GetSeedInfo().session_geo_level1, IsEmpty());
+  if (GetParam().HasGeoLevel1Pref()) {
+    EXPECT_THAT(
+        local_state_.GetString(GetParam().seed_fields_prefs.session_geo_level1),
+        IsEmpty());
+  }
 }
 
 // Verifies clients using local state to store seeds read seeds from local
diff --git a/components/variations/seed_response.h b/components/variations/seed_response.h
index d17d74c4..3b1daa12 100644
--- a/components/variations/seed_response.h
+++ b/components/variations/seed_response.h
@@ -21,6 +21,7 @@
   std::string data;  // "data" is binary, for which protobuf uses strings.
   std::string signature;
   std::string country;
+  std::string geo_level1;
   base::Time date;
   bool is_gzip_compressed = false;
 };
diff --git a/components/variations/service/variations_field_trial_creator.cc b/components/variations/service/variations_field_trial_creator.cc
index 5e7adcc..61d97bd 100644
--- a/components/variations/service/variations_field_trial_creator.cc
+++ b/components/variations/service/variations_field_trial_creator.cc
@@ -801,6 +801,7 @@
   }
   seed_store_->StoreSeedData(/*done_callback=*/base::DoNothing(), decoded_seed,
                              seed_signature->GetString(), /*country_code=*/"",
+                             /*geo_level1=*/"",
                              /*date_fetched=*/base::Time(),
                              /*is_delta_compressed=*/false,
                              /*is_gzip_compressed=*/true,
diff --git a/components/variations/service/variations_service.cc b/components/variations/service/variations_service.cc
index 8a82dde4..f14ea5f 100644
--- a/components/variations/service/variations_service.cc
+++ b/components/variations/service/variations_service.cc
@@ -707,6 +707,7 @@
 void VariationsService::StoreSeed(std::string seed_data,
                                   std::string seed_signature,
                                   std::string country_code,
+                                  std::string geo_level1,
                                   base::Time date_fetched,
                                   bool is_delta_compressed,
                                   bool is_gzip_compressed) {
@@ -717,8 +718,8 @@
                      weak_ptr_factory_.GetWeakPtr(), is_delta_compressed);
   field_trial_creator_.seed_store()->StoreSeedData(
       std::move(done_callback), std::move(seed_data), std::move(seed_signature),
-      std::move(country_code), date_fetched, is_delta_compressed,
-      is_gzip_compressed,
+      std::move(country_code), std::move(geo_level1), date_fetched,
+      is_delta_compressed, is_gzip_compressed,
       /*require_synchronous=*/false);
 }
 
@@ -899,9 +900,11 @@
   std::string_view signature =
       GetHeaderValue(headers.get(), "X-Seed-Signature");
   std::string_view country_code = GetHeaderValue(headers.get(), "X-Country");
+  std::string_view geo_level1 = GetHeaderValue(headers.get(), "X-Geo-Level-1");
   StoreSeed(std::move(*response_body), std::string(signature),
-            std::string(country_code), response_date.value_or(base::Time()),
-            is_delta_compressed, is_gzip_compressed);
+            std::string(country_code), std::string(geo_level1),
+            response_date.value_or(base::Time()), is_delta_compressed,
+            is_gzip_compressed);
 }
 
 bool VariationsService::MaybeRetryOverHTTP() {
diff --git a/components/variations/service/variations_service.h b/components/variations/service/variations_service.h
index 5cf521a..2c236d3 100644
--- a/components/variations/service/variations_service.h
+++ b/components/variations/service/variations_service.h
@@ -285,6 +285,7 @@
   virtual void StoreSeed(std::string seed_data,
                          std::string seed_signature,
                          std::string country_code,
+                         std::string geo_level1,
                          base::Time date_fetched,
                          bool is_delta_compressed,
                          bool is_gzip_compressed);
diff --git a/components/variations/service/variations_service_unittest.cc b/components/variations/service/variations_service_unittest.cc
index 53efeba..ac5e21e 100644
--- a/components/variations/service/variations_service_unittest.cc
+++ b/components/variations/service/variations_service_unittest.cc
@@ -135,14 +135,7 @@
       : VariationsService(std::make_unique<TestVariationsServiceClient>(),
                           std::move(test_notifier),
                           local_state,
-                          state_manager),
-        intercepts_fetch_(true),
-        fetch_attempted_(false),
-        latest_serial_number_(""),
-        seed_stores_succeed_(true),
-        seed_stored_(false),
-        delta_compressed_seed_(false),
-        gzip_compressed_seed_(false) {
+                          state_manager) {
     interception_url_ =
         GetVariationsServerURL(use_secure_url ? USE_HTTPS : USE_HTTP);
     set_variations_server_url(interception_url_);
@@ -168,6 +161,7 @@
   bool fetch_attempted() const { return fetch_attempted_; }
   bool seed_stored() const { return seed_stored_; }
   const std::string& stored_country() const { return stored_country_; }
+  const std::string& stored_geo_level() const { return stored_geo_level_; }
   bool delta_compressed_seed() const { return delta_compressed_seed_; }
   bool gzip_compressed_seed() const { return gzip_compressed_seed_; }
 
@@ -199,12 +193,14 @@
   void StoreSeed(std::string seed_data,
                  std::string seed_signature,
                  std::string country_code,
+                 std::string geo_level1,
                  base::Time date_fetched,
                  bool is_delta_compressed,
                  bool is_gzip_compressed) override {
     seed_stored_ = true;
     stored_seed_data_ = seed_data;
     stored_country_ = country_code;
+    stored_geo_level_ = geo_level1;
     delta_compressed_seed_ = is_delta_compressed;
     gzip_compressed_seed_ = is_gzip_compressed;
     OnSeedStoreResult(is_delta_compressed, seed_stores_succeed_,
@@ -222,15 +218,16 @@
 
  private:
   GURL interception_url_;
-  bool intercepts_fetch_;
-  bool fetch_attempted_;
+  bool intercepts_fetch_ = true;
+  bool fetch_attempted_ = false;
   std::string latest_serial_number_;
-  bool seed_stores_succeed_;
-  bool seed_stored_;
+  bool seed_stores_succeed_ = true;
+  bool seed_stored_ = false;
   std::string stored_seed_data_;
   std::string stored_country_;
-  bool delta_compressed_seed_;
-  bool gzip_compressed_seed_;
+  std::string stored_geo_level_;
+  bool delta_compressed_seed_ = false;
+  bool gzip_compressed_seed_ = false;
 };
 
 class TestVariationsServiceObserver : public VariationsService::Observer {
@@ -653,6 +650,7 @@
   head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
       net::HttpUtil::AssembleRawHeaders(headers));
   head->headers->SetHeader("X-Country", "test");
+  head->headers->SetHeader("X-Geo-Level-1", "test-geo-level");
   network::URLLoaderCompletionStatus status;
   status.decoded_body_length = serialized_seed.size();
   service.test_url_loader_factory()->AddResponse(
@@ -662,6 +660,7 @@
 
   EXPECT_TRUE(service.seed_stored());
   EXPECT_EQ("test", service.stored_country());
+  EXPECT_EQ("test-geo-level", service.stored_geo_level());
 }
 
 TEST_F(VariationsServiceTest, Observer) {
diff --git a/components/variations/variations_seed_store.cc b/components/variations/variations_seed_store.cc
index 2c40587..6cb624fe 100644
--- a/components/variations/variations_seed_store.cc
+++ b/components/variations/variations_seed_store.cc
@@ -313,6 +313,7 @@
     std::string data,
     std::string base64_seed_signature,
     std::string country_code,
+    std::string geo_level1,
     base::Time date_fetched,
     bool is_delta_compressed,
     bool is_gzip_compressed,
@@ -333,6 +334,7 @@
   seed_data.data = std::move(data);
   seed_data.base64_seed_signature = std::move(base64_seed_signature);
   seed_data.country_code = std::move(country_code);
+  seed_data.geo_level1 = std::move(geo_level1);
   seed_data.date_fetched = date_fetched;
   seed_data.is_gzip_compressed = is_gzip_compressed;
   seed_data.is_delta_compressed = is_delta_compressed;
@@ -556,6 +558,7 @@
   // Regular seed prefs:
   registry->RegisterStringPref(prefs::kVariationsCompressedSeed, std::string());
   registry->RegisterStringPref(prefs::kVariationsCountry, std::string());
+  registry->RegisterStringPref(prefs::kVariationsGeoLevel1, std::string());
   registry->RegisterTimePref(prefs::kVariationsLastFetchTime, base::Time());
   registry->RegisterIntegerPref(prefs::kVariationsSeedMilestone, 0);
   registry->RegisterTimePref(prefs::kVariationsSeedDate, base::Time());
@@ -646,7 +649,8 @@
       });
   StoreSeedData(std::move(done_callback), std::move(initial_seed->data),
                 std::move(initial_seed->signature),
-                std::move(initial_seed->country), initial_seed->date,
+                std::move(initial_seed->country),
+                std::move(initial_seed->geo_level1), initial_seed->date,
                 /*is_delta_compressed=*/false, initial_seed->is_gzip_compressed,
                 /*require_synchronous=*/true);
 }
@@ -844,7 +848,8 @@
       base::BindOnce(&VariationsSeedStore::StoreValidatedSeed,
                      weak_ptr_factory_.GetWeakPtr(), std::move(done_callback),
                      std::move(result.validated), result.seed_data.country_code,
-                     result.seed_data.date_fetched, require_synchronous);
+                     result.seed_data.geo_level1, result.seed_data.date_fetched,
+                     require_synchronous);
   ReadSeedData(/*done_callback=*/std::move(store_validated_seed_cb),
                SeedType::SAFE, require_synchronous);
 }
@@ -880,6 +885,7 @@
     base::OnceCallback<void(bool, VariationsSeed)> done_callback,
     ValidatedSeed seed,
     std::string country_code,
+    std::string geo_level1,
     base::Time date_fetched,
     bool require_synchronous,
     SeedReaderWriter::ReadSeedDataResult safe_seed_read_result) {
@@ -914,6 +920,7 @@
           .seed_date = date_fetched,
           .client_fetch_time = base::Time::Now(),
           .session_country_code = country_code,
+          .session_geo_level1 = geo_level1,
       });
   if (result == StoreSeedResult::kSuccess) {
     StoreLatestSerialNumber(seed.parsed.serial_number());
diff --git a/components/variations/variations_seed_store.h b/components/variations/variations_seed_store.h
index 8f9a66b..7a0f2eb 100644
--- a/components/variations/variations_seed_store.h
+++ b/components/variations/variations_seed_store.h
@@ -135,6 +135,7 @@
       std::string data,
       std::string base64_seed_signature,
       std::string country_code,
+      std::string geo_level1,
       base::Time date_fetched,
       bool is_delta_compressed,
       bool is_gzip_compressed,
@@ -306,6 +307,7 @@
     std::string data;
     std::string base64_seed_signature;
     std::string country_code;
+    std::string geo_level1;
     base::Time date_fetched;
     bool is_gzip_compressed = false;
     bool is_delta_compressed = false;
@@ -409,6 +411,7 @@
       base::OnceCallback<void(bool, VariationsSeed)> done_callback,
       ValidatedSeed seed,
       std::string country_code,
+      std::string geo_level1,
       base::Time date_fetched,
       bool require_synchronous,
       SeedReaderWriter::ReadSeedDataResult read_result);
diff --git a/components/variations/variations_seed_store_unittest.cc b/components/variations/variations_seed_store_unittest.cc
index 6bea762..12702bb 100644
--- a/components/variations/variations_seed_store_unittest.cc
+++ b/components/variations/variations_seed_store_unittest.cc
@@ -260,6 +260,7 @@
           .seed_date = now - delta * 1,
           .client_fetch_time = now,
           .session_country_code = "us",
+          .session_geo_level1 = "us-ny",
       });
   seed_store.SetSerialNumberForTesting("123");
 
@@ -274,6 +275,7 @@
           .seed_date = now - delta * 2,
           .client_fetch_time = now - delta * 3,
           .session_country_code = "gt",
+          .session_geo_level1 = "gt-16",
           .permanent_country_code = "mx",
       });
   prefs->SetString(prefs::kVariationsSafeSeedLocale, "en-MX");
@@ -1472,6 +1474,7 @@
 
   struct Params {
     std::string country_code;
+    std::string geo_level1;
     bool is_delta_compressed;
     bool is_gzip_compressed;
   };
@@ -1488,7 +1491,7 @@
             &StoreSeedDataGroupTest::OnSeedStoreResult, base::Unretained(this),
             run_loop.QuitClosure()),
         seed_data, /*base64_seed_signature=*/std::string(), params.country_code,
-        base::Time::Now(), params.is_delta_compressed,
+        params.geo_level1, base::Time::Now(), params.is_delta_compressed,
         params.is_gzip_compressed, RequireSynchronousStores());
     // If we're testing synchronous stores, we shouldn't issue a Run() call so
     // that the test verifies that the operation completed synchronously.
@@ -1629,6 +1632,33 @@
   EXPECT_EQ("test_country", GetSeedInfo(seed_store).session_country_code);
 }
 
+TEST_P(StoreSeedDataAllGroupsTest, GeoLevelIgnoredWithoutCountry) {
+  TestVariationsSeedStore seed_store(&prefs_, temp_dir_.GetPath());
+  std::string seed = SerializeSeed(CreateTestSeed());
+
+  // Test with no country code specified - which should skip the geo level.
+  ASSERT_TRUE(
+      StoreSeedData(seed_store, seed, {.geo_level1 = "test_geo_level1"}));
+  EXPECT_EQ("", GetSeedInfo(seed_store).session_country_code);
+  EXPECT_EQ("", GetSeedInfo(seed_store).session_geo_level1);
+
+  // Test with a valid values.
+  ASSERT_TRUE(StoreSeedData(seed_store, seed,
+                            {
+                                .country_code = "test_country",
+                                .geo_level1 = "test_geo_level1",
+                            }));
+  EXPECT_EQ("test_country", GetSeedInfo(seed_store).session_country_code);
+  EXPECT_EQ("test_geo_level1", GetSeedInfo(seed_store).session_geo_level1);
+
+  // Test with no country code specified - which should preserve the old geo
+  // level.
+  ASSERT_TRUE(
+      StoreSeedData(seed_store, seed, {.geo_level1 = "test_geo_level2"}));
+  EXPECT_EQ("test_country", GetSeedInfo(seed_store).session_country_code);
+  EXPECT_EQ("test_geo_level1", GetSeedInfo(seed_store).session_geo_level1);
+}
+
 TEST_P(StoreSeedDataAllGroupsTest, GzippedSeed) {
   TestVariationsSeedStore seed_store(&prefs_, temp_dir_.GetPath());
   ASSERT_EQ(base::FieldTrialList::FindFullName(kSeedFileTrial),
diff --git a/components/viz/service/layers/layer_context_impl.cc b/components/viz/service/layers/layer_context_impl.cc
index 3cf1e469..3f7ed13 100644
--- a/components/viz/service/layers/layer_context_impl.cc
+++ b/components/viz/service/layers/layer_context_impl.cc
@@ -1979,21 +1979,23 @@
       !std::isfinite(update->max_safe_area_inset_bottom)) {
     return base::unexpected("Invalid max safe area inset bottom");
   }
-  if (update->browser_controls_params.top_controls_height < 0 ||
-      !std::isfinite(update->browser_controls_params.top_controls_height) ||
-      update->browser_controls_params.top_controls_min_height < 0 ||
+  if (!std::isfinite(update->browser_controls_params.top_controls_height) ||
       !std::isfinite(update->browser_controls_params.top_controls_min_height) ||
-      update->browser_controls_params.bottom_controls_height < 0 ||
       !std::isfinite(update->browser_controls_params.bottom_controls_height) ||
-      update->browser_controls_params.bottom_controls_min_height < 0 ||
       !std::isfinite(
-          update->browser_controls_params.bottom_controls_min_height) ||
-      update->browser_controls_params.top_controls_min_height >
-          update->browser_controls_params.top_controls_height ||
-      update->browser_controls_params.bottom_controls_min_height >
-          update->browser_controls_params.bottom_controls_height) {
+          update->browser_controls_params.bottom_controls_min_height)) {
     return base::unexpected("Invalid browser controls params");
   }
+  update->browser_controls_params.top_controls_height =
+      std::max(0.f, update->browser_controls_params.top_controls_height);
+  update->browser_controls_params.top_controls_min_height =
+      std::clamp(update->browser_controls_params.top_controls_min_height, 0.f,
+                 update->browser_controls_params.top_controls_height);
+  update->browser_controls_params.bottom_controls_height =
+      std::max(0.f, update->browser_controls_params.bottom_controls_height);
+  update->browser_controls_params.bottom_controls_min_height =
+      std::clamp(update->browser_controls_params.bottom_controls_min_height,
+                 0.f, update->browser_controls_params.bottom_controls_height);
   layers.SetBrowserControlsParams(update->browser_controls_params);
   host_impl_->browser_controls_manager()->SetOffsetTagModifications(
       update->browser_controls_offset_tag_modifications);
diff --git a/components/viz/service/layers/layer_context_impl_unittest.cc b/components/viz/service/layers/layer_context_impl_unittest.cc
index 9414903..4be873f 100644
--- a/components/viz/service/layers/layer_context_impl_unittest.cc
+++ b/components/viz/service/layers/layer_context_impl_unittest.cc
@@ -4387,8 +4387,7 @@
     LayerContextImplUpdateDisplayTreeInvalidBrowserControlsTest,
     ::testing::Values(std::numeric_limits<float>::infinity(),
                       -std::numeric_limits<float>::infinity(),
-                      std::numeric_limits<float>::quiet_NaN(),
-                      -1.0f),
+                      std::numeric_limits<float>::quiet_NaN()),
     [](const testing::TestParamInfo<
         LayerContextImplUpdateDisplayTreeInvalidBrowserControlsTest::ParamType>&
            info) {
@@ -4398,28 +4397,47 @@
       if (std::isnan(info.param)) {
         return "NaN";
       }
-      if (info.param < 0.0f) {
-        return "Negative";
-      }
       return "Other";
     });
 
-TEST_F(LayerContextImplTest, InvalidMinMaxBrowserControlsHeight) {
+TEST_F(LayerContextImplTest, ClampedMinMaxBrowserControlsHeight) {
   auto update = CreateDefaultUpdate();
   update->browser_controls_params.top_controls_height = 10.f;
   update->browser_controls_params.top_controls_min_height = 20.f;
 
   auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
-  ASSERT_FALSE(result.has_value());
-  EXPECT_EQ(result.error(), "Invalid browser controls params");
+  ASSERT_TRUE(result.has_value());
+  const auto& params = layer_context_impl_->host_impl()
+                           ->active_tree()
+                           ->browser_controls_params();
+  EXPECT_EQ(params.top_controls_height, 10.f);
+  EXPECT_EQ(params.top_controls_min_height, 10.f);
 
   update = CreateDefaultUpdate();
   update->browser_controls_params.bottom_controls_height = 10.f;
   update->browser_controls_params.bottom_controls_min_height = 20.f;
 
   result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
-  ASSERT_FALSE(result.has_value());
-  EXPECT_EQ(result.error(), "Invalid browser controls params");
+  ASSERT_TRUE(result.has_value());
+  const auto& params2 = layer_context_impl_->host_impl()
+                            ->active_tree()
+                            ->browser_controls_params();
+  EXPECT_EQ(params2.bottom_controls_height, 10.f);
+  EXPECT_EQ(params2.bottom_controls_min_height, 10.f);
+}
+
+TEST_F(LayerContextImplTest, NegativeBrowserControlsHeight) {
+  auto update = CreateDefaultUpdate();
+  update->browser_controls_params.top_controls_height = -10.f;
+  update->browser_controls_params.top_controls_min_height = -5.f;
+
+  auto result = layer_context_impl_->DoUpdateDisplayTree(std::move(update));
+  ASSERT_TRUE(result.has_value());
+  const auto& params = layer_context_impl_->host_impl()
+                           ->active_tree()
+                           ->browser_controls_params();
+  EXPECT_EQ(params.top_controls_height, 0.f);
+  EXPECT_EQ(params.top_controls_min_height, 0.f);
 }
 
 class LayerContextImplUpdateDisplayTreeInvalidElasticOverscrollTest
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index 372417d..54e2b1c 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -361,6 +361,7 @@
   RFH_NEW_ISOLATED_WEB_APP_PERMISSION_POLICIES = 333,
   RFH_CREATE_NEW_WINDOW_INVALID_DISPOSITION = 334,
   RFH_CREATE_NEW_WINDOW_FROM_SANDBOXED_FRAME = 335,
+  RFH_MODAL_DIALOG_FROM_SANDBOXED_FRAME = 336,
 
   // Please add new elements here. The naming convention is abbreviated class
   // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/preloading/prefetch/prefetch_match_resolver.cc b/content/browser/preloading/prefetch/prefetch_match_resolver.cc
index 43dff576..f2a4faaf 100644
--- a/content/browser/preloading/prefetch/prefetch_match_resolver.cc
+++ b/content/browser/preloading/prefetch/prefetch_match_resolver.cc
@@ -53,6 +53,7 @@
     PrefetchServiceWorkerState expected_service_worker_state,
     bool is_nav_prerender,
     base::WeakPtr<PrerenderHost> prerender_host,
+    PrerenderHostId prerender_host_id,
     scoped_refptr<PreloadPipelineInfoImpl> preload_pipeline_info,
     Callback callback,
     perfetto::Flow flow)
@@ -64,6 +65,7 @@
       flow_(std::move(flow)),
       is_nav_prerender_(is_nav_prerender),
       prerender_host_for_metrics_(std::move(prerender_host)),
+      prerender_host_id_(std::move(prerender_host_id)),
       preload_pipeline_info_(std::move(preload_pipeline_info)),
       prefetch_match_metrics_(std::make_unique<PrefetchMatchMetrics>()) {
   switch (expected_service_worker_state_) {
@@ -76,6 +78,10 @@
       break;
   }
 
+  // Valid preload pipeline info implies that the navigation is prerender (valid
+  // prerender host id).
+  CHECK(!preload_pipeline_info_ || prerender_host_id_);
+
   prefetch_match_metrics_->expected_service_worker_state =
       expected_service_worker_state;
   prefetch_match_metrics_->time_match_start = base::TimeTicks::Now();
@@ -126,6 +132,8 @@
     return frame_tree_node->navigation_request()->GetWeakPtr();
   })();
 
+  PrerenderHostId prerender_host_id =
+      frame_tree_node->frame_tree().delegate()->GetPrerenderHostId();
   auto prerender_host = ([&]() -> base::WeakPtr<PrerenderHost> {
     PrerenderHostRegistry* prerender_host_registry =
         frame_tree_node->current_frame_host()
@@ -135,8 +143,6 @@
       return nullptr;
     }
 
-    PrerenderHostId prerender_host_id =
-        frame_tree_node->frame_tree().delegate()->GetPrerenderHostId();
     PrerenderHost* prerender_host =
         prerender_host_registry->FindNonReservedHostById(prerender_host_id);
     if (!prerender_host) {
@@ -155,7 +161,8 @@
       std::move(navigation_request), prefetch_service, std::move(navigated_key),
       expected_service_worker_state,
       frame_tree_node->frame_tree().is_prerendering(),
-      std::move(prerender_host), std::move(preload_pipeline_info),
+      std::move(prerender_host), std::move(prerender_host_id),
+      std::move(preload_pipeline_info),
       std::move(serving_page_metrics_container), std::move(callback),
       std::move(flow));
 }
@@ -172,7 +179,9 @@
   PrefetchMatchResolver::FindPrefetchInternal1(
       /*navigation_request=*/nullptr, prefetch_service,
       std::move(navigated_key), expected_service_worker_state, is_nav_prerender,
-      /*prerender_host=*/nullptr, /*preload_pipeline_info=*/nullptr,
+      /*prerender_host=*/nullptr,
+      /*prerender_host_id=*/PrerenderHostId(),
+      /*preload_pipeline_info=*/nullptr,
       std::move(serving_page_metrics_container), std::move(callback),
       perfetto::Flow::ProcessScoped(0));
 }
@@ -185,6 +194,7 @@
     PrefetchServiceWorkerState expected_service_worker_state,
     bool is_nav_prerender,
     base::WeakPtr<PrerenderHost> prerender_host,
+    PrerenderHostId prerender_host_id,
     scoped_refptr<PreloadPipelineInfoImpl> preload_pipeline_info,
     base::WeakPtr<PrefetchServingPageMetricsContainer>
         serving_page_metrics_container,
@@ -200,8 +210,8 @@
   auto prefetch_match_resolver = base::WrapUnique(new PrefetchMatchResolver(
       std::move(navigation_request), prefetch_service.GetWeakPtr(),
       std::move(navigated_key), expected_service_worker_state, is_nav_prerender,
-      std::move(prerender_host), std::move(preload_pipeline_info),
-      std::move(callback), std::move(flow)));
+      std::move(prerender_host), std::move(prerender_host_id),
+      std::move(preload_pipeline_info), std::move(callback), std::move(flow)));
   PrefetchMatchResolver& ref = *prefetch_match_resolver.get();
   ref.self_ = std::move(prefetch_match_resolver);
 
@@ -723,8 +733,10 @@
   // If this is for a prerender initial navigation, mark its preload pipeline is
   // using a prefetch. Note that the prefetch may not be the prefetch ahead of
   // prerender.
+  CHECK(!preload_pipeline_info_ || prerender_host_id_);
   if (preload_pipeline_info_) {
-    preload_pipeline_info_->MarkPrerenderMatchedWithPrefetch();
+    preload_pipeline_info_->MarkPrerenderMatchedWithPrefetch(
+        prerender_host_id_);
   }
 
   PrefetchServingHandle serving_handle =
diff --git a/content/browser/preloading/prefetch/prefetch_match_resolver.h b/content/browser/preloading/prefetch/prefetch_match_resolver.h
index d46c7a6b..8a5c3fc2 100644
--- a/content/browser/preloading/prefetch/prefetch_match_resolver.h
+++ b/content/browser/preloading/prefetch/prefetch_match_resolver.h
@@ -228,6 +228,7 @@
       PrefetchServiceWorkerState expected_service_worker_state,
       bool is_nav_prerender,
       base::WeakPtr<PrerenderHost> prerender_host,
+      PrerenderHostId prerender_host_id,
       scoped_refptr<PreloadPipelineInfoImpl> preload_pipeline_info,
       base::WeakPtr<PrefetchServingPageMetricsContainer>
           serving_page_metrics_container,
@@ -241,6 +242,7 @@
       PrefetchServiceWorkerState expected_service_worker_state,
       bool is_nav_prerender,
       base::WeakPtr<PrerenderHost> prerender_host,
+      PrerenderHostId prerender_host_id,
       scoped_refptr<PreloadPipelineInfoImpl> preload_pipeline_info,
       Callback callback,
       perfetto::Flow flow);
@@ -332,9 +334,10 @@
   // `PreloadServingMetricsCapsule::IsFeatureEnabled()` is false.
   base::WeakPtr<PrerenderHost> prerender_host_for_metrics_;
 
-  // `PreloadPipelineInfo` of prerender if the navigation is prerender.
+  // Attributes of prerender if the navigation is prerender.
   //
   // Non-null iff the navigation is prerender.
+  const PrerenderHostId prerender_host_id_;
   const scoped_refptr<PreloadPipelineInfoImpl> preload_pipeline_info_;
 
   std::unique_ptr<PrefetchMatchMetrics> prefetch_match_metrics_;
diff --git a/content/browser/preloading/preload_pipeline_info_impl.cc b/content/browser/preloading/preload_pipeline_info_impl.cc
index 8bf1d88..aceff733 100644
--- a/content/browser/preloading/preload_pipeline_info_impl.cc
+++ b/content/browser/preloading/preload_pipeline_info_impl.cc
@@ -5,6 +5,7 @@
 #include "content/browser/preloading/preload_pipeline_info_impl.h"
 
 #include "base/trace_event/trace_event.h"
+#include "content/browser/preloading/prerender/prerender_host.h"
 
 namespace content {
 
@@ -62,8 +63,16 @@
   prefetch_status_ = prefetch_status;
 }
 
-void PreloadPipelineInfoImpl::MarkPrerenderMatchedWithPrefetch() {
-  is_prerender_matched_with_prefetch_ = true;
+bool PreloadPipelineInfoImpl::IsPrerenderMatchedWithPrefetch(
+    const PrerenderHostId& prerender_host_id) const {
+  return prerender_ids_matched_with_prefetch_.contains(prerender_host_id);
+}
+
+void PreloadPipelineInfoImpl::MarkPrerenderMatchedWithPrefetch(
+    PrerenderHostId prerender_host_id) {
+  CHECK(!IsPrerenderMatchedWithPrefetch(prerender_host_id));
+
+  prerender_ids_matched_with_prefetch_.insert(prerender_host_id);
 }
 
 }  // namespace content
diff --git a/content/browser/preloading/preload_pipeline_info_impl.h b/content/browser/preloading/preload_pipeline_info_impl.h
index cf30879..078a608 100644
--- a/content/browser/preloading/preload_pipeline_info_impl.h
+++ b/content/browser/preloading/preload_pipeline_info_impl.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_BROWSER_PRELOADING_PRELOAD_PIPELINE_INFO_IMPL_H_
 #define CONTENT_BROWSER_PRELOADING_PRELOAD_PIPELINE_INFO_IMPL_H_
 
+#include "base/containers/flat_set.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/unguessable_token.h"
@@ -12,6 +13,7 @@
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/preload_pipeline_info.h"
 #include "content/public/browser/preloading.h"
+#include "content/public/browser/prerender_host_id.h"
 #include "third_party/perfetto/include/perfetto/tracing/track_event_args.h"
 
 namespace content {
@@ -50,10 +52,9 @@
   }
   void SetPrefetchStatus(PrefetchStatus prefetch_status);
 
-  bool is_prerender_matched_with_prefetch() const {
-    return is_prerender_matched_with_prefetch_;
-  }
-  void MarkPrerenderMatchedWithPrefetch();
+  bool IsPrerenderMatchedWithPrefetch(
+      const PrerenderHostId& prerender_host_id) const;
+  void MarkPrerenderMatchedWithPrefetch(PrerenderHostId prerender_host_id);
 
  private:
   friend class base::RefCounted<PreloadPipelineInfo>;
@@ -70,9 +71,9 @@
       PreloadingEligibility::kUnspecified;
   std::optional<PrefetchStatus> prefetch_status_ = std::nullopt;
 
-  // Set to true when the prerender in this pipeline matches a prefetch.
-  // Note that the prefetch may not be the prefetch in this pipeline.
-  bool is_prerender_matched_with_prefetch_ = false;
+  // Records `PrerenderHostId` that matched to a prefetch. Note that the
+  // prefetch may not be the prefetch in this pipeline.
+  base::flat_set<PrerenderHostId> prerender_ids_matched_with_prefetch_;
 };
 
 }  // namespace content
diff --git a/content/browser/preloading/prerender/prerender_host.cc b/content/browser/preloading/prerender/prerender_host.cc
index 6b0321f..6dfe382 100644
--- a/content/browser/preloading/prerender/prerender_host.cc
+++ b/content/browser/preloading/prerender/prerender_host.cc
@@ -1817,7 +1817,8 @@
 
   // Use a prefetch (in many cases, aheaf of prerender) if it is about to be
   // used.
-  if (attributes_.preload_pipeline_info->is_prerender_matched_with_prefetch()) {
+  if (attributes_.preload_pipeline_info->IsPrerenderMatchedWithPrefetch(
+          prerender_host_id())) {
     return false;
   }
 
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index c5ee522..12304d4 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -7244,6 +7244,16 @@
     JavaScriptDialogType dialog_type,
     bool disable_third_party_subframe_suppresion,
     JavaScriptDialogCallback ipc_response_callback) {
+  // Sandboxed frames should only be allowed to show modal dialogs when they
+  // have the "allow-modals" attribute. This should have already been checked
+  // by the renderer process (see LocalDOMWindow::alert/confirm/prompt), and
+  // this browser-side check defends against compromised renderers.
+  if (IsSandboxed(network::mojom::WebSandboxFlags::kModals)) {
+    bad_message::ReceivedBadMessage(
+        GetProcess(), bad_message::RFH_MODAL_DIALOG_FROM_SANDBOXED_FRAME);
+    return;
+  }
+
   // Don't show the dialog if it's triggered on a non-active RenderFrameHost
   // or is contained in a Fenced Frame.
   if (!IsActive() || IsNestedWithinFencedFrame()) {
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 5664e61..7a1bdc10 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -3493,6 +3493,8 @@
                            CreateNewWindowWithInaccessibleFile);
   FRIEND_TEST_ALL_PREFIXES(SecurityExploitBrowserTest,
                            WindowOpenDisallowedFromSandboxedFrame);
+  FRIEND_TEST_ALL_PREFIXES(SecurityExploitBrowserTest,
+                           ModalDialogDisallowedFromSandboxedFrame);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
                            RenderViewHostIsNotReusedAfterDelayedUnloadACK);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessBrowserTest,
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index ccf0287..d2fea7e 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -1160,6 +1160,51 @@
   EXPECT_EQ(1u, Shell::windows().size());
 }
 
+// Regression test for browser-side validation of the allow-modals sandbox
+// attribute. A sandboxed frame without allow-modals should not be able to show
+// modal dialogs (alert, confirm, prompt). This is a variant of
+// WindowOpenDisallowedFromSandboxedFrame for the kModals sandbox flag.
+IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest,
+                       ModalDialogDisallowedFromSandboxedFrame) {
+  IsolateOrigin("b.com");
+
+  GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  FrameTreeNode* root = web_contents->GetPrimaryFrameTree().root();
+  RenderFrameHostImpl* main_frame = root->current_frame_host();
+
+  // Create cross-site sandboxed child frame. The frame lacks the allow-modals
+  // attribute, so it should not be allowed to show modal dialogs.
+  GURL child_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
+  {
+    std::string js_str = base::StringPrintf(
+        "var frame = document.createElement('iframe'); "
+        "frame.sandbox = 'allow-scripts'; "
+        "frame.src = '%s'; "
+        "document.body.appendChild(frame);",
+        child_url.spec().c_str());
+    EXPECT_TRUE(ExecJs(main_frame, js_str));
+    ASSERT_TRUE(WaitForLoadStop(web_contents));
+  }
+
+  RenderFrameHostImpl* subframe = root->child_at(0)->current_frame_host();
+  EXPECT_TRUE(
+      subframe->GetSiteInstance()->GetSecurityPrincipal().IsSandboxed());
+  EXPECT_TRUE(subframe->IsSandboxed(network::mojom::WebSandboxFlags::kModals));
+
+  // Simulate that the b.com renderer is compromised and sends an IPC to show
+  // a modal dialog, bypassing renderer-side checks in LocalDOMWindow::alert().
+  // The browser process should detect this and terminate the renderer.
+  RenderProcessHostBadIpcMessageWaiter kill_waiter(subframe->GetProcess());
+  subframe->RunModalAlertDialog(u"test", false, base::DoNothing());
+  EXPECT_EQ(bad_message::RFH_MODAL_DIALOG_FROM_SANDBOXED_FRAME,
+            kill_waiter.Wait());
+  EXPECT_FALSE(subframe->IsRenderFrameLive());
+}
+
 // Regression test for browser-side validation of POST submissions in
 // CreateNewWindow. See https://crbug.com/487768779.
 IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest,
diff --git a/docs/webui/webui_lit_style_guide.md b/docs/webui/webui_lit_style_guide.md
index 4f56915..3a11be0 100644
--- a/docs/webui/webui_lit_style_guide.md
+++ b/docs/webui/webui_lit_style_guide.md
@@ -374,7 +374,8 @@
 Where the pattern to derive the class and file names from the DOM name is
 
 * DOM name: `foo-bar-baz`
-* Class name candidates: `FooBarBazElement`, or `BarBazElement`, or `BazElement`
+* Class name candidates: `<OptionalPrefix>FooBarBazElement`, or `BarBazElement`,
+  or `BazElement`
 * File name candidates: `foo_bar_baz.{css,html,ts}`, `bar_baz.{css,html,ts}` or
   `baz.{css,html,ts}`,
 
diff --git a/extensions/browser/service_worker/service_worker_state.cc b/extensions/browser/service_worker/service_worker_state.cc
index b29eaeb..d6000569 100644
--- a/extensions/browser/service_worker/service_worker_state.cc
+++ b/extensions/browser/service_worker/service_worker_state.cc
@@ -181,9 +181,6 @@
   }
 
   if (renderer_state() != RendererState::kNotActive) {
-    // If the new token is different from the preexisting token, and it's still
-    // live, we are in an unexpected situation in which the content layer is
-    // tracking two different service workers.
     auto renderer_state_debug = renderer_state_;
     base::debug::Alias(&renderer_state_debug);
     CHECK(worker_id_.has_value());
@@ -194,13 +191,18 @@
     base::debug::Alias(&preexisting_version_id);
     base::debug::Alias(&new_version_id);
     if (preexisting_token != new_token &&
+        preexisting_version_id >= new_version_id &&
         service_worker_context_->IsLiveServiceWorkerWithToken(
             preexisting_version_id, preexisting_token)) {
+      // We received an initialization signal for an older (or equal) worker
+      // version with a different token, while the worker we are currently
+      // tracking is still live. Record a crash dump for investigation and drop
+      // the stale IPC to prevent overwriting the current worker's state.
       base::debug::DumpWithoutCrashing();
       return;
     }
-    // However, if the preexisting instance is actually not running anymore,
-    // then we should set a new `worker_id` here.
+    // TODO(andreaorru): if the preexisting version_id / service worker token is
+    // not valid anymore, should we untrack it from ProcessManager here?
   }
 
   SetWorkerId(worker_id);
diff --git a/infra/inclusive_language_presubmit_exempt_dirs.txt b/infra/inclusive_language_presubmit_exempt_dirs.txt
index 5bf36bf..7395788 100644
--- a/infra/inclusive_language_presubmit_exempt_dirs.txt
+++ b/infra/inclusive_language_presubmit_exempt_dirs.txt
@@ -669,7 +669,7 @@
 third_party/rust/chromium_crates_io/vendor/icu_normalizer-v2/benches/data
 third_party/rust/chromium_crates_io/vendor/image-v0_25
 third_party/rust/chromium_crates_io/vendor/indexmap-v2/.github/workflows
-third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.github/workflows
+third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/workflows
 third_party/rust/chromium_crates_io/vendor/itoa-v1
 third_party/rust/chromium_crates_io/vendor/itoa-v1/.github/workflows
 third_party/rust/chromium_crates_io/vendor/itoa-v1/src
diff --git a/internal b/internal
index 5f3259b..926c416 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 5f3259bc268e83f385b3d235a66f8fbd380336f8
+Subproject commit 926c416205c73ac88c808786ed9f9f1e659f6bfc
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm
index 63d456c..c100dccb9 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm
@@ -99,13 +99,19 @@
     }
   }
 
-  [self
-      registerForTraitChanges:
-          @[ UITraitVerticalSizeClass.class, UITraitHorizontalSizeClass.class ]
-                   withAction:@selector(updateLayout)];
+  NSArray<UITrait>* traits = TraitCollectionSetForTraits(
+      @[ UITraitVerticalSizeClass.class, UITraitHorizontalSizeClass.class ]);
+  [self registerForTraitChanges:traits withAction:@selector(setNeedsLayout)];
   [super didMoveToSuperview];
 }
 
+- (void)layoutSubviews {
+  [super layoutSubviews];
+  // Perform updates during the layout phase to avoid re-entrancy issues
+  // during trait collection changes.
+  [self updateLayout];
+}
+
 // Returns intrinsicContentSize based on the content of the toolbar.
 // When showing the floating Button the contentsize for the toolbar should be
 // zero so that the toolbar isn't accounted for when calculating the bottom
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm
index a4603056..3277b92 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm
@@ -336,19 +336,23 @@
     }
   }
 
+  NSArray<UITrait>* traits = TraitCollectionSetForTraits(
+      @[ UITraitVerticalSizeClass.class, UITraitHorizontalSizeClass.class ]);
   __weak TabGridTopToolbar* weakSelf = self;
-  [weakSelf
-      registerForTraitChanges:
-          @[ UITraitVerticalSizeClass.class, UITraitHorizontalSizeClass.class ]
-                  withHandler:^(id<UITraitEnvironment> traitEnvironment,
-                                UITraitCollection* previousCollection) {
-                    [weakSelf
-                        setButtonsForTraitCollection:weakSelf.traitCollection];
-                  }];
+  [weakSelf registerForTraitChanges:traits
+                         withAction:@selector(setNeedsLayout)];
 
   [super didMoveToSuperview];
 }
 
+- (void)layoutSubviews {
+  [super layoutSubviews];
+  // Perform updates during the layout phase to avoid re-entrancy issues
+  // during trait collection changes.
+  __weak TabGridTopToolbar* weakSelf = self;
+  [weakSelf setButtonsForTraitCollection:weakSelf.traitCollection];
+}
+
 #pragma mark - Private
 
 // Returns a new button to be used.
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index d488ef9..e922fe8 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -1123,6 +1123,10 @@
 BASE_FEATURE(kUseAudioManagerMaxChannelLayout,
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Allows native temporal layer ID retrieval for NdkVideoEncodeAccelerator.
+BASE_FEATURE(kNdkVideoEncodeAcceleratorNativeSvc,
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 #endif  // BUILDFLAG(IS_ANDROID)
 
 // TODO(crbug.com/414430336): Consider restricting to IS_CHROMEOS.
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index a9986de1..839047ef 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -388,6 +388,7 @@
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kUseAudioLatencyFromHAL);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kUseAudioManagerMaxChannelLayout);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kUseSecurityLevelWhenCheckingMediaDrmVersion);
+MEDIA_EXPORT BASE_DECLARE_FEATURE(kNdkVideoEncodeAcceleratorNativeSvc);
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(USE_LINUX_VIDEO_ACCELERATION)
diff --git a/media/gpu/android/ndk_video_encode_accelerator.cc b/media/gpu/android/ndk_video_encode_accelerator.cc
index 47dc148a..3cc62dd59 100644
--- a/media/gpu/android/ndk_video_encode_accelerator.cc
+++ b/media/gpu/android/ndk_video_encode_accelerator.cc
@@ -475,6 +475,15 @@
         GetOptimalLayeringSchema(log, codec_name, num_temporal_layers);
     AMediaFormat_setString(result.get(), AMEDIAFORMAT_KEY_TEMPORAL_LAYERING,
                            svc_layer_config.c_str());
+
+    // Signal that we want to receive the temporal layer ID in the output
+    // format.
+    if (NdkVideoEncodeAcceleratorSvcApi::IsTemporalLayerIdSupported()) {
+      AMediaFormat_setInt32(
+          result.get(),
+          NdkVideoEncodeAcceleratorSvcApi::AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID,
+          0);
+    }
   }
 
   return result;
@@ -1567,25 +1576,59 @@
       input_since_keyframe_count_ = 0;
     }
 
-    TemporalScalabilityIdExtractor::BitstreamMetadata bits_md;
-    if (!svc_parser_->ParseChunk(out_buffer_data, input_since_keyframe_count_,
-                                 bits_md)) {
-      NotifyErrorStatus({EncoderStatus::Codes::kEncoderHardwareDriverError,
-                         "Parse bitstream failed"});
+    int32_t temporal_id = -1;
+    if (NdkVideoEncodeAcceleratorSvcApi::IsTemporalLayerIdSupported()) {
+      // For supported Android versions, retrieve the temporal layer ID
+      // natively from the output buffer format.
+      MediaFormatPtr buffer_format(AMediaCodec_getBufferFormat(
+          media_codec_->codec(), output_buffer.buffer_index));
+      if (buffer_format) {
+        AMediaFormat_getInt32(
+            buffer_format.get(),
+            NdkVideoEncodeAcceleratorSvcApi::AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID,
+            &temporal_id);
+      }
+    }
+
+    if (temporal_id < 0 && VideoCodecProfileToVideoCodec(
+                               config_.output_profile) == VideoCodec::kH264) {
+      // For H.264, if native retrieval is not supported or failed,
+      // fallback to parsing the bitstream.
+      TemporalScalabilityIdExtractor::BitstreamMetadata bits_md;
+      if (!svc_parser_->ParseChunk(out_buffer_data, input_since_keyframe_count_,
+                                   bits_md)) {
+        NotifyErrorStatus({EncoderStatus::Codes::kEncoderHardwareDriverError,
+                           "Parse bitstream failed"});
+        return;
+      }
+      temporal_id = bits_md.temporal_id;
+    }
+
+    if (temporal_id < 0 &&
+        NdkVideoEncodeAcceleratorSvcApi::IsTemporalLayerIdSupported()) {
+      // If native retrieval was expected but failed, treat it as a hardware
+      // error. For AV1/VP9 on older Android versions, we don't error out
+      // because these codecs follow standard scalability modes
+      // (follow_svc_spec = true), where layer information is available in the
+      // bitstream itself.
+      NotifyErrorStatus(
+          {EncoderStatus::Codes::kEncoderHardwareDriverError,
+           "Failed to retrieve temporal layer ID for SVC stream"});
       return;
     }
 
     switch (VideoCodecProfileToVideoCodec(config_.output_profile)) {
       case VideoCodec::kH264:
-        metadata.h264.emplace().temporal_idx = bits_md.temporal_id;
+        metadata.h264.emplace().temporal_idx = temporal_id;
         break;
       case VideoCodec::kAV1:
       case VideoCodec::kVP9:
-        // TODO(b/432558680): We should query for this from the new temporal
-        // layer encoding API once it's available. Currently, the only encoders
-        // on Android that implement AV1 and VP9 temporal layer encoding are the
-        // cros-codecs ones, which we know to support SVC spec.
         metadata.svc_generic.emplace().follow_svc_spec = true;
+        if (NdkVideoEncodeAcceleratorSvcApi::IsTemporalLayerIdSupported()) {
+          // Native temporal ID is only expected for AV1/VP9 when supported
+          // by the platform.
+          metadata.svc_generic->temporal_idx = temporal_id;
+        }
         break;
       default:
         NOTIMPLEMENTED() << "SVC is only supported for AV1, H.264, and VP9.";
diff --git a/media/gpu/android/ndk_video_encode_accelerator_svc_api.cc b/media/gpu/android/ndk_video_encode_accelerator_svc_api.cc
index be56be82..6314dea 100644
--- a/media/gpu/android/ndk_video_encode_accelerator_svc_api.cc
+++ b/media/gpu/android/ndk_video_encode_accelerator_svc_api.cc
@@ -8,6 +8,7 @@
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/native_library.h"
+#include "media/base/media_switches.h"
 
 namespace media {
 
@@ -55,4 +56,14 @@
   }
 }
 
+// static
+bool NdkVideoEncodeAcceleratorSvcApi::IsTemporalLayerIdSupported() {
+  if (__builtin_available(android 37, *)) {
+    return base::FeatureList::IsEnabled(
+        media::kNdkVideoEncodeAcceleratorNativeSvc);
+  }
+
+  return false;
+}
+
 }  // namespace media
diff --git a/media/gpu/android/ndk_video_encode_accelerator_svc_api.h b/media/gpu/android/ndk_video_encode_accelerator_svc_api.h
index 22a80a0..e55b2609 100644
--- a/media/gpu/android/ndk_video_encode_accelerator_svc_api.h
+++ b/media/gpu/android/ndk_video_encode_accelerator_svc_api.h
@@ -6,6 +6,7 @@
 #define MEDIA_GPU_ANDROID_NDK_VIDEO_ENCODE_ACCELERATOR_SVC_API_H_
 
 #include <media/NdkMediaCodec.h>
+#include <stdint.h>
 
 #include "base/no_destructor.h"
 #include "media/gpu/media_gpu_export.h"
@@ -46,6 +47,11 @@
   ACodecEncoderCapabilities_getSupportedLayeringSchemas_Type
       ACodecEncoderCapabilities_getSupportedLayeringSchemas = nullptr;
 
+  static bool IsTemporalLayerIdSupported();
+
+  static constexpr const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_ID =
+      "temporal-layer-id";
+
  private:
   friend class base::NoDestructor<NdkVideoEncodeAcceleratorSvcApi>;
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index ea8172d..a451f16 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -118,26 +118,6 @@
             ]
         }
     ],
-    "AXBlockFlowIterator": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AccessibilityBlockFlowIterator"
-                    ]
-                }
-            ]
-        }
-    ],
     "AXRandomizedStressTests": [
         {
             "platforms": [
@@ -645,21 +625,6 @@
             ]
         }
     ],
-    "AllowChangingSelectedContent": [
-        {
-            "platforms": [
-                "mac"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AllowChangingSelectedContent"
-                    ]
-                }
-            ]
-        }
-    ],
     "AllowDatapipeDrainedAsBytesConsumerInBFCache": [
         {
             "platforms": [
@@ -769,21 +734,6 @@
             ]
         }
     ],
-    "AndroidAudioDeviceListener": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AndroidAudioDeviceListener"
-                    ]
-                }
-            ]
-        }
-    ],
     "AndroidBookmarkHistoryPane": [
         {
             "platforms": [
@@ -1377,21 +1327,6 @@
             ]
         }
     ],
-    "AndroidUseCorrectWindowBounds": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AndroidUseCorrectWindowBounds"
-                    ]
-                }
-            ]
-        }
-    ],
     "AndroidUseDisplayTopology": [
         {
             "platforms": [
@@ -1557,51 +1492,6 @@
             ]
         }
     ],
-    "AnnotatorMode": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AnnotatorMode"
-                    ]
-                }
-            ]
-        }
-    ],
-    "ApiPrintingMarginsAndScale": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ApiPrintingMarginsAndScale"
-                    ]
-                }
-            ]
-        }
-    ],
-    "ApnRevamp": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ApnRevamp"
-                    ]
-                }
-            ]
-        }
-    ],
     "AppListLaunchRecorder": [
         {
             "platforms": [
@@ -2060,26 +1950,6 @@
             ]
         }
     ],
-    "AudioDecoderAudioFileReader": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AudioDecoderAudioFileReader"
-                    ]
-                }
-            ]
-        }
-    ],
     "AudioFocusEnforcementStudy": [
         {
             "platforms": [
@@ -2125,21 +1995,6 @@
             ]
         }
     ],
-    "AuthPanelInSession": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "UseAuthPanelInSession"
-                    ]
-                }
-            ]
-        }
-    ],
     "AutoDisableAccessibility": [
         {
             "platforms": [
@@ -3624,28 +3479,6 @@
             ]
         }
     ],
-    "AvoidTrustedParamsCopies": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "fuchsia",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AvoidTrustedParamsCopies"
-                    ]
-                }
-            ]
-        }
-    ],
     "AvoidUnnecessaryForcedLayoutMeasurements": [
         {
             "platforms": [
@@ -3784,28 +3617,6 @@
             ]
         }
     ],
-    "BackForwardCacheNotRestoredReasons": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "fuchsia",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "BackForwardCacheSendNotRestoredReasons"
-                    ]
-                }
-            ]
-        }
-    ],
     "BackForwardCachePauseMicrotasks": [
         {
             "platforms": [
@@ -3872,27 +3683,6 @@
             ]
         }
     ],
-    "BackgroundResourceFetch": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "BackgroundResourceFetch",
-                        "ReduceTransferSizeUpdatedIPC"
-                    ]
-                }
-            ]
-        }
-    ],
     "BackgroundTabsInterventions": [
         {
             "platforms": [
@@ -3956,21 +3746,6 @@
             ]
         }
     ],
-    "BatteryBadgeIconEnabled": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "BatteryBadgeIcon"
-                    ]
-                }
-            ]
-        }
-    ],
     "BatterySaverModeAlignWakeUps": [
         {
             "platforms": [
@@ -4216,53 +3991,6 @@
             ]
         }
     ],
-    "BocaScreenSharingStudent": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "BocaScreenSharingStudent"
-                    ]
-                }
-            ]
-        }
-    ],
-    "BocaScreenSharingTeacher": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "BocaScreenSharingTeacher"
-                    ]
-                }
-            ]
-        }
-    ],
-    "BookmarksImportOnFirstRun": [
-        {
-            "platforms": [
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "BookmarksImportOnFirstRun"
-                    ]
-                }
-            ]
-        }
-    ],
     "BookmarksMigrateUiChanges": [
         {
             "platforms": [
@@ -4328,21 +4056,6 @@
             ]
         }
     ],
-    "BorealisZinkGlDriver": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "BorealisZinkGlDriver"
-                    ]
-                }
-            ]
-        }
-    ],
     "BrowserControlsEarlyResize": [
         {
             "platforms": [
@@ -4667,26 +4380,6 @@
             ]
         }
     ],
-    "CSSReadingFlow": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "CSSReadingFlow"
-                    ]
-                }
-            ]
-        }
-    ],
     "CacheIdentityListInChrome": [
         {
             "platforms": [
@@ -4702,21 +4395,6 @@
             ]
         }
     ],
-    "CacheIsMultiInstanceApi31Enabled": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "CacheIsMultiInstanceApi31Enabled"
-                    ]
-                }
-            ]
-        }
-    ],
     "CacheSharingForPervasiveResources": [
         {
             "platforms": [
@@ -4737,21 +4415,6 @@
             ]
         }
     ],
-    "CacheStylusSettings": [
-        {
-            "platforms": [
-                "android_webview"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "CacheStylusSettings"
-                    ]
-                }
-            ]
-        }
-    ],
     "Canvas2DAutoFlushParams": [
         {
             "platforms": [
@@ -4934,24 +4597,6 @@
             ]
         }
     ],
-    "ChromeCompose": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "Compose"
-                    ]
-                }
-            ]
-        }
-    ],
     "ChromeOSARCVMLmkPerceptibleMinState": [
         {
             "platforms": [
@@ -5878,42 +5523,6 @@
             ]
         }
     ],
-    "ChromnientUpdatedFeedbackEntrypoint": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "LensUpdatedFeedbackEntrypoint"
-                    ]
-                }
-            ]
-        }
-    ],
-    "ChromnientVideoCitations": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "LensVideoCitations"
-                    ]
-                }
-            ]
-        }
-    ],
     "ChromnientZeroStateCsb": [
         {
             "platforms": [
@@ -5980,21 +5589,6 @@
             ]
         }
     ],
-    "ClankMostVisitedTilesCustomization": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "MostVisitedTilesCustomization"
-                    ]
-                }
-            ]
-        }
-    ],
     "ClankMostVisitedTilesNewScoring": [
         {
             "platforms": [
@@ -6228,25 +5822,6 @@
             ]
         }
     ],
-    "ClientSideDetectionSamplePing": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ClientSideDetectionSamplePing"
-                    ]
-                }
-            ]
-        }
-    ],
     "ClientSideDetectionSendLlamaForcedTriggerInfo": [
         {
             "platforms": [
@@ -6340,21 +5915,6 @@
             ]
         }
     ],
-    "CloudProfileReporting": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "CloudProfileReporting"
-                    ]
-                }
-            ]
-        }
-    ],
     "Collections": [
         {
             "platforms": [
@@ -7030,21 +6590,6 @@
             ]
         }
     ],
-    "ControlsVisibilityFromNavigations": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ControlsVisibilityFromNavigations"
-                    ]
-                }
-            ]
-        }
-    ],
     "CookieDeprecationFacilitatedTestingCookieDeprecation": [
         {
             "platforms": [
@@ -7098,21 +6643,6 @@
             ]
         }
     ],
-    "CopyClientKeysCertsToChaps": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "CopyClientKeysCertsToChaps"
-                    ]
-                }
-            ]
-        }
-    ],
     "CpssUseTfliteSignatureRunnerKillSwitch": [
         {
             "platforms": [
@@ -7796,50 +7326,6 @@
             ]
         }
     ],
-    "CursiveManagedStylusPreinstall": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "CursiveManagedStylusPreinstall"
-                    ]
-                }
-            ]
-        }
-    ],
-    "CustomizableSelect": [
-        {
-            "platforms": [
-                "android",
-                "linux",
-                "mac",
-                "windows",
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "CustomizableSelect",
-                        "InputClosesSelect",
-                        "SelectParserRelaxation"
-                    ]
-                },
-                {
-                    "name": "Disabled",
-                    "disable_features": [
-                        "CustomizableSelect",
-                        "InputClosesSelect",
-                        "SelectParserRelaxation"
-                    ]
-                }
-            ]
-        }
-    ],
     "CustomizeChromeExperiments": [
         {
             "platforms": [
@@ -8109,26 +7595,6 @@
             ]
         }
     ],
-    "DefaultSiteInstanceGroups": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "DefaultSiteInstanceGroups"
-                    ]
-                }
-            ]
-        }
-    ],
     "DeferSpeculativeRFHCreation": [
         {
             "platforms": [
@@ -8211,36 +7677,6 @@
             ]
         }
     ],
-    "DemoModeSignInNonUS": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "DemoModeSignIn"
-                    ]
-                }
-            ]
-        }
-    ],
-    "DemoModeSignInUS": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "DemoModeSignIn"
-                    ]
-                }
-            ]
-        }
-    ],
     "DeprecateUnload": [
         {
             "platforms": [
@@ -8303,21 +7739,6 @@
             ]
         }
     ],
-    "DeskButton": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20231025",
-                    "enable_features": [
-                        "DeskButton"
-                    ]
-                }
-            ]
-        }
-    ],
     "DesktopCapturePermissionCheckerPreMacos14_4": [
         {
             "platforms": [
@@ -8372,25 +7793,6 @@
             ]
         }
     ],
-    "DesktopNtpMiddleSlotPromoDismissal": [
-        {
-            "platforms": [
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "MiddleSlotPromoDismissal",
-                    "enable_features": [
-                        "NtpMiddleSlotPromoDismissal"
-                    ]
-                }
-            ]
-        }
-    ],
     "DesktopNtpModules": [
         {
             "platforms": [
@@ -8591,35 +7993,6 @@
             ]
         }
     ],
-    "DesktopOmniboxShortcutBoost": [
-        {
-            "platforms": [
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "ShortcutBoostGroupWithSearches": "true",
-                        "ShortcutBoostNonTopHitSearchThreshold": "2",
-                        "ShortcutBoostNonTopHitThreshold": "2",
-                        "ShortcutBoostSearchScore": "1414",
-                        "ShortcutBoostUrlScore": "1414",
-                        "omnibox_history_cluster_provider_inherit_search_match_score": "true",
-                        "omnibox_history_cluster_provider_score": "1414"
-                    },
-                    "enable_features": [
-                        "JourneysOmniboxHistoryClusterProvider",
-                        "OmniboxShortcutBoost"
-                    ]
-                }
-            ]
-        }
-    ],
     "DesktopOmniboxStarterPackExpansion": [
         {
             "platforms": [
@@ -8758,21 +8131,6 @@
             ]
         }
     ],
-    "DexFixer": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "DexFixer"
-                    ]
-                }
-            ]
-        }
-    ],
     "DiacriticsOnPhysicalKeyboardLongpressOnByDefault": [
         {
             "platforms": [
@@ -8897,21 +8255,6 @@
             ]
         }
     ],
-    "DisableGeoLanguageModel": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "DisableGeoLanguageModel"
-                    ]
-                }
-            ]
-        }
-    ],
     "DisableNonYUVOverlaysFromExo": [
         {
             "platforms": [
@@ -9063,25 +8406,6 @@
             ]
         }
     ],
-    "DlpRegionalizedEndpoints": [
-        {
-            "platforms": [
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "DlpRegionalizedEndpoints"
-                    ]
-                }
-            ]
-        }
-    ],
     "DnsHttpsSvcbTimeout": [
         {
             "platforms": [
@@ -9110,28 +8434,6 @@
             ]
         }
     ],
-    "DoNotEvictOnAXLocationChange": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "fuchsia",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "DoNotEvictOnAXLocationChange"
-                    ]
-                }
-            ]
-        }
-    ],
     "DomStorageSqliteInMemory": [
         {
             "platforms": [
@@ -9152,33 +8454,6 @@
             ]
         }
     ],
-    "DownloadLater": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "EnabledWithDateTimePicker",
-                    "params": {
-                        "show_date_time_picker": "true"
-                    },
-                    "enable_features": [
-                        "DownloadLater"
-                    ]
-                },
-                {
-                    "name": "EnabledWithoutDateTimePicker",
-                    "params": {
-                        "show_date_time_picker": "false"
-                    },
-                    "enable_features": [
-                        "DownloadLater"
-                    ]
-                }
-            ]
-        }
-    ],
     "DownloadWarningSurvey": [
         {
             "platforms": [
@@ -9992,24 +9267,6 @@
             ]
         }
     ],
-    "EnablePolicyPromotionBanner": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "EnablePolicyPromotionBanner"
-                    ]
-                }
-            ]
-        }
-    ],
     "EnablePreferencesAccountStorageChromeOS": [
         {
             "platforms": [
@@ -10094,42 +9351,6 @@
             ]
         }
     ],
-    "EnableWatermarkCustomization": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "EnableWatermarkCustomization"
-                    ]
-                }
-            ]
-        }
-    ],
-    "EnableWatermarkTestPage": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "EnableWatermarkTestPage"
-                    ]
-                }
-            ]
-        }
-    ],
     "EnhancedFieldsForSecOps": [
         {
             "platforms": [
@@ -10172,24 +9393,6 @@
             ]
         }
     ],
-    "EnterpriseFileObfuscation": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "EnterpriseFileObfuscation"
-                    ]
-                }
-            ]
-        }
-    ],
     "EnterpriseFileObfuscationArchiveAnalyzer": [
         {
             "platforms": [
@@ -10359,24 +9562,6 @@
             ]
         }
     ],
-    "ExtensionManifestV2Deprecation": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_MV2ExtensionsUnsupported",
-                    "enable_features": [
-                        "ExtensionManifestV2Unsupported"
-                    ]
-                }
-            ]
-        }
-    ],
     "ExtensionsToolbarAndMenuRedesign": [
         {
             "platforms": [
@@ -10449,21 +9634,6 @@
             ]
         }
     ],
-    "ExternalDisplayEventTelemetry": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ExternalDisplayEventTelemetry"
-                    ]
-                }
-            ]
-        }
-    ],
     "ExternalHDR10": [
         {
             "platforms": [
@@ -10578,21 +9748,6 @@
             ]
         }
     ],
-    "FastPairAdvertisingFormat2025": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "FastPairAdvertisingFormat2025"
-                    ]
-                }
-            ]
-        }
-    ],
     "FastPairHandshakeLongTermRefactor": [
         {
             "platforms": [
@@ -11068,72 +10223,6 @@
             ]
         }
     ],
-    "ForceOffTextAutosizing": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ForceOffTextAutosizing"
-                    ]
-                }
-            ]
-        }
-    ],
-    "ForceOffTextAutosizingWebView": [
-        {
-            "platforms": [
-                "android_webview"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ForceOffTextAutosizing"
-                    ]
-                }
-            ]
-        }
-    ],
-    "ForestFeature": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ForestFeature"
-                    ]
-                }
-            ]
-        }
-    ],
-    "FrameRoutingCache": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "FrameRoutingCache"
-                    ]
-                }
-            ]
-        }
-    ],
     "FreezeSharedWorker": [
         {
             "platforms": [
@@ -11299,22 +10388,6 @@
             ]
         }
     ],
-    "Glic1PTools": [
-        {
-            "platforms": [
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "GlicExtensions"
-                    ]
-                }
-            ]
-        }
-    ],
     "GlicActorObservationDelayExcludeAdFrameLoading": [
         {
             "platforms": [
@@ -11552,21 +10625,6 @@
             ]
         }
     ],
-    "GlicExplicitBackgroundColor": [
-        {
-            "platforms": [
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_Dogfood",
-                    "enable_features": [
-                        "GlicExplicitBackgroundColor"
-                    ]
-                }
-            ]
-        }
-    ],
     "GlicForceNonSkSLBorder": [
         {
             "platforms": [
@@ -11601,22 +10659,6 @@
             ]
         }
     ],
-    "GlicFreWarming": [
-        {
-            "platforms": [
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "GlicFreWarming"
-                    ]
-                }
-            ]
-        }
-    ],
     "GlicGA": [
         {
             "platforms": [
@@ -11810,23 +10852,6 @@
             ]
         }
     ],
-    "GlicTieredRollout": [
-        {
-            "platforms": [
-                "mac",
-                "windows",
-                "linux"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "GlicTieredRollout"
-                    ]
-                }
-            ]
-        }
-    ],
     "GlicTrustFirstOnboarding": [
         {
             "platforms": [
@@ -12001,115 +11026,6 @@
             ]
         }
     ],
-    "GoogleLensDesktopImageFormatOptimizations": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "WebpQualityBackendV6",
-                    "params": {
-                        "dismiss-loading-state-on-document-on-load-completed-in-primary-main-frame": "false",
-                        "dismiss-loading-state-on-navigation-entry-committed": "true",
-                        "encoding-quality-image-search": "45",
-                        "encoding-quality-region-search": "45",
-                        "lens-homepage-url": "https://lens.google.com/v3/",
-                        "lens-html-redirect-fix": "false",
-                        "use-jpeg-for-image-search": "false",
-                        "use-webp-for-image-search": "true"
-                    },
-                    "enable_features": [
-                        "LensImageFormatOptimizations",
-                        "LensStandalone"
-                    ]
-                },
-                {
-                    "name": "WebpBackendOnlyV6",
-                    "params": {
-                        "encoding-quality-image-search": "90",
-                        "encoding-quality-region-search": "90",
-                        "lens-homepage-url": "https://lens.google.com/v3/",
-                        "lens-html-redirect-fix": "false",
-                        "use-jpeg-for-image-search": "false",
-                        "use-webp-for-image-search": "true"
-                    },
-                    "enable_features": [
-                        "LensImageFormatOptimizations",
-                        "LensStandalone"
-                    ]
-                },
-                {
-                    "name": "JpegQualityBackendV6",
-                    "params": {
-                        "dismiss-loading-state-on-document-on-load-completed-in-primary-main-frame": "false",
-                        "dismiss-loading-state-on-navigation-entry-committed": "true",
-                        "encoding-quality-image-search": "40",
-                        "encoding-quality-region-search": "40",
-                        "lens-homepage-url": "https://lens.google.com/v3/",
-                        "lens-html-redirect-fix": "false",
-                        "use-jpeg-for-image-search": "true",
-                        "use-webp-for-image-search": "false"
-                    },
-                    "enable_features": [
-                        "LensImageFormatOptimizations",
-                        "LensStandalone"
-                    ]
-                },
-                {
-                    "name": "JpegBackendOnlyV6",
-                    "params": {
-                        "encoding-quality-image-search": "90",
-                        "encoding-quality-region-search": "90",
-                        "lens-homepage-url": "https://lens.google.com/v3/",
-                        "lens-html-redirect-fix": "false",
-                        "use-jpeg-for-image-search": "true",
-                        "use-webp-for-image-search": "false"
-                    },
-                    "enable_features": [
-                        "LensImageFormatOptimizations",
-                        "LensStandalone"
-                    ]
-                },
-                {
-                    "name": "JpegQualityOnlyV6",
-                    "params": {
-                        "encoding-quality-image-search": "40",
-                        "encoding-quality-region-search": "40",
-                        "use-jpeg-for-image-search": "true",
-                        "use-webp-for-image-search": "false"
-                    },
-                    "enable_features": [
-                        "LensImageFormatOptimizations"
-                    ]
-                }
-            ]
-        }
-    ],
-    "GoogleLensTabletIntegration": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_110422",
-                    "params": {
-                        "enableCameraAssistedSearchOnTablet": "true",
-                        "enableCameraAssistedSearchOnTabletWidget": "true",
-                        "enableContextMenuSearchOnTablet": "true"
-                    },
-                    "enable_features": [
-                        "LensCameraAssistedSearch",
-                        "LensOnQuickActionSearchWidget"
-                    ]
-                }
-            ]
-        }
-    ],
     "GpuPersistentCache": [
         {
             "platforms": [
@@ -12232,49 +11148,6 @@
             ]
         }
     ],
-    "HTMLParserYieldByUserTiming": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "EnableWithTimeout5ms",
-                    "params": {
-                        "timeout_ms": "5"
-                    },
-                    "enable_features": [
-                        "SearchAboveFoldBytesReceived"
-                    ]
-                }
-            ]
-        }
-    ],
-    "HTTP2": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "fuchsia",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled6",
-                    "params": {
-                        "http2_grease_settings": "true"
-                    },
-                    "enable_features": [
-                        "Http2Grease"
-                    ]
-                }
-            ]
-        }
-    ],
     "HangoutsExtensionV3": [
         {
             "platforms": [
@@ -12935,27 +11808,6 @@
             ]
         }
     ],
-    "Hotspot": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_Dogfood",
-                    "enable_features": [
-                        "Hotspot"
-                    ]
-                },
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "Hotspot"
-                    ]
-                }
-            ]
-        }
-    ],
     "HttpCacheInitializeDiskCacheBackendEarly": [
         {
             "platforms": [
@@ -13141,22 +11993,6 @@
             ]
         }
     ],
-    "IOSChromnientEscapeHatch": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "IPH_iOSLensOverlayEscapeHatchTip",
-                        "LensOverlayEnableLVFEscapeHatch"
-                    ]
-                }
-            ]
-        }
-    ],
     "IOSCloseOtherTabs": [
         {
             "platforms": [
@@ -13448,36 +12284,6 @@
             ]
         }
     ],
-    "IOSFeedCUI1Refinements": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "EnableCUI1Refinements"
-                    ]
-                }
-            ]
-        }
-    ],
-    "IOSFeedCUI3NextPageAppFlow": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "EnableCUI3NextPageAppFlow"
-                    ]
-                }
-            ]
-        }
-    ],
     "IOSFullscreenScrollThreshold": [
         {
             "platforms": [
@@ -13736,21 +12542,6 @@
             ]
         }
     ],
-    "IOSMemoryAblation": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "IOSMemoryAblation"
-                    ]
-                }
-            ]
-        }
-    ],
     "IOSMostVisitedTilesCustomization": [
         {
             "platforms": [
@@ -13781,21 +12572,6 @@
             ]
         }
     ],
-    "IOSOmahaResyncTimerOnForeground": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "OmahaResyncTimerOnForeground"
-                    ]
-                }
-            ]
-        }
-    ],
     "IOSOneTapMiniMapRestrictions": [
         {
             "platforms": [
@@ -13900,21 +12676,6 @@
             ]
         }
     ],
-    "IOSPasskeyShim": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "IOSPasskeyShim"
-                    ]
-                }
-            ]
-        }
-    ],
     "IOSProactivePasswordGenerationBottomSheet": [
         {
             "platforms": [
@@ -14018,24 +12779,6 @@
             ]
         }
     ],
-    "IOSStart4Hour": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled4Hour",
-                    "params": {
-                        "ReturnToStartSurfaceInactiveDurationInSeconds": "14400"
-                    },
-                    "enable_features": [
-                        "StartSurface"
-                    ]
-                }
-            ]
-        }
-    ],
     "IOSStatelessFormSuggestionController": [
         {
             "platforms": [
@@ -14071,22 +12814,6 @@
             ]
         }
     ],
-    "IOSTFLiteLanguageDetection": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20220429",
-                    "enable_features": [
-                        "DownloadServiceForegroundSessionIOSFeature",
-                        "TFLiteLanguageDetectionEnabled"
-                    ]
-                }
-            ]
-        }
-    ],
     "IOSTabGridDragAndDrop": [
         {
             "platforms": [
@@ -14606,24 +13333,6 @@
             ]
         }
     ],
-    "InlineFullscreenPerfExperiment": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "InlineFullscreenPerfExperiment"
-                    ]
-                }
-            ]
-        }
-    ],
     "InputVizard": [
         {
             "platforms": [
@@ -14716,22 +13425,6 @@
             ]
         }
     ],
-    "IsolatedWebApps": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "IsolatedWebAppAutomaticUpdates",
-                        "IsolatedWebApps"
-                    ]
-                }
-            ]
-        }
-    ],
     "IsolatesPriorityUseProcessPriority": [
         {
             "platforms": [
@@ -15260,26 +13953,6 @@
             ]
         }
     ],
-    "LazyUpdateTranslateModel": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "LazyUpdateTranslateModel"
-                    ]
-                }
-            ]
-        }
-    ],
     "LegacyKeyRepeatSynthesis": [
         {
             "platforms": [
@@ -15788,26 +14461,6 @@
             ]
         }
     ],
-    "MediaDeviceIdStoragePartitioning": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "MediaDeviceIdPartitioning"
-                    ]
-                }
-            ]
-        }
-    ],
     "MediaFoundationBatchRead": [
         {
             "platforms": [
@@ -15998,63 +14651,6 @@
             ]
         }
     ],
-    "MemoryReducerGcCount": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "compaction_max_evacuated_bytes_mb_for_reduce_memory": "36",
-                        "compaction_target_fragmentation_percent_for_reduce_memory": "20",
-                        "memory_reducer_gc_count": "1"
-                    },
-                    "enable_features": [
-                        "V8Flag_memory_reducer"
-                    ]
-                }
-            ]
-        }
-    ],
-    "MessagesPreinstall": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "MessagesPreinstall"
-                    ]
-                }
-            ]
-        }
-    ],
-    "MetricsClusteringSurvey": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_CANARY_DEV_50_20240918",
-                    "enable_features": [
-                        "SegmentationPlatformMetricsClustering",
-                        "SegmentationSurveyPage"
-                    ]
-                }
-            ]
-        }
-    ],
     "MetricsDataQuality": [
         {
             "platforms": [
@@ -16108,44 +14704,6 @@
             ]
         }
     ],
-    "MigrateDefaultChromeAppToWebAppsGSuite": [
-        {
-            "platforms": [
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20210111",
-                    "enable_features": [
-                        "MigrateDefaultChromeAppToWebAppsGSuite"
-                    ]
-                }
-            ]
-        }
-    ],
-    "MigrateDefaultChromeAppToWebAppsNonGSuite": [
-        {
-            "platforms": [
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20210111",
-                    "enable_features": [
-                        "MigrateDefaultChromeAppToWebAppsNonGSuite"
-                    ]
-                }
-            ]
-        }
-    ],
     "MigrateIOSKeychainAccessibility": [
         {
             "platforms": [
@@ -16320,21 +14878,6 @@
             ]
         }
     ],
-    "MultiCalendarSupport": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "MultiCalendarSupport"
-                    ]
-                }
-            ]
-        }
-    ],
     "MultipleSpareRPHs": [
         {
             "platforms": [
@@ -16371,21 +14914,6 @@
             ]
         }
     ],
-    "NTPBackgroundCustomization": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "NTPBackgroundCustomization"
-                    ]
-                }
-            ]
-        }
-    ],
     "NTPBackgroundImageCache": [
         {
             "platforms": [
@@ -16401,25 +14929,6 @@
             ]
         }
     ],
-    "NTPPopularSites": [
-        {
-            "platforms": [
-                "android",
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Version7",
-                    "params": {
-                        "version": "7"
-                    },
-                    "enable_features": [
-                        "NTPPopularSites"
-                    ]
-                }
-            ]
-        }
-    ],
     "NavigationThrottleRegistryAttributeCache": [
         {
             "platforms": [
@@ -16439,36 +14948,6 @@
             ]
         }
     ],
-    "NearbyBleV2": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "EnableNearbyBleV2"
-                    ]
-                }
-            ]
-        }
-    ],
-    "NearbyShareNameEnabled": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "IsNameEnabled"
-                    ]
-                }
-            ]
-        }
-    ],
     "NearbySharingRemoveRestrictToContacts": [
         {
             "platforms": [
@@ -16484,21 +14963,6 @@
             ]
         }
     ],
-    "NearbySharingWifiDirect": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "NearbySharingWifiDirect"
-                    ]
-                }
-            ]
-        }
-    ],
     "NetworkQualityEstimatorAsync": [
         {
             "platforms": [
@@ -16894,21 +15358,6 @@
             ]
         }
     ],
-    "NotificationLimit": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "NotificationLimit"
-                    ]
-                }
-            ]
-        }
-    ],
     "NotificationTelemetrySwb": [
         {
             "platforms": [
@@ -16948,21 +15397,6 @@
             ]
         }
     ],
-    "NotificationWidthIncrease": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "NotificationWidthIncrease"
-                    ]
-                }
-            ]
-        }
-    ],
     "NotificationsIgnoreRequireInteraction": [
         {
             "platforms": [
@@ -17170,21 +15604,6 @@
             ]
         }
     ],
-    "OfferPinToTaskbarInSettings": [
-        {
-            "platforms": [
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "MostlyLaunched",
-                    "enable_features": [
-                        "OfferPinToTaskbarInSettings"
-                    ]
-                }
-            ]
-        }
-    ],
     "OfferPinToTaskbarWhenSettingDefault": [
         {
             "platforms": [
@@ -17238,23 +15657,6 @@
             ]
         }
     ],
-    "OidcAuthProfileManagement": [
-        {
-            "platforms": [
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "OidcAuthProfileManagement"
-                    ]
-                }
-            ]
-        }
-    ],
     "OmahaMinSdkVersionAndroid": [
         {
             "platforms": [
@@ -17327,21 +15729,6 @@
         },
         {
             "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "AndroidExperiments",
-                    "enable_features": [
-                        "OmniboxDynamicMaxAutocomplete",
-                        "OmniboxMaxURLMatches",
-                        "OmniboxUIExperimentMaxAutocompleteMatches"
-                    ]
-                }
-            ]
-        },
-        {
-            "platforms": [
                 "ios"
             ],
             "experiments": [
@@ -17368,27 +15755,6 @@
             ]
         }
     ],
-    "OmniboxInspireMePhase2": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "EnabledCarouselAboveTrends",
-                    "params": {
-                        "QueryTilesShowAboveTrends": "true",
-                        "QueryTilesShowAsCarousel": "false",
-                        "experiment_tag": "{maxLevels: 1, rankTiles: true}"
-                    },
-                    "enable_features": [
-                        "OmniboxQueryTilesInZPSOnNTP",
-                        "QueryTiles"
-                    ]
-                }
-            ]
-        }
-    ],
     "OmniboxLogURLScoringSignals": [
         {
             "platforms": [
@@ -17581,21 +15947,6 @@
             ]
         }
     ],
-    "OnDeviceStorage": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "OnDeviceStorage"
-                    ]
-                }
-            ]
-        }
-    ],
     "OnTaskStatusCheck": [
         {
             "platforms": [
@@ -17614,21 +15965,6 @@
             ]
         }
     ],
-    "OngoingProcesses": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "OngoingProcesses"
-                    ]
-                }
-            ]
-        }
-    ],
     "OptGuideBatchSRPTuning": [
         {
             "platforms": [
@@ -17675,23 +16011,6 @@
             ]
         }
     ],
-    "OptimizationGuideOnDeviceModelCpuBackend": [
-        {
-            "platforms": [
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "OnDeviceModelCpuBackend"
-                    ]
-                }
-            ]
-        }
-    ],
     "OptimizeAssociateWindowsAndroid": [
         {
             "platforms": [
@@ -17865,44 +16184,6 @@
             ]
         }
     ],
-    "OzonePlatformAuto": [
-        {
-            "platforms": [
-                "linux"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_Dogfood",
-                    "enable_features": [
-                        "AcceleratedVideoDecodeLinuxGL",
-                        "AcceleratedVideoDecodeLinuxZeroCopyGL",
-                        "OverrideDefaultOzonePlatformHintToAuto"
-                    ]
-                },
-                {
-                    "name": "Default",
-                    "disable_features": [
-                        "OverrideDefaultOzonePlatformHintToAuto"
-                    ]
-                }
-            ]
-        }
-    ],
-    "PWAIconAndTitleInNativeNotificationsWin": [
-        {
-            "platforms": [
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "EnablePWAIconAndTitleInNativeNotificationsWin",
-                    "enable_features": [
-                        "AppSpecificNotifications"
-                    ]
-                }
-            ]
-        }
-    ],
     "PageActionsMigration": [
         {
             "platforms": [
@@ -18011,27 +16292,6 @@
             ]
         }
     ],
-    "ParkableStringsLessAggressiveAndZstd": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "LessAggressiveParkableString",
-                        "UseZstdForParkableStrings"
-                    ]
-                }
-            ]
-        }
-    ],
     "PartialPageZeroing": [
         {
             "platforms": [
@@ -18901,25 +17161,6 @@
             ]
         }
     ],
-    "PermissionsAIP92": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows",
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "PermissionsAIP92"
-                    ]
-                }
-            ]
-        }
-    ],
     "PermissionsAIv4": [
         {
             "platforms": [
@@ -19015,22 +17256,6 @@
             ]
         }
     ],
-    "PickerUI": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ModifierSplit",
-                        "Picker"
-                    ]
-                }
-            ]
-        }
-    ],
     "PinTimeoutMigration": [
         {
             "platforms": [
@@ -19094,21 +17319,6 @@
             ]
         }
     ],
-    "PlayReadyHardwareSecureDecryption": [
-        {
-            "platforms": [
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "HardwareSecureDecryption"
-                    ]
-                }
-            ]
-        }
-    ],
     "PlusAddressAcceptedFirstTimeCreateSurvey": [
         {
             "platforms": [
@@ -19339,30 +17549,6 @@
             ]
         }
     ],
-    "PompanoEnabled": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "EnabledeWithSummarizeSelected",
-                    "enable_features": [
-                        "MahiPanelResizable",
-                        "MahiSummarizeSelected",
-                        "Pompano"
-                    ]
-                },
-                {
-                    "name": "EnabledWithoutSummarizeSelected",
-                    "enable_features": [
-                        "MahiPanelResizable",
-                        "Pompano"
-                    ]
-                }
-            ]
-        }
-    ],
     "PostDelayedTaskFocusTab": [
         {
             "platforms": [
@@ -19550,24 +17736,6 @@
             ]
         }
     ],
-    "PrefetchProxyDesktop": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "PrefetchProxy"
-                    ]
-                }
-            ]
-        }
-    ],
     "PreloadTopChromeWebUILessNavigations": [
         {
             "platforms": [
@@ -19845,38 +18013,6 @@
             ]
         }
     ],
-    "PrivacySandboxAdsAPIs": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_Notice_M1_AllAPIs_Expanded_NoOT_Stable",
-                    "params": {
-                        "implementation_type": "mparch"
-                    },
-                    "enable_features": [
-                        "AllowURNsInIframes",
-                        "BrowsingTopics",
-                        "FencedFrames",
-                        "FencedFramesAPIChanges",
-                        "InterestGroupStorage",
-                        "KAnonymityService",
-                        "PrivacySandboxAdsAPIs",
-                        "PrivacySandboxAdsAPIsM1Override",
-                        "PrivateAggregationApi",
-                        "SharedStorageAPI"
-                    ]
-                }
-            ]
-        }
-    ],
     "PrivacySandboxInternalsDevUI": [
         {
             "platforms": [
@@ -20139,42 +18275,6 @@
             ]
         }
     ],
-    "ProfileRemoteCommands": [
-        {
-            "platforms": [
-                "android",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "UserRemoteCommands",
-                        "UserRemoteCommandsInvalidationWithDirectMessagesEnabled"
-                    ]
-                }
-            ]
-        }
-    ],
-    "ProfileSignalsReportingEnabled": [
-        {
-            "platforms": [
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ProfileSignalsReportingEnabled"
-                    ]
-                }
-            ]
-        }
-    ],
     "ProfilesReordering": [
         {
             "platforms": [
@@ -20212,38 +18312,6 @@
             ]
         }
     ],
-    "PropagateWebViewNetworkingSignals": [
-        {
-            "platforms": [
-                "android_webview"
-            ],
-            "experiments": [
-                {
-                    "name": "SignalAndConnecMigration_20230706",
-                    "enable_features": [
-                        "MigrateSessionsOnNetworkChangeV2",
-                        "webViewPropagateNetworkSignals"
-                    ]
-                },
-                {
-                    "name": "Control_20230706",
-                    "disable_features": [
-                        "MigrateSessionsOnNetworkChangeV2",
-                        "webViewPropagateNetworkSignals"
-                    ]
-                },
-                {
-                    "name": "SignalNoConnecMigration_20230706",
-                    "enable_features": [
-                        "webViewPropagateNetworkSignals"
-                    ],
-                    "disable_features": [
-                        "MigrateSessionsOnNetworkChangeV2"
-                    ]
-                }
-            ]
-        }
-    ],
     "ProtoBasedEnterpriseReporting": [
         {
             "platforms": [
@@ -20359,31 +18427,6 @@
             ]
         }
     ],
-    "QUIC": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "EnabledNoId",
-                    "params": {
-                        "channel": "D",
-                        "epoch": "20250423",
-                        "retransmittable_on_wire_timeout_milliseconds": "200"
-                    },
-                    "enable_features": [
-                        "QuicDoesNotUseFeatures"
-                    ]
-                }
-            ]
-        }
-    ],
     "QuicLongerIdleConnectionTimeout": [
         {
             "platforms": [
@@ -20404,45 +18447,6 @@
             ]
         }
     ],
-    "QuickShareV2": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_Dogfood",
-                    "enable_features": [
-                        "QuickShareV2"
-                    ]
-                },
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "QuickShareV2"
-                    ]
-                }
-            ]
-        }
-    ],
-    "RawDrawAndDrDc": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "DrDc_vulkan",
-                    "params": {
-                        "BlockListByDevice": "amber|chopin|secret|a03|SO-51B|on7xelte|j7xelte|F41B|doha|HWYAL|a20s|begonia|b2q|t2s|channel|galahad|rk322x_box|a32|ellis|dandelion|tonga|RMX3231"
-                    },
-                    "disable_features": [
-                        "RawDraw"
-                    ]
-                }
-            ]
-        }
-    ],
     "RazeOldHistoryDatabase": [
         {
             "platforms": [
@@ -20707,21 +18711,6 @@
             ]
         }
     ],
-    "ReduceCpuUtilization2": [
-        {
-            "platforms": [
-                "android_webview"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ReduceCpuUtilization2"
-                    ]
-                }
-            ]
-        }
-    ],
     "ReduceIPCCombined": [
         {
             "platforms": [
@@ -20777,21 +18766,6 @@
             ]
         }
     ],
-    "RegisterOsUpdateHandlerWin": [
-        {
-            "platforms": [
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "RegisterOsUpdateHandlerWin"
-                    ]
-                }
-            ]
-        }
-    ],
     "RejectWeakCiphertext": [
         {
             "platforms": [
@@ -20807,69 +18781,6 @@
             ]
         }
     ],
-    "RemotePageMetadataAndroid": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20240514",
-                    "params": {
-                        "supported_countries": "*",
-                        "supported_locales": "*"
-                    },
-                    "enable_features": [
-                        "PageContentAnnotationsPersistSalientImageMetadata",
-                        "RemotePageMetadata"
-                    ]
-                }
-            ]
-        }
-    ],
-    "RemotePageMetadataBling": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20240514",
-                    "params": {
-                        "supported_countries": "*",
-                        "supported_locales": "*"
-                    },
-                    "enable_features": [
-                        "PageContentAnnotationsPersistSalientImageMetadata",
-                        "RemotePageMetadata"
-                    ]
-                }
-            ]
-        }
-    ],
-    "RemotePageMetadataDesktopExpansion": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20240514",
-                    "params": {
-                        "supported_countries": "*",
-                        "supported_locales": "*"
-                    },
-                    "enable_features": [
-                        "PageContentAnnotationsPersistSalientImageMetadata",
-                        "RemotePageMetadata"
-                    ]
-                }
-            ]
-        }
-    ],
     "RemoveCachedProcessFromBindingManager": [
         {
             "platforms": [
@@ -20919,19 +18830,16 @@
             ]
         }
     ],
-    "RemoveRendererProcessLimit": [
+    "RemoveGreySnapshot": [
         {
             "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
+                "ios"
             ],
             "experiments": [
                 {
                     "name": "Enabled",
                     "enable_features": [
-                        "RemoveRendererProcessLimit"
+                        "RemoveGreySnapshot"
                     ]
                 }
             ]
@@ -21154,24 +19062,6 @@
             ]
         }
     ],
-    "ResolutionBasedDecoderPriority": [
-        {
-            "platforms": [
-                "linux",
-                "mac",
-                "windows",
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ResolutionBasedDecoderPriority"
-                    ]
-                }
-            ]
-        }
-    ],
     "RestrictAbusePorts": [
         {
             "platforms": [
@@ -21303,21 +19193,6 @@
             ]
         }
     ],
-    "RunDefaultStatusCheck": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "RunDefaultStatusCheck"
-                    ]
-                }
-            ]
-        }
-    ],
     "RustyBmpFeature": [
         {
             "platforms": [
@@ -21545,26 +19420,6 @@
             ]
         }
     ],
-    "SavedTabGroupUrlRestriction": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "EnableUrlRestriction"
-                    ]
-                }
-            ]
-        }
-    ],
     "ScaleScrollbarAnimationTiming": [
         {
             "platforms": [
@@ -21708,51 +19563,6 @@
             ]
         }
     ],
-    "SchoolToolsSpotlight": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "BocaSpotlight"
-                    ]
-                }
-            ]
-        }
-    ],
-    "SchoolToolsSpotlightRobotRequester": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "BocaSpotlightRobotRequester"
-                    ]
-                }
-            ]
-        }
-    ],
-    "ScreenCaptureKitMacScreen": [
-        {
-            "platforms": [
-                "mac"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ScreenCaptureKitMacScreen"
-                    ]
-                }
-            ]
-        }
-    ],
     "ScreencastForceEnableServerSideSpeechRecognition": [
         {
             "platforms": [
@@ -21865,57 +19675,6 @@
             ]
         }
     ],
-    "SeaPenQueryRewrite": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SeaPenQueryRewrite"
-                    ]
-                }
-            ]
-        }
-    ],
-    "SeaPenTextInput": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SeaPenTextInput"
-                    ]
-                }
-            ]
-        }
-    ],
-    "SeaPenTextInputI18n": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SeaPenTextInputTranslation"
-                    ]
-                },
-                {
-                    "name": "Enabled_Dogfood",
-                    "enable_features": [
-                        "SeaPenTextInputTranslation"
-                    ]
-                }
-            ]
-        }
-    ],
     "SeaPenUseExptTemplate": [
         {
             "platforms": [
@@ -21931,26 +19690,6 @@
             ]
         }
     ],
-    "SeamlessRenderFrameSwap": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SeamlessRenderFrameSwap"
-                    ]
-                }
-            ]
-        }
-    ],
     "SeamlessSigninClank": [
         {
             "platforms": [
@@ -22168,21 +19907,6 @@
             ]
         }
     ],
-    "SelectFileOpenDocument": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SelectFileOpenDocument"
-                    ]
-                }
-            ]
-        }
-    ],
     "SelectivePermissionsIntervention": [
         {
             "platforms": [
@@ -22626,21 +20350,6 @@
             ]
         }
     ],
-    "ShareDefaultBrowserStatus": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "ShareDefaultBrowserStatus"
-                    ]
-                }
-            ]
-        }
-    ],
     "SharedHighlightingIphClank": [
         {
             "platforms": [
@@ -22750,24 +20459,6 @@
             ]
         }
     ],
-    "SharedWorkerBlobURLFix": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SharedWorkerBlobURLFix"
-                    ]
-                }
-            ]
-        }
-    ],
     "SharingHubDesktopScreenshots": [
         {
             "platforms": [
@@ -22856,80 +20547,6 @@
             ]
         }
     ],
-    "SidePanelCompanionDesktopM116Plus": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "EnableCompanionChromeOS_20240222",
-                    "params": {
-                        "open-companion-for-image-search": "false",
-                        "open-companion-for-web-search": "false",
-                        "open-contextual-lens-panel": "false",
-                        "open-links-in-current-tab": "false"
-                    },
-                    "enable_features": [
-                        "SidePanelCompanion",
-                        "SidePanelCompanionChromeOS",
-                        "VisualQuerySuggestions"
-                    ],
-                    "disable_features": [
-                        "SideSearch"
-                    ]
-                }
-            ]
-        }
-    ],
-    "SidePanelFlyoverAnimation": [
-        {
-            "platforms": [
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SidePanelFlyoverAnimation"
-                    ]
-                }
-            ]
-        }
-    ],
-    "SidePanelPinningWithResponsiveToolbar": [
-        {
-            "platforms": [
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "EnabledWithSidePanelPinning",
-                    "enable_features": [
-                        "IPH_SidePanelGenericPinnableFeature",
-                        "ResponsiveToolbar",
-                        "SidePanelPinning"
-                    ]
-                },
-                {
-                    "name": "EnabledWithoutSidePanelPinning",
-                    "enable_features": [
-                        "ResponsiveToolbar"
-                    ]
-                }
-            ]
-        }
-    ],
     "SigninPromoLimitsExperiment": [
         {
             "platforms": [
@@ -23091,83 +20708,6 @@
             ]
         }
     ],
-    "SimpleCachePrioritizedCaching": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "linux",
-                "mac"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SimpleCachePrioritizedCaching"
-                    ]
-                }
-            ]
-        }
-    ],
-    "SingleCaCertVerificationPhase1": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SingleCaCertVerificationPhase1"
-                    ]
-                }
-            ]
-        }
-    ],
-    "SingleCaCertVerificationPhase2": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SingleCaCertVerificationPhase2"
-                    ]
-                }
-            ]
-        }
-    ],
-    "SingleNTPRemoveExtraNTPs": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_SingleNTP_RemoveExtraNTP",
-                    "enable_features": [
-                        "RemoveExcessNTPs",
-                        "SingleNTP"
-                    ]
-                },
-                {
-                    "name": "Enabled_SingleNTP",
-                    "enable_features": [
-                        "SingleNTP"
-                    ]
-                },
-                {
-                    "name": "Enabled_RemoveExtraNTP",
-                    "enable_features": [
-                        "RemoveExcessNTPs"
-                    ]
-                }
-            ]
-        }
-    ],
     "SingleVideoFrameRateThrottling": [
         {
             "platforms": [
@@ -23187,27 +20727,6 @@
             ]
         }
     ],
-    "SiteInstanceGroupsForDataUrls": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "SiteInstanceGroupsForDataUrls"
-                    ]
-                }
-            ]
-        }
-    ],
     "SkiaGraphite": [
         {
             "platforms": [
@@ -23377,36 +20896,6 @@
             ]
         }
     ],
-    "SkyVaultGA": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_SkyVaultV2",
-                    "enable_features": [
-                        "SkyVaultV2"
-                    ]
-                }
-            ]
-        }
-    ],
-    "SkyVaultTTBeta": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_SkyVault",
-                    "enable_features": [
-                        "SkyVault"
-                    ]
-                }
-            ]
-        }
-    ],
     "SlopBucket": [
         {
             "platforms": [
@@ -23754,24 +21243,6 @@
             ]
         }
     ],
-    "StringWidthCache": [
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "StringWidthCache"
-                    ]
-                }
-            ]
-        }
-    ],
     "StructuredCloningForMessaging": [
         {
             "platforms": [
@@ -24038,63 +21509,6 @@
             ]
         }
     ],
-    "SysUiHoldbackStudy": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Default",
-                    "disable_features": [
-                        "SysUiShouldHoldbackGifRecording",
-                        "SysUiShouldHoldbackTaskManagement"
-                    ]
-                },
-                {
-                    "name": "SysUiHoldbackStudy",
-                    "enable_features": [
-                        "SysUiShouldHoldbackGifRecording",
-                        "SysUiShouldHoldbackTaskManagement"
-                    ]
-                },
-                {
-                    "name": "NoFirstPartyIntegrations",
-                    "enable_features": [
-                        "SysUiShouldHoldbackTaskManagement"
-                    ],
-                    "disable_features": [
-                        "SysUiShouldHoldbackGifRecording"
-                    ]
-                }
-            ]
-        }
-    ],
-    "SysUiHoldbackStudyM129": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Default",
-                    "disable_features": [
-                        "SysUiShouldHoldbackDriveIntegration",
-                        "SysUiShouldHoldbackFocusMode",
-                        "SysUiShouldHoldbackForest"
-                    ]
-                },
-                {
-                    "name": "EnrolledInHoldback",
-                    "enable_features": [
-                        "SysUiShouldHoldbackDriveIntegration",
-                        "SysUiShouldHoldbackFocusMode",
-                        "SysUiShouldHoldbackForest"
-                    ]
-                }
-            ]
-        }
-    ],
     "SystemEmojiPickerGIFSupportByDefault": [
         {
             "platforms": [
@@ -24153,32 +21567,6 @@
             ]
         }
     ],
-    "TabAudioMuting": [
-        {
-            "platforms": [
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "availability": "any",
-                        "event_trigger": "name:tab_audio_muting_iph_triggered;comparator:==0;window:120;storage:365",
-                        "event_used": "name:tab_audio_muting_toggle_viewed;comparator:==0;window:120;storage:365",
-                        "session_rate": "==0"
-                    },
-                    "enable_features": [
-                        "IPH_TabAudioMuting",
-                        "TabAudioMuting"
-                    ]
-                }
-            ]
-        }
-    ],
     "TabGroupInteractionsDesktop": [
         {
             "platforms": [
@@ -24262,46 +21650,6 @@
             ]
         }
     ],
-    "TabModelInitFixes": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "TabModelInitFixes"
-                    ]
-                }
-            ]
-        }
-    ],
-    "TabSearchInProductHelp": [
-        {
-            "platforms": [
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "TabSearchIPH",
-                    "params": {
-                        "availability": "any",
-                        "event_trigger": "name:tab_search_iph_tgr;comparator:==0;window:90;storage:360",
-                        "event_used": "name:tab_search_opened;comparator:==0;window:90;storage:360",
-                        "session_rate": "<3"
-                    },
-                    "enable_features": [
-                        "IPH_TabSearch"
-                    ]
-                }
-            ]
-        }
-    ],
     "TabStorageSqlitePrototype": [
         {
             "platforms": [
@@ -24336,40 +21684,6 @@
             ]
         }
     ],
-    "TailoredSecurityIntegration": [
-        {
-            "platforms": [
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "TailoredSecurityIntegration",
-                    "enable_features": [
-                        "TailoredSecurityIntegration"
-                    ]
-                }
-            ]
-        }
-    ],
-    "TailoredSecurityIntegrationAndroid": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20220302",
-                    "enable_features": [
-                        "TailoredSecurityIntegration"
-                    ]
-                }
-            ]
-        }
-    ],
     "TaiyakiAllUsers": [
         {
             "platforms": [
@@ -24823,29 +22137,6 @@
             ]
         }
     ],
-    "TrustTokenOriginTrial": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "TrustTokenOperationsRequiringOriginTrial": "all-operations-require-origin-trial"
-                    },
-                    "enable_features": [
-                        "TrustTokens"
-                    ]
-                }
-            ]
-        }
-    ],
     "UMA-Pseudo-Metrics-Effect-Injection-25-Percent": [
         {
             "platforms": [
@@ -25079,21 +22370,6 @@
             ]
         }
     ],
-    "UploadOfficeToCloud": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "UploadOfficeToCloud"
-                    ]
-                }
-            ]
-        }
-    ],
     "UseBoringSSLForRandBytes": [
         {
             "platforms": [
@@ -25180,21 +22456,6 @@
             ]
         }
     ],
-    "UseEncryptedReportingPipelineToReportArcAppInstallEvents": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "UseEncryptedReportingPipelineToReportArcAppInstallEvents"
-                    ]
-                }
-            ]
-        }
-    ],
     "UseHeuristicForFindingEditor": [
         {
             "platforms": [
@@ -25421,153 +22682,6 @@
             ]
         }
     ],
-    "V8BytecodeVerification": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "V8Flag_verify_bytecode_light"
-                    ]
-                }
-            ]
-        }
-    ],
-    "V8CodeFlushing": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Time180",
-                    "params": {
-                        "bytecode_old_time": "180"
-                    },
-                    "enable_features": [
-                        "V8Flag_flush_code_based_on_time",
-                        "V8Flag_ineffective_gcs_forces_last_resort"
-                    ]
-                }
-            ]
-        }
-    ],
-    "V8DisableEagerAllocationFailures": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Disabled",
-                    "disable_features": [
-                        "V8Flag_enable_allocation_failures_optimize_memory",
-                        "V8Flag_enable_allocation_failures_optimize_memory_ignoring_priority"
-                    ]
-                }
-            ]
-        }
-    ],
-    "V8DisableScavengerUpdatesAllocationLimit": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "disable_features": [
-                        "V8Flag_scavenger_updates_allocation_limit"
-                    ]
-                }
-            ]
-        }
-    ],
-    "V8EnforceGlobalHeapLimit": [
-        {
-            "platforms": [
-                "android",
-                "android_webview"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "maximum_global_heap_limit_factor": "2"
-                    },
-                    "enable_features": [
-                        "V8Flag_enforce_global_heap_limit"
-                    ]
-                }
-            ]
-        },
-        {
-            "platforms": [
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "maximum_global_heap_limit_factor": "8"
-                    },
-                    "enable_features": [
-                        "V8Flag_enforce_global_heap_limit"
-                    ]
-                }
-            ]
-        }
-    ],
-    "V8ExternalMemoryAccountedInGlobalLimit": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "external_memory_max_growing_factor": "1.3"
-                    },
-                    "enable_features": [
-                        "V8Flag_external_memory_accounted_in_global_limit"
-                    ]
-                }
-            ]
-        }
-    ],
     "V8HighEndAndroid": [
         {
             "platforms": [
@@ -25587,71 +22701,6 @@
             ]
         }
     ],
-    "V8IneffectiveMarkCompact": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "HighSizeThreshold",
-                    "params": {
-                        "ineffective_gc_size_threshold": "0.95"
-                    },
-                    "enable_features": [
-                        "V8Flag_detect_ineffective_gcs_near_heap_limit"
-                    ]
-                }
-            ]
-        }
-    ],
-    "V8LargePagePool": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "V8Flag_large_page_pool"
-                    ]
-                }
-            ]
-        }
-    ],
-    "V8LateHeapLimitCheck": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "V8Flag_late_heap_limit_check"
-                    ]
-                }
-            ]
-        }
-    ],
     "V8ManagedZoneMemory": [
         {
             "platforms": [
@@ -25736,43 +22785,6 @@
             ]
         }
     ],
-    "V8NoTrimDescriptorArray": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "NoTrimWithStack",
-                    "enable_features": [
-                        "V8Flag_trim_descriptor_arrays_in_gc"
-                    ],
-                    "disable_features": [
-                        "V8Flag_trim_descriptor_arrays_in_gc_with_stack"
-                    ]
-                },
-                {
-                    "name": "NoTrim",
-                    "disable_features": [
-                        "V8Flag_trim_descriptor_arrays_in_gc",
-                        "V8Flag_trim_descriptor_arrays_in_gc_with_stack"
-                    ]
-                },
-                {
-                    "name": "Default",
-                    "enable_features": [
-                        "V8Flag_trim_descriptor_arrays_in_gc",
-                        "V8Flag_trim_descriptor_arrays_in_gc_with_stack"
-                    ]
-                }
-            ]
-        }
-    ],
     "V8ParserAblation": [
         {
             "platforms": [
@@ -26161,21 +23173,6 @@
             ]
         }
     ],
-    "VideoConferenceDLCUIRollout": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "VcDlcUi"
-                    ]
-                }
-            ]
-        }
-    ],
     "VideoConferenceRollout": [
         {
             "platforms": [
@@ -26260,21 +23257,6 @@
             ]
         }
     ],
-    "VulkanV2": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "VulkanV2",
-                    "enable_features": [
-                        "VulkanV2"
-                    ]
-                }
-            ]
-        }
-    ],
     "WebApkBackupAndRestore": [
         {
             "platforms": [
@@ -26465,25 +23447,6 @@
             ]
         }
     ],
-    "WebProtectDlpScanPastedImages": [
-        {
-            "platforms": [
-                "chromeos",
-                "fuchsia",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "DlpScanPastedImages"
-                    ]
-                }
-            ]
-        }
-    ],
     "WebRTC-Aec3BufferingMaxAllowedExcessRenderBlocksOverride": [
         {
             "platforms": [
@@ -26711,26 +23674,6 @@
             ]
         }
     ],
-    "WebRTC-DataChannelMessageInterleaving": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "WebRTC-DataChannelMessageInterleaving"
-                    ]
-                }
-            ]
-        }
-    ],
     "WebRTC-EncoderSpeed": [
         {
             "platforms": [
@@ -26975,49 +23918,6 @@
             ]
         }
     ],
-    "WebRTC-Video-H26xPacketBuffer": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "WebRTC-Video-H26xPacketBuffer"
-                    ]
-                }
-            ]
-        }
-    ],
-    "WebRTC-Video-ReceiveAndSendH265": [
-        {
-            "platforms": [
-                "android",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_SendReceive",
-                    "enable_features": [
-                        "WebRtcAllowH265Receive",
-                        "WebRtcAllowH265Send"
-                    ]
-                },
-                {
-                    "name": "Enabled_ReceiveOnly",
-                    "enable_features": [
-                        "WebRtcAllowH265Receive"
-                    ]
-                }
-            ]
-        }
-    ],
     "WebRTC-Vp9ExternalRefCtrl": [
         {
             "platforms": [
@@ -27722,21 +24622,6 @@
             ]
         }
     ],
-    "WebXrEnableCardboard": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "Cardboard"
-                    ]
-                }
-            ]
-        }
-    ],
     "WebrtcEncodeReadbackOptimization": [
         {
             "platforms": [
@@ -27915,21 +24800,6 @@
             ]
         }
     ],
-    "WindowsSystemTracing": [
-        {
-            "platforms": [
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "WindowsSystemTracing"
-                    ]
-                }
-            ]
-        }
-    ],
     "XMLParsingRustNonXslt": [
         {
             "platforms": [
@@ -28089,26 +24959,6 @@
             ]
         }
     ],
-    "ZeroStateSuggestionsV2": [
-        {
-            "platforms": [
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "ZSSMaxPinnedPagesForTriggeringSuggestions": "10"
-                    },
-                    "enable_features": [
-                        "ZeroStateSuggestionsV2"
-                    ]
-                }
-            ]
-        }
-    ],
     "ZstdForCrossSiteSpeculationRulesPrefetch": [
         {
             "platforms": [
diff --git a/third_party/blink/renderer/core/html/forms/form_mcp_schema.cc b/third_party/blink/renderer/core/html/forms/form_mcp_schema.cc
index 293a717..d541730 100644
--- a/third_party/blink/renderer/core/html/forms/form_mcp_schema.cc
+++ b/third_party/blink/renderer/core/html/forms/form_mcp_schema.cc
@@ -889,10 +889,12 @@
   }
   if (auto* input = DynamicTo<HTMLInputElement>(
           controls_for_name.front()->ToHTMLElement())) {
-    input->SetValue(string);
+    input->SetValue(string,
+                    TextFieldEventBehavior::kDispatchInputAndChangeEvent);
   } else if (auto* textarea = DynamicTo<HTMLTextAreaElement>(
                  controls_for_name.front()->ToHTMLElement())) {
-    textarea->SetValue(string);
+    textarea->SetValue(string,
+                       TextFieldEventBehavior::kDispatchInputAndChangeEvent);
   }
 }
 
@@ -903,7 +905,8 @@
     String number_string;
     bool success = ToString(value, number_string);
     CHECK(success) << "ValidateNumberData should be called first";
-    input->SetValue(number_string);
+    input->SetValue(number_string,
+                    TextFieldEventBehavior::kDispatchInputAndChangeEvent);
   }
 }
 
@@ -912,8 +915,13 @@
   if (controls_for_name.size() == 1u) {
     bool checked;
     CHECK(ToBoolean(value, checked));
-    To<HTMLInputElement>(controls_for_name.front()->ToHTMLElement())
-        .SetChecked(checked);
+    HTMLInputElement& input =
+        To<HTMLInputElement>(controls_for_name.front()->ToHTMLElement());
+    input.SetChecked(checked,
+                     TextFieldEventBehavior::kDispatchInputAndChangeEvent);
+    // SetChecked only dispatches `input`. Usually, `change` is dispatched by
+    // CheckboxInputType::RunInputActivationBehavior.
+    input.DispatchChangeEvent();
     return;
   }
 
@@ -935,7 +943,11 @@
   // Check (or uncheck) each value.
   for (ListedElement* control : controls_for_name) {
     HTMLInputElement& input = To<HTMLInputElement>(control->ToHTMLElement());
-    input.SetChecked(checked_values.Contains(input.Value()));
+    input.SetChecked(checked_values.Contains(input.Value()),
+                     TextFieldEventBehavior::kDispatchInputAndChangeEvent);
+    // SetChecked only dispatches `input`. Usually, `change` is dispatched by
+    // CheckboxInputType::RunInputActivationBehavior.
+    input.DispatchChangeEvent();
   }
 }
 
@@ -948,7 +960,11 @@
   for (ListedElement* control : controls_for_name) {
     HTMLInputElement& input = To<HTMLInputElement>(control->ToHTMLElement());
     if (input.Value() == string) {
-      input.SetChecked(true, TextFieldEventBehavior::kDispatchChangeEvent);
+      input.SetChecked(true,
+                       TextFieldEventBehavior::kDispatchInputAndChangeEvent);
+      // SetChecked only dispatches `input`. Usually, `change` is dispatched by
+      // RadioInputType::RunInputActivationBehavior.
+      input.DispatchChangeEvent();
     }
   }
 }
diff --git a/third_party/blink/renderer/core/html/html_tag_names.json5 b/third_party/blink/renderer/core/html/html_tag_names.json5
index 6ebddbe..beeee6f 100644
--- a/third_party/blink/renderer/core/html/html_tag_names.json5
+++ b/third_party/blink/renderer/core/html/html_tag_names.json5
@@ -120,6 +120,8 @@
       name: "colgroup",
       interfaceName: "HTMLTableColElement",
     },
+    // TODO(crbug.com/491158089): This should be removed in M150, once the
+    // HTMLCommandElementRemoval flag was removed.
     {
       name: "command",
       interfaceName: "HTMLUnknownElement",
diff --git a/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc b/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc
index d7af6f7..20bceda4 100644
--- a/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc
@@ -340,11 +340,10 @@
                 MakeGarbageCollected<ConsoleMessage>(
                     mojom::ConsoleMessageSource::kOther,
                     mojom::ConsoleMessageLevel::kWarning,
-                    StrCat(
-                        {"Dropped srcset candidate ",
-                         JSONValue::QuoteString(String(attribute_span.subspan(
-                             image_url_start,
-                             image_url_end - image_url_start)))})));
+                    StrCat({"Dropped srcset candidate ",
+                            JSONValue::QuoteString(attribute.subview(
+                                image_url_start,
+                                image_url_end - image_url_start))})));
           }
         }
         continue;
diff --git a/third_party/blink/renderer/core/html/parser/html_stack_item.h b/third_party/blink/renderer/core/html/parser/html_stack_item.h
index d884b20e..9f2927a5 100644
--- a/third_party/blink/renderer/core/html/parser/html_stack_item.h
+++ b/third_party/blink/renderer/core/html/parser/html_stack_item.h
@@ -229,7 +229,6 @@
         case html_names::HTMLTag::kCenter:
         case html_names::HTMLTag::kCol:
         case html_names::HTMLTag::kColgroup:
-        case html_names::HTMLTag::kCommand:
         case html_names::HTMLTag::kDd:
         case html_names::HTMLTag::kDetails:
         case html_names::HTMLTag::kDir:
@@ -294,6 +293,8 @@
         case html_names::HTMLTag::kWbr:
         case html_names::HTMLTag::kXmp:
           return true;
+        case html_names::HTMLTag::kCommand:
+          return !RuntimeEnabledFeatures::HTMLCommandElementRemovalEnabled();
         default:
           return false;
       }
diff --git a/third_party/blink/renderer/core/html/parser/html_tree_builder.cc b/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
index 827f1815..c90b5957 100644
--- a/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
+++ b/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
@@ -693,7 +693,6 @@
     case HTMLTag::kBase:
     case HTMLTag::kBasefont:
     case HTMLTag::kBgsound:
-    case HTMLTag::kCommand:
     case HTMLTag::kLink:
     case HTMLTag::kMeta:
     case HTMLTag::kNoframes:
@@ -1081,6 +1080,13 @@
     case HTMLTag::kTr:
       ParseError(token);
       break;
+    case HTMLTag::kCommand:
+      if (!RuntimeEnabledFeatures::HTMLCommandElementRemovalEnabled()) {
+        bool did_process = ProcessStartTagForInHead(token);
+        DCHECK(did_process);
+        break;
+      }
+      [[fallthrough]];
     default:
       if (token->GetName() == mathml_names::kMathTag.LocalName()) {
         tree_.ReconstructTheActiveFormattingElements();
@@ -2733,13 +2739,18 @@
     case HTMLTag::kBase:
     case HTMLTag::kBasefont:
     case HTMLTag::kBgsound:
-    case HTMLTag::kCommand:
     case HTMLTag::kLink:
     case HTMLTag::kMeta:
       tree_.InsertSelfClosingHTMLElementDestroyingToken(token);
       // Note: The custom processing for the <meta> tag is done in
       // HTMLMetaElement::process().
       return true;
+    case html_names::HTMLTag::kCommand:
+      if (RuntimeEnabledFeatures::HTMLCommandElementRemovalEnabled()) {
+        return false;
+      }
+      tree_.InsertSelfClosingHTMLElementDestroyingToken(token);
+      return true;
     case HTMLTag::kTitle:
       ProcessGenericRCDATAStartTag(token);
       return true;
diff --git a/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc b/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
index 2f7e705..8dff9474 100644
--- a/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
@@ -1023,10 +1023,9 @@
 InspectorStyle::LonghandProperties(
     const CSSPropertySourceData& property_entry) {
   DCHECK(style_);
-  String property_value = property_entry.value;
+  StringView property_value = property_entry.value;
   if (property_entry.important) {
-    property_value = property_value.substr(
-        0, property_value.length() - 10 /* length of "!important" */);
+    property_value.remove_suffix(10 /* length of "!important" */);
   }
   CSSParserTokenStream stream(property_value);
   stream.EnsureLookAhead();  // Several parsers expect this.
diff --git a/third_party/blink/renderer/core/layout/box_fragment_builder.h b/third_party/blink/renderer/core/layout/box_fragment_builder.h
index 52415f7..4f528e63 100644
--- a/third_party/blink/renderer/core/layout/box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/box_fragment_builder.h
@@ -690,9 +690,8 @@
     table_section_row_offsets_ = std::move(row_offsets);
   }
 
-  void TransferGridLayoutData(
-      std::unique_ptr<GridLayoutData> grid_layout_data) {
-    grid_layout_data_ = std::move(grid_layout_data);
+  void SetGridLayoutData(const GridLayoutData* grid_layout_data) {
+    grid_layout_data_ = grid_layout_data;
   }
   void TransferFlexLayoutData(
       std::unique_ptr<DevtoolsFlexInfo> flex_layout_data) {
@@ -707,7 +706,7 @@
 
   const GridLayoutData& GetGridLayoutData() const {
     DCHECK(grid_layout_data_);
-    return *grid_layout_data_.get();
+    return *grid_layout_data_;
   }
 
   BreakTokenAlgorithmData* GetBreakTokenData() { return break_token_data_; }
@@ -854,9 +853,7 @@
   Vector<LayoutUnit> table_section_row_offsets_;
 
   BreakTokenAlgorithmData* break_token_data_ = nullptr;
-
-  // Grid specific types.
-  std::unique_ptr<GridLayoutData> grid_layout_data_;
+  const GridLayoutData* grid_layout_data_ = nullptr;
 
   std::unique_ptr<DevtoolsFlexInfo> flex_layout_data_;
   std::unique_ptr<FrameSetLayoutData> frame_set_layout_data_;
diff --git a/third_party/blink/renderer/core/layout/grid/grid_data.h b/third_party/blink/renderer/core/layout/grid/grid_data.h
index 05c091de..e48f18a 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_data.h
+++ b/third_party/blink/renderer/core/layout/grid/grid_data.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_GRID_GRID_DATA_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_GRID_GRID_DATA_H_
 
+#include "base/memory/values_equivalent.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/grid/grid_line_resolver.h"
 #include "third_party/blink/renderer/core/layout/grid/grid_subtree.h"
@@ -60,42 +61,40 @@
   wtf_size_t row_start_offset{0};
 };
 
-namespace {
-
-bool AreEqual(const std::unique_ptr<GridLayoutTrackCollection>& lhs,
-              const std::unique_ptr<GridLayoutTrackCollection>& rhs) {
-  return (lhs && rhs) ? *lhs == *rhs : !lhs && !rhs;
-}
-
-}  // namespace
-
 // This struct contains the column and row data necessary to layout grid items.
 // For grid sizing, it will store |GridSizingTrackCollection| pointers, which
 // are able to modify the geometry of its sets. However, after sizing is done,
 // it should only copy |GridLayoutTrackCollection| immutable data.
-class CORE_EXPORT GridLayoutData {
-  USING_FAST_MALLOC(GridLayoutData);
-
+class CORE_EXPORT GridLayoutData : public GarbageCollected<GridLayoutData> {
  public:
   GridLayoutData() = default;
   GridLayoutData(GridLayoutData&&) = default;
   GridLayoutData& operator=(GridLayoutData&&) = default;
 
   GridLayoutData(const GridLayoutData& other) {
+    // Do a deep copy of the track collections if they have baselines, since
+    // they may be mutated per-subgrid with baseline alignment; otherwise,
+    // shallow copy is sufficient.
     if (other.columns_) {
-      columns_ = std::make_unique<GridLayoutTrackCollection>(other.Columns());
+      if (other.columns_->HasBaselines()) {
+        columns_ =
+            MakeGarbageCollected<GridLayoutTrackCollection>(other.Columns());
+      } else {
+        columns_ = other.columns_;
+      }
     }
     if (other.rows_) {
-      rows_ = std::make_unique<GridLayoutTrackCollection>(other.Rows());
+      if (other.rows_->HasBaselines()) {
+        rows_ = MakeGarbageCollected<GridLayoutTrackCollection>(other.Rows());
+      } else {
+        rows_ = other.rows_;
+      }
     }
   }
 
-  GridLayoutData& operator=(const GridLayoutData& other) {
-    return *this = GridLayoutData(other);
-  }
-
   bool operator==(const GridLayoutData& other) const {
-    return AreEqual(columns_, other.columns_) && AreEqual(rows_, other.rows_);
+    return base::ValuesEquivalent(columns_, other.columns_) &&
+           base::ValuesEquivalent(rows_, other.rows_);
   }
 
   bool HasSubgriddedAxis(GridTrackSizingDirection track_direction) const {
@@ -134,21 +133,20 @@
 
   // This method is intended for subgrids with both a standalone and a
   // subgridded axis. Returns the only subgridded track collection.
-  const GridLayoutTrackCollection& OnlySubgriddedCollection() const {
+  const GridLayoutTrackCollection* OnlySubgriddedCollection() const {
     DCHECK(columns_);
     DCHECK(rows_);
     DCHECK_NE(columns_->IsForSizing(), rows_->IsForSizing());
-    return columns_->IsForSizing() ? *rows_ : *columns_;
+    return columns_->IsForSizing() ? rows_.Get() : columns_.Get();
   }
 
-  void SetTrackCollection(
-      std::unique_ptr<GridLayoutTrackCollection> track_collection) {
+  void SetTrackCollection(GridLayoutTrackCollection* track_collection) {
     DCHECK(track_collection);
 
     if (track_collection->Direction() == kForColumns) {
-      columns_ = std::move(track_collection);
+      columns_ = track_collection;
     } else {
-      rows_ = std::move(track_collection);
+      rows_ = track_collection;
     }
   }
 
@@ -160,10 +158,14 @@
            (rows_ && Rows().HasIndefiniteSet());
   }
 
+  void Trace(Visitor* visitor) const {
+    visitor->Trace(columns_);
+    visitor->Trace(rows_);
+  }
+
  private:
-  // TODO(7154891): Oilpanify GridLayoutTrackCollection.
-  std::unique_ptr<GridLayoutTrackCollection> columns_;
-  std::unique_ptr<GridLayoutTrackCollection> rows_;
+  Member<GridLayoutTrackCollection> columns_;
+  Member<GridLayoutTrackCollection> rows_;
 };
 
 // Subgrid layout relies on the root grid to perform the track sizing algorithm
@@ -178,15 +180,15 @@
 class GridLayoutTree : public GarbageCollected<GridLayoutTree> {
  public:
   struct GridTreeNode : public GarbageCollected<GridTreeNode> {
-    GridTreeNode(const GridLayoutData& layout_data, wtf_size_t subtree_size)
-        : has_unresolved_geometry(layout_data.HasIndefiniteSet()),
+    GridTreeNode(const GridLayoutData* layout_data, wtf_size_t subtree_size)
+        : has_unresolved_geometry(layout_data->HasIndefiniteSet()),
           layout_data(layout_data),
           subtree_size(subtree_size) {}
 
-    void Trace(Visitor* visitor) const {}
+    void Trace(Visitor* visitor) const { visitor->Trace(layout_data); }
+
     bool has_unresolved_geometry;
-    // TODO(7154891): Oilpanify GridLayoutData.
-    GridLayoutData layout_data;
+    Member<const GridLayoutData> layout_data;
     wtf_size_t subtree_size;
   };
 
@@ -203,8 +205,8 @@
     }
 
     for (wtf_size_t i = 0; i < subtree_size; ++i) {
-      if (other.LayoutData(other_subtree_root + i) !=
-          LayoutData(subtree_root + i)) {
+      if (*other.LayoutData(other_subtree_root + i) !=
+          *LayoutData(subtree_root + i)) {
         return false;
       }
     }
@@ -216,7 +218,7 @@
     return tree_data_[index]->has_unresolved_geometry;
   }
 
-  const GridLayoutData& LayoutData(wtf_size_t index) const {
+  const GridLayoutData* LayoutData(wtf_size_t index) const {
     DCHECK_LT(index, tree_data_.size());
     return tree_data_[index]->layout_data;
   }
@@ -274,12 +276,13 @@
     return LayoutTree().HasUnresolvedGeometry(subtree_root_);
   }
 
-  const GridLayoutData& LayoutData() const {
+  const GridLayoutData* LayoutData() const {
     return LayoutTree().LayoutData(subtree_root_);
   }
 
   void Trace(Visitor* visitor) const { visitor->Trace(layout_tree_); }
 
+ private:
   const GridLayoutTree& LayoutTree() const {
     DCHECK(layout_tree_);
     return *layout_tree_;
diff --git a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc
index aadd1f5..dfa1e03 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc
@@ -95,7 +95,7 @@
         ComputeGridGeometry(&grid_items, &intrinsic_block_size, &oof_children);
   }
 
-  const auto& layout_data = layout_subtree->LayoutData();
+  const auto* layout_data = layout_subtree->LayoutData();
   LayoutUnit offset_in_stitched_container;
   LayoutUnit previous_offset_in_stitched_container;
   Vector<GridItemPlacementData> grid_items_placement_data;
@@ -131,12 +131,12 @@
       first_unprocessed_row_gap_idx = grid_data->first_unprocessed_row_gap_idx;
     } else {
       row_offset_adjustments =
-          Vector<LayoutUnit>(layout_data.Rows().GetSetCount() + 1);
+          Vector<LayoutUnit>(layout_data->Rows().GetSetCount() + 1);
       // `EndLineOfImplicitGrid()` is equivalent to the total track count.
       // TODO(samomekarajr): Add this and number of gaps to the
       // `GridTrackCollection` API.
       const wtf_size_t total_column_track_count =
-          layout_data.Columns().EndLineOfImplicitGrid();
+          layout_data->Columns().EndLineOfImplicitGrid();
       if (total_column_track_count > 1) {
         column_gaps_segment_ranges_start_indices =
             Vector<wtf_size_t>(total_column_track_count - 1, 0);
@@ -168,11 +168,11 @@
   // are the size of the grid, and *not* where the inflow grid items are placed.
   // Explicitly set the inflow-bounds to the grid size.
   if (node.IsScrollContainer()) {
-    LogicalOffset offset = {layout_data.Columns().GetSetOffset(0),
-                            layout_data.Rows().GetSetOffset(0)};
+    LogicalOffset offset = {layout_data->Columns().GetSetOffset(0),
+                            layout_data->Rows().GetSetOffset(0)};
 
-    LogicalSize size = {layout_data.Columns().CalculateSetSpanSize(),
-                        layout_data.Rows().CalculateSetSpanSize()};
+    LogicalSize size = {layout_data->Columns().CalculateSetSpanSize(),
+                        layout_data->Rows().CalculateSetSpanSize()};
 
     container_builder_.SetInflowBounds(LogicalRect(offset, size));
   }
@@ -230,12 +230,12 @@
     container_builder_.SetPreviousBreakAfter(row_break_between.back());
   }
 
-  if (!oof_children.empty())
-    PlaceOutOfFlowItems(layout_data, block_size, oof_children);
+  if (!oof_children.empty()) {
+    PlaceOutOfFlowItems(*layout_data, block_size, oof_children);
+  }
 
-  // Copy grid layout data for use in computed style and devtools.
-  container_builder_.TransferGridLayoutData(
-      std::make_unique<GridLayoutData>(layout_data));
+  // Store grid layout data for use in computed style and devtools.
+  container_builder_.SetGridLayoutData(layout_data);
 
   SetReadingFlowNodes(grid_items);
 
@@ -272,7 +272,7 @@
   if (const auto* layout_subtree =
           GetConstraintSpace().GetGridLayoutSubtree()) {
     return FixedMinMaxSizes(
-        layout_subtree->LayoutData().Columns().CalculateSetSpanSize());
+        layout_subtree->LayoutData()->Columns().CalculateSetSpanSize());
   }
 
   // If we have inline size containment ignore all children.
@@ -374,7 +374,7 @@
   // subgrid whose geometry is already computed. We can exit early by simply
   // copying the layout data and constructing our grid items.
   if (const auto* layout_subtree = constraint_space.GetGridLayoutSubtree()) {
-    const auto& layout_data = layout_subtree->LayoutData();
+    const auto* layout_data = layout_subtree->LayoutData();
 
     if (!node.ChildLayoutBlockedByDisplayLock()) {
       bool must_invalidate_placement_cache = false;
@@ -386,14 +386,14 @@
           << "We shouldn't need to invalidate the placement cache if we relied "
              "on the cached line resolver; it must produce the same placement.";
 
-      GridTrackSizingAlgorithm::CacheGridItemsProperties(layout_data.Columns(),
+      GridTrackSizingAlgorithm::CacheGridItemsProperties(layout_data->Columns(),
                                                          grid_items);
-      GridTrackSizingAlgorithm::CacheGridItemsProperties(layout_data.Rows(),
+      GridTrackSizingAlgorithm::CacheGridItemsProperties(layout_data->Rows(),
                                                          grid_items);
     }
 
     *intrinsic_block_size =
-        CalculateIntrinsicBlockSize(*grid_items, layout_data);
+        CalculateIntrinsicBlockSize(*grid_items, *layout_data);
     return layout_subtree;
   }
 
@@ -611,7 +611,7 @@
   const auto subgridded_item =
       grid_item->is_subgridded_to_parent_grid
           ? sizing_subtree.LookupSubgriddedItemData(*grid_item)
-          : SubgriddedItemData(*grid_item, sizing_subtree.LayoutData(),
+          : SubgriddedItemData(*grid_item, &sizing_subtree.LayoutData(),
                                writing_mode);
 
   // TODO(ikilpatrick): We'll need to record if any child used an indefinite
@@ -855,8 +855,10 @@
     }
   }
 
-  layout_data.SetTrackCollection(std::make_unique<GridSizingTrackCollection>(
-      range_builder.FinalizeRanges(), track_direction, must_create_baselines));
+  layout_data.SetTrackCollection(
+      MakeGarbageCollected<GridSizingTrackCollection>(
+          range_builder.FinalizeRanges(), track_direction,
+          must_create_baselines));
 }
 
 GridLineResolver GridLayoutAlgorithm::BuildGridLineResolver(
@@ -989,7 +991,7 @@
     const auto subgridded_item =
         grid_item.is_subgridded_to_parent_grid
             ? sizing_subtree.LookupSubgriddedItemData(grid_item)
-            : SubgriddedItemData(grid_item, sizing_subtree.LayoutData(),
+            : SubgriddedItemData(grid_item, &sizing_subtree.LayoutData(),
                                  writing_mode);
 
     LayoutUnit inline_offset, block_offset;
@@ -1890,13 +1892,13 @@
   DCHECK(out_row_break_between);
 
   const auto& container_space = GetConstraintSpace();
-  const auto& layout_data = layout_subtree.LayoutData();
+  const auto* layout_data = layout_subtree.LayoutData();
   const bool should_propagate_child_break_values =
       container_space.ShouldPropagateChildBreakValues();
 
   if (should_propagate_child_break_values) {
     *out_row_break_between = Vector<EBreakBetween>(
-        layout_data.Rows().GetSetCount() + 1, EBreakBetween::kAuto);
+        layout_data->Rows().GetSetCount() + 1, EBreakBetween::kAuto);
   }
 
   GridBaselineAccumulator baseline_accumulator(Style().GetFontBaseline());
@@ -1918,11 +1920,11 @@
       (RuntimeEnabledFeatures::CSSGridGapSuppressionEnabled() &&
        out_unfragmented_gap_geometry)) {
     gap_accumulator = GapAccumulator();
-    gap_accumulator->BuildGapGeometry(layout_data);
+    gap_accumulator->BuildGapGeometry(*layout_data);
 
     if (out_track_idx_to_set_idx) {
       *out_track_idx_to_set_idx =
-          gap_accumulator->GetRowGapToSetIndicesMap(layout_data);
+          gap_accumulator->GetRowGapToSetIndicesMap(*layout_data);
     }
   }
 
@@ -1937,7 +1939,7 @@
 
     LogicalRect containing_grid_area;
     const auto space = CreateConstraintSpaceForLayout(
-        grid_item, layout_data, child_layout_subtree, &containing_grid_area);
+        grid_item, *layout_data, child_layout_subtree, &containing_grid_area);
 
     const auto& item_style = grid_item.node.Style();
     const auto margins = ComputeMarginsFor(space, item_style, container_space);
@@ -1948,13 +1950,13 @@
     LogicalBoxFragment fragment(container_writing_direction, physical_fragment);
 
     LayoutUnit inline_baseline_offset = ComputeBaselineOffset(
-        grid_item, layout_data.Columns(),
+        grid_item, layout_data->Columns(),
         LogicalBoxFragment(grid_item.BaselineWritingDirection(kForColumns),
                            physical_fragment),
         fragment, grid_item.parent_grid_font_baseline, kForColumns,
         containing_grid_area.size.inline_size);
     LayoutUnit block_baseline_offset = ComputeBaselineOffset(
-        grid_item, layout_data.Rows(),
+        grid_item, layout_data->Rows(),
         LogicalBoxFragment(grid_item.BaselineWritingDirection(kForRows),
                            physical_fragment),
         fragment, grid_item.parent_grid_font_baseline, kForRows,
@@ -2031,8 +2033,8 @@
   }
 
   // Propagate the baselines.
-  if (layout_data.Rows().HasBaselines()) {
-    baseline_accumulator.AccumulateRows(layout_data.Rows());
+  if (layout_data->Rows().HasBaselines()) {
+    baseline_accumulator.AccumulateRows(layout_data->Rows());
   }
   if (auto first_baseline = baseline_accumulator.FirstBaseline())
     container_builder_.SetFirstBaseline(*first_baseline);
@@ -2162,7 +2164,7 @@
   }
 
   const auto fragmentainer_block_size = FragmentainerCapacityForChildren();
-  const auto& layout_data = layout_subtree.LayoutData();
+  const auto& layout_data = *layout_subtree.LayoutData();
 
   base::span<const Member<const BreakToken>> child_break_tokens;
   if (GetBreakToken()) {
diff --git a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm_test.cc
index 767e21b..09074735 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm_test.cc
@@ -43,12 +43,12 @@
     algorithm.CompleteTrackSizingAlgorithm(kForRows, SizingConstraint::kLayout,
                                            &grid_sizing_tree);
 
-    layout_data_ = std::move(grid_sizing_tree.LayoutData());
+    layout_data_ = &grid_sizing_tree.LayoutData();
     for (const auto& grid_item : grid_sizing_tree.GetGridItems()) {
       GridItemCachedData item_data;
 
       item_data.available_row_size =
-          grid_item.CalculateAvailableSize(layout_data_.Rows());
+          grid_item.CalculateAvailableSize(layout_data_->Rows());
       item_data.column_span_properties = grid_item.column_span_properties;
       item_data.row_span_properties = grid_item.row_span_properties;
       item_data.resolved_position = grid_item.resolved_position;
@@ -59,8 +59,8 @@
   const GridSizingTrackCollection& TrackCollection(
       GridTrackSizingDirection track_direction) {
     const auto& track_collection = (track_direction == kForColumns)
-                                       ? layout_data_.Columns()
-                                       : layout_data_.Rows();
+                                       ? layout_data_->Columns()
+                                       : layout_data_->Rows();
     return To<GridSizingTrackCollection>(track_collection);
   }
 
@@ -163,7 +163,7 @@
   };
 
   Vector<GridItemCachedData> grid_items_data_;
-  GridLayoutData layout_data_;
+  Persistent<GridLayoutData> layout_data_;
 };
 
 TEST_F(GridLayoutAlgorithmTest, GridLayoutAlgorithmAvailableRowSizes) {
diff --git a/third_party/blink/renderer/core/layout/grid/grid_layout_utils.cc b/third_party/blink/renderer/core/layout/grid/grid_layout_utils.cc
index 645a3e97..d541ae9 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_layout_utils.cc
+++ b/third_party/blink/renderer/core/layout/grid/grid_layout_utils.cc
@@ -686,7 +686,7 @@
   const auto writing_mode = constraint_space.GetWritingMode();
 
   GridItems grid_items;
-  GridLayoutData layout_data;
+  GridLayoutData* layout_data = MakeGarbageCollected<GridLayoutData>();
   bool has_nested_subgrid = false;
 
   if (!must_ignore_children) {
@@ -702,18 +702,17 @@
 
   if (has_standalone_columns) {
     algorithm.BuildSizingCollection(kForColumns, line_resolver, grid_items,
-                                    layout_data, sizing_constraint,
+                                    *layout_data, sizing_constraint,
                                     needs_intrinsic_track_size);
   }
   if (has_standalone_rows) {
     algorithm.BuildSizingCollection(kForRows, line_resolver, grid_items,
-                                    layout_data, sizing_constraint,
+                                    *layout_data, sizing_constraint,
                                     needs_intrinsic_track_size);
   }
 
   if (!has_nested_subgrid) {
-    sizing_tree->SetSizingNodeData(node, std::move(grid_items),
-                                   std::move(layout_data));
+    sizing_tree->SetSizingNodeData(node, std::move(grid_items), layout_data);
     return;
   }
 
@@ -721,17 +720,17 @@
   InitializeTrackCollection(opt_subgrid_data, style, constraint_space,
                             algorithm.BorderScrollbarPadding(),
                             algorithm.GetGridAvailableSize(), kForColumns,
-                            &layout_data);
+                            layout_data);
   InitializeTrackCollection(opt_subgrid_data, style, constraint_space,
                             algorithm.BorderScrollbarPadding(),
                             algorithm.GetGridAvailableSize(), kForRows,
-                            &layout_data);
+                            layout_data);
 
   if (has_standalone_columns) {
-    layout_data.SizingCollection(kForColumns).CacheDefiniteSetsGeometry();
+    layout_data->SizingCollection(kForColumns).CacheDefiniteSetsGeometry();
   }
   if (has_standalone_rows) {
-    layout_data.SizingCollection(kForRows).CacheDefiniteSetsGeometry();
+    layout_data->SizingCollection(kForRows).CacheDefiniteSetsGeometry();
   }
 
   // `AppendSubgriddedItems` rely on the cached placement data of a subgrid to
@@ -749,11 +748,11 @@
     // the set indices of this grid item to determine its available space. This
     // happens because subgridded items are not considered by the range builder
     // since they can't be placed before we recurse into subgrids.
-    grid_item.ComputeSetIndices(layout_data.Columns());
-    grid_item.ComputeSetIndices(layout_data.Rows());
+    grid_item.ComputeSetIndices(layout_data->Columns());
+    grid_item.ComputeSetIndices(layout_data->Rows());
 
     const auto space =
-        algorithm.CreateConstraintSpaceForLayout(grid_item, layout_data);
+        algorithm.CreateConstraintSpaceForLayout(grid_item, *layout_data);
     const auto fragment_geometry =
         CalculateInitialFragmentGeometryForSubgrid(grid_item, space);
 
@@ -788,17 +787,16 @@
   // repetitions, so we do this process twice to avoid a cyclic dependency.
   if (has_standalone_columns) {
     algorithm.BuildSizingCollection(kForColumns, line_resolver, grid_items,
-                                    layout_data, sizing_constraint,
+                                    *layout_data, sizing_constraint,
                                     needs_intrinsic_track_size);
   }
   if (has_standalone_rows) {
     algorithm.BuildSizingCollection(kForRows, line_resolver, grid_items,
-                                    layout_data, sizing_constraint,
+                                    *layout_data, sizing_constraint,
                                     needs_intrinsic_track_size);
   }
 
-  sizing_tree->SetSizingNodeData(node, std::move(grid_items),
-                                 std::move(layout_data));
+  sizing_tree->SetSizingNodeData(node, std::move(grid_items), layout_data);
 }
 
 // TODO(almaher): Need to add an instance for GridLanesLayoutAlgorithm.
@@ -913,7 +911,7 @@
   return fragment_geometry;
 }
 
-std::unique_ptr<GridLayoutTrackCollection> CreateSubgridTrackCollection(
+GridLayoutTrackCollection* CreateSubgridTrackCollection(
     const SubgriddedItemData& subgrid_data,
     const ComputedStyle& style,
     const ConstraintSpace& space,
@@ -932,17 +930,16 @@
                                   ? subgrid_data->column_range_indices
                                   : subgrid_data->row_range_indices;
 
-  return std::make_unique<GridLayoutTrackCollection>(
-      parent_track_collection.CreateSubgridTrackCollection(
-          range_indices.begin, range_indices.end,
-          GridTrackSizingAlgorithm::CalculateGutterSize(
-              style, grid_available_size, track_direction,
-              parent_track_collection.GutterSize()),
-          ComputeMarginsForSelf(space, style), border_scrollbar_padding,
-          track_direction,
-          is_for_columns_in_parent
-              ? subgrid_data->is_opposite_direction_in_root_grid_columns
-              : subgrid_data->is_opposite_direction_in_root_grid_rows));
+  return parent_track_collection.CreateSubgridTrackCollection(
+      range_indices.begin, range_indices.end,
+      GridTrackSizingAlgorithm::CalculateGutterSize(
+          style, grid_available_size, track_direction,
+          parent_track_collection.GutterSize()),
+      ComputeMarginsForSelf(space, style), border_scrollbar_padding,
+      track_direction,
+      is_for_columns_in_parent
+          ? subgrid_data->is_opposite_direction_in_root_grid_columns
+          : subgrid_data->is_opposite_direction_in_root_grid_rows);
 }
 
 void InitializeTrackCollection(const SubgriddedItemData& opt_subgrid_data,
diff --git a/third_party/blink/renderer/core/layout/grid/grid_layout_utils.h b/third_party/blink/renderer/core/layout/grid/grid_layout_utils.h
index 7d2b1a1..b443ce8 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_layout_utils.h
+++ b/third_party/blink/renderer/core/layout/grid/grid_layout_utils.h
@@ -244,14 +244,14 @@
     DCHECK(next_subgrid_subtree);
     callback_func(
         subgrid_algorithm, next_subgrid_subtree,
-        SubgriddedItemData(grid_item, layout_data,
+        SubgriddedItemData(grid_item, &layout_data,
                            algorithm.GetConstraintSpace().GetWritingMode()));
 
     next_subgrid_subtree = next_subgrid_subtree.NextSibling();
   }
 }
 
-std::unique_ptr<GridLayoutTrackCollection> CreateSubgridTrackCollection(
+GridLayoutTrackCollection* CreateSubgridTrackCollection(
     const SubgriddedItemData& subgrid_data,
     const ComputedStyle& style,
     const ConstraintSpace& space,
diff --git a/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.cc b/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.cc
index 11c148cd..bc1a899 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.cc
+++ b/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.cc
@@ -18,12 +18,12 @@
 
 void GridSizingTree::SetSizingNodeData(const BlockNode& grid_node,
                                        GridItems&& grid_items,
-                                       GridLayoutData&& layout_data) {
+                                       GridLayoutData* layout_data) {
   DCHECK(grid_node.IsGrid());
 
   const bool has_standalone_columns =
-      !layout_data.HasSubgriddedAxis(kForColumns);
-  const bool has_standalone_rows = !layout_data.HasSubgriddedAxis(kForRows);
+      !layout_data->HasSubgriddedAxis(kForColumns);
+  const bool has_standalone_rows = !layout_data->HasSubgriddedAxis(kForRows);
 
   const auto grid_node_index = LookupSubgridIndex(grid_node);
   auto child_subgrid_index = grid_node_index + 1;
@@ -60,7 +60,7 @@
   }
 
   tree_node.grid_items = std::move(grid_items);
-  tree_node.layout_data = std::move(layout_data);
+  tree_node.layout_data = layout_data;
   tree_node.writing_mode = grid_node.Style().GetWritingMode();
 }
 
@@ -73,7 +73,8 @@
   for (const auto& grid_tree_node : tree_data_) {
     layout_tree_data.emplace_back(
         MakeGarbageCollected<GridLayoutTree::GridTreeNode>(
-            grid_tree_node.layout_data, grid_tree_node.subtree_size));
+            MakeGarbageCollected<GridLayoutData>(*grid_tree_node.layout_data),
+            grid_tree_node.subtree_size));
   }
 
   for (wtf_size_t i = tree_size; i; --i) {
diff --git a/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.h b/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.h
index b61028b..48c5065 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.h
+++ b/third_party/blink/renderer/core/layout/grid/grid_sizing_tree.h
@@ -25,10 +25,10 @@
   SubgriddedItemData() = default;
 
   SubgriddedItemData(const GridItemData& item_data_in_parent,
-                     const GridLayoutData& parent_layout_data,
+                     const GridLayoutData* parent_layout_data,
                      WritingMode parent_writing_mode)
       : item_data_in_parent_(&item_data_in_parent),
-        parent_layout_data_(&parent_layout_data),
+        parent_layout_data_(parent_layout_data),
         parent_writing_mode_(parent_writing_mode) {}
 
   explicit operator bool() const { return item_data_in_parent_ != nullptr; }
@@ -83,10 +83,14 @@
   struct GridTreeNode {
     DISALLOW_NEW();
 
-    void Trace(Visitor* visitor) const { visitor->Trace(grid_items); }
+    void Trace(Visitor* visitor) const {
+      visitor->Trace(grid_items);
+      visitor->Trace(layout_data);
+    }
 
     GridItems grid_items;
-    GridLayoutData layout_data;
+    // TODO(crbug.com/460491953): Make this Member<const GridLayoutData>
+    Member<GridLayoutData> layout_data;
     wtf_size_t subtree_size{1};
     WritingMode writing_mode;
   };
@@ -101,12 +105,12 @@
 
   void SetSizingNodeData(const BlockNode& grid_node,
                          GridItems&& grid_items,
-                         GridLayoutData&& layout_data);
+                         GridLayoutData* layout_data);
 
   GridItems& GetGridItems(wtf_size_t index = 0) { return At(index).grid_items; }
 
   GridLayoutData& LayoutData(wtf_size_t index = 0) {
-    return At(index).layout_data;
+    return *At(index).layout_data;
   }
 
   // Creates a copy of the current grid geometry for the entire tree in a new
diff --git a/third_party/blink/renderer/core/layout/grid/grid_track_collection.cc b/third_party/blink/renderer/core/layout/grid/grid_track_collection.cc
index 7cd74a0a..50411a2 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_track_collection.cc
+++ b/third_party/blink/renderer/core/layout/grid/grid_track_collection.cc
@@ -626,7 +626,7 @@
          begin_set_index <= last_indefinite_index;
 }
 
-GridLayoutTrackCollection
+GridLayoutTrackCollection*
 GridLayoutTrackCollection::CreateSubgridTrackCollection(
     wtf_size_t begin_range_index,
     wtf_size_t end_range_index,
@@ -638,7 +638,8 @@
   DCHECK_LE(begin_range_index, end_range_index);
   DCHECK_LT(end_range_index, ranges_.size());
 
-  GridLayoutTrackCollection subgrid_track_collection(subgrid_track_direction);
+  GridLayoutTrackCollection* subgrid_track_collection =
+      MakeGarbageCollected<GridLayoutTrackCollection>(subgrid_track_direction);
 
   const wtf_size_t begin_set_index = ranges_[begin_range_index].begin_set_index;
   const wtf_size_t end_set_index = ranges_[end_range_index].begin_set_index +
@@ -649,8 +650,8 @@
 
   // Copy and translate the ranges in the subgrid's span.
   {
-    auto& subgrid_properties = subgrid_track_collection.properties_;
-    auto& subgrid_ranges = subgrid_track_collection.ranges_;
+    auto& subgrid_properties = subgrid_track_collection->properties_;
+    auto& subgrid_ranges = subgrid_track_collection->ranges_;
 
     const wtf_size_t range_count = end_range_index - begin_range_index;
     wtf_size_t current_begin_set_index = 0;
@@ -701,22 +702,22 @@
   // Accumulate the extra margin from the spanned sets in the parent track
   // collection and this subgrid's margins and gutter size delta.
   {
-    subgrid_track_collection.accumulated_gutter_size_delta_ =
+    subgrid_track_collection->accumulated_gutter_size_delta_ =
         subgrid_gutter_size_delta + accumulated_gutter_size_delta_;
 
-    auto& subgrid_sets_geometry = subgrid_track_collection.sets_geometry_;
+    auto& subgrid_sets_geometry = subgrid_track_collection->sets_geometry_;
     subgrid_sets_geometry.ReserveInitialCapacity(set_span_size + 1);
     subgrid_sets_geometry.emplace_back(
         /* offset */ subgrid_border_scrollbar_padding_start);
 
     // Opposite direction subgrids adjust extra margin from the opposite side.
-    subgrid_track_collection.accumulated_start_extra_margin_ =
+    subgrid_track_collection->accumulated_start_extra_margin_ =
         subgrid_margin_border_scrollbar_padding_start +
         (is_opposite_direction_in_root_grid
              ? EndExtraMargin(end_set_index)
              : StartExtraMargin(begin_set_index));
 
-    subgrid_track_collection.accumulated_end_extra_margin_ =
+    subgrid_track_collection->accumulated_end_extra_margin_ =
         subgrid_margin_border_scrollbar_padding_end +
         (is_opposite_direction_in_root_grid ? StartExtraMargin(begin_set_index)
                                             : EndExtraMargin(end_set_index));
@@ -776,7 +777,7 @@
   // Copy the last indefinite indices in the subgrid's span.
   if (!last_indefinite_index_.empty()) {
     auto& subgrid_last_indefinite_index =
-        subgrid_track_collection.last_indefinite_index_;
+        subgrid_track_collection->last_indefinite_index_;
 
     subgrid_last_indefinite_index.ReserveInitialCapacity(set_span_size + 1);
     subgrid_last_indefinite_index.push_back(kNotFound);
@@ -833,10 +834,10 @@
       std::swap(subgrid_baselines.major, subgrid_baselines.minor);
     }
 
-    subgrid_track_collection.baselines_.emplace(std::move(subgrid_baselines));
+    subgrid_track_collection->baselines_.emplace(std::move(subgrid_baselines));
   }
 
-  subgrid_track_collection.gutter_size_ = subgrid_gutter_size;
+  subgrid_track_collection->gutter_size_ = subgrid_gutter_size;
   return subgrid_track_collection;
 }
 
diff --git a/third_party/blink/renderer/core/layout/grid/grid_track_collection.h b/third_party/blink/renderer/core/layout/grid/grid_track_collection.h
index e45cb5b0..6275bed 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_track_collection.h
+++ b/third_party/blink/renderer/core/layout/grid/grid_track_collection.h
@@ -148,9 +148,9 @@
   Vector<TrackBoundaryToRangePair, 16> end_lines_;
 };
 
-class CORE_EXPORT GridLayoutTrackCollection : public GridTrackCollectionBase {
-  USING_FAST_MALLOC(GridLayoutTrackCollection);
-
+class CORE_EXPORT GridLayoutTrackCollection
+    : public GarbageCollected<GridLayoutTrackCollection>,
+      public GridTrackCollectionBase {
  public:
   struct SetGeometry {
     explicit SetGeometry(LayoutUnit offset, wtf_size_t track_count = 0)
@@ -160,6 +160,9 @@
     wtf_size_t track_count;
   };
 
+  explicit GridLayoutTrackCollection(GridTrackSizingDirection track_direction)
+      : track_direction_(track_direction) {}
+
   GridLayoutTrackCollection() = delete;
 
   // Don't allow this class to be used for grid sizing.
@@ -207,7 +210,7 @@
 
   // Creates a track collection containing every |Range| with index in the range
   // [begin, end], including their respective |SetGeometry| and baselines.
-  GridLayoutTrackCollection CreateSubgridTrackCollection(
+  GridLayoutTrackCollection* CreateSubgridTrackCollection(
       wtf_size_t begin_range_index,
       wtf_size_t end_range_index,
       LayoutUnit subgrid_gutter_size,
@@ -231,6 +234,8 @@
     return collapsed_track_indexes_;
   }
 
+  virtual void Trace(Visitor* visitor) const {}
+
  protected:
   friend class GridLanesLayoutAlgorithmTest;
 
@@ -239,9 +244,6 @@
     Vector<LayoutUnit, 16> minor;
   };
 
-  explicit GridLayoutTrackCollection(GridTrackSizingDirection track_direction)
-      : track_direction_(track_direction) {}
-
   // Checks whether any set in the range [begin, end) is indefinite.
   bool IsSpanningIndefiniteSet(wtf_size_t begin_set_index,
                                wtf_size_t end_set_index) const;
@@ -374,11 +376,11 @@
 
 class CORE_EXPORT GridSizingTrackCollection final
     : public GridLayoutTrackCollection {
-  USING_FAST_MALLOC(GridSizingTrackCollection);
-
  public:
   template <bool is_const>
   class CORE_EXPORT SetIteratorBase {
+    STACK_ALLOCATED();
+
    public:
     using TrackCollectionPtr =
         typename std::conditional<is_const,
diff --git a/third_party/blink/renderer/core/layout/grid/grid_track_collection_test.cc b/third_party/blink/renderer/core/layout/grid/grid_track_collection_test.cc
index 57a68cf..e0c62cd 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_track_collection_test.cc
+++ b/third_party/blink/renderer/core/layout/grid/grid_track_collection_test.cc
@@ -327,14 +327,16 @@
   auto range_builder = CreateRangeBuilder(explicit_tracks, implicit_tracks,
                                           /* auto_repetitions */ 0);
 
-  GridSizingTrackCollection track_collection(range_builder.FinalizeRanges());
+  GridSizingTrackCollection* track_collection =
+      MakeGarbageCollected<GridSizingTrackCollection>(
+          range_builder.FinalizeRanges());
   InitializeSetsForSizingCollection(explicit_tracks, implicit_tracks,
-                                    &track_collection);
-  const auto& ranges = GetRangesFrom(track_collection);
+                                    track_collection);
+  const auto& ranges = GetRangesFrom(*track_collection);
 
   // Test the set iterator for the entire collection.
   wtf_size_t set_count = 0;
-  for (auto set_iterator = track_collection.GetSetIterator();
+  for (auto set_iterator = track_collection->GetSetIterator();
        !set_iterator.IsAtEnd(); set_iterator.MoveToNextSet()) {
     EXPECT_SET(GridTrackSize(Length::Flex(set_count++)), 1u, set_iterator);
   }
@@ -347,7 +349,7 @@
     EXPECT_RANGE(set_count, set_counts[range_count], ranges[i]);
 
     wtf_size_t current_range_set_count = 0;
-    for (auto set_iterator = IteratorForRange(track_collection, i);
+    for (auto set_iterator = IteratorForRange(*track_collection, i);
          !set_iterator.IsAtEnd(); set_iterator.MoveToNextSet()) {
       EXPECT_SET(GridTrackSize(Length::Flex(set_count++)), 1u, set_iterator);
       ++current_range_set_count;
@@ -387,10 +389,12 @@
   range_builder.EnsureTrackCoverage(17, 3, &range3_start, &range3_end);
   range_builder.EnsureTrackCoverage(22, 5, &range4_start, &range4_end);
 
-  GridSizingTrackCollection track_collection(range_builder.FinalizeRanges());
+  GridSizingTrackCollection* track_collection =
+      MakeGarbageCollected<GridSizingTrackCollection>(
+          range_builder.FinalizeRanges());
   InitializeSetsForSizingCollection(explicit_tracks, implicit_tracks,
-                                    &track_collection);
-  const auto& ranges = GetRangesFrom(track_collection);
+                                    track_collection);
+  const auto& ranges = GetRangesFrom(*track_collection);
 
   EXPECT_EQ(1u, range1_start);
   EXPECT_EQ(1u, range1_end);
@@ -403,14 +407,14 @@
 
   EXPECT_EQ(10u, ranges.size());
   EXPECT_RANGE(0u, 2u, ranges[0]);
-  auto set_iterator = IteratorForRange(track_collection, /* range_index */ 0);
+  auto set_iterator = IteratorForRange(*track_collection, /* range_index */ 0);
   EXPECT_SET(GridTrackSize(Length::Fixed(1)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(2)), 1u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
 
   EXPECT_RANGE(2u, 4u, ranges[1]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 1);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 1);
   EXPECT_SET(GridTrackSize(Length::Fixed(3)), 2u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(1)), 1u, set_iterator);
@@ -419,7 +423,7 @@
   EXPECT_FALSE(set_iterator.MoveToNextSet());
 
   EXPECT_RANGE(6u, 3u, ranges[2]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 2);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 2);
   EXPECT_SET(GridTrackSize(Length::Fixed(1)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(2)), 1u, set_iterator);
@@ -428,39 +432,39 @@
   EXPECT_FALSE(set_iterator.MoveToNextSet());
 
   EXPECT_COLLAPSED_RANGE(9u, 3u, ranges[3]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 3);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 3);
   EXPECT_TRUE(set_iterator.IsAtEnd());
 
   EXPECT_RANGE(12u, 4u, ranges[4]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 4);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 4);
   EXPECT_SET(GridTrackSize(Length::Fixed(5)), 2u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(4)), 2u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
 
   EXPECT_COLLAPSED_RANGE(16u, 1u, ranges[5]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 5);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 5);
   EXPECT_TRUE(set_iterator.IsAtEnd());
 
   EXPECT_RANGE(17u, 2u, ranges[6]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 6);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 6);
   EXPECT_SET(GridTrackSize(Length::Fixed(4)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(5)), 1u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
 
   EXPECT_RANGE(19u, 1u, ranges[7]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 7);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 7);
   EXPECT_SET(GridTrackSize(Length::Auto()), 1u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
 
   EXPECT_RANGE(20u, 2u, ranges[8]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 8);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 8);
   EXPECT_SET(GridTrackSize(Length::Auto()), 2u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
 
   EXPECT_RANGE(22u, 5u, ranges[9]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 9);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 9);
   EXPECT_SET(GridTrackSize(Length::Auto()), 5u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
 }
@@ -492,10 +496,12 @@
   range_builder.EnsureTrackCoverage(2, 13, &range1_start, &range1_end);
   range_builder.EnsureTrackCoverage(23, 2, &range2_start, &range2_end);
 
-  GridSizingTrackCollection track_collection(range_builder.FinalizeRanges());
+  GridSizingTrackCollection* track_collection =
+      MakeGarbageCollected<GridSizingTrackCollection>(
+          range_builder.FinalizeRanges());
   InitializeSetsForSizingCollection(explicit_tracks, implicit_tracks,
-                                    &track_collection);
-  const auto& ranges = GetRangesFrom(track_collection);
+                                    track_collection);
+  const auto& ranges = GetRangesFrom(*track_collection);
 
   EXPECT_EQ(1u, range1_start);
   EXPECT_EQ(2u, range1_end);
@@ -504,21 +510,21 @@
 
   EXPECT_EQ(5u, ranges.size());
   EXPECT_RANGE(0u, 2u, ranges[0]);
-  auto set_iterator = IteratorForRange(track_collection, /* range_index */ 0);
+  auto set_iterator = IteratorForRange(*track_collection, /* range_index */ 0);
   EXPECT_SET(GridTrackSize(Length::Fixed(1)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(2)), 1u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
 
   EXPECT_RANGE(2u, 2u, ranges[1]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 1);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 1);
   EXPECT_SET(GridTrackSize(Length::Fixed(3)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(4)), 1u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
 
   EXPECT_RANGE(4u, 11u, ranges[2]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 2);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 2);
   EXPECT_SET(GridTrackSize(Length::Fixed(5)), 4u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(6)), 4u, set_iterator);
@@ -527,7 +533,7 @@
   EXPECT_FALSE(set_iterator.MoveToNextSet());
 
   EXPECT_RANGE(15u, 8u, ranges[3]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 3);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 3);
   EXPECT_SET(GridTrackSize(Length::Fixed(7)), 3u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(5)), 3u, set_iterator);
@@ -536,7 +542,7 @@
   EXPECT_FALSE(set_iterator.MoveToNextSet());
 
   EXPECT_RANGE(23u, 2u, ranges[4]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 4);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 4);
   EXPECT_SET(GridTrackSize(Length::Fixed(6)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(7)), 1u, set_iterator);
@@ -564,10 +570,12 @@
   range_builder.EnsureTrackCoverage(1, 2, &range1_start, &range1_end);
   range_builder.EnsureTrackCoverage(7, 4, &range2_start, &range2_end);
 
-  GridSizingTrackCollection track_collection(range_builder.FinalizeRanges());
+  GridSizingTrackCollection* track_collection =
+      MakeGarbageCollected<GridSizingTrackCollection>(
+          range_builder.FinalizeRanges());
   InitializeSetsForSizingCollection(explicit_tracks, implicit_tracks,
-                                    &track_collection);
-  const auto& ranges = GetRangesFrom(track_collection);
+                                    track_collection);
+  const auto& ranges = GetRangesFrom(*track_collection);
 
   EXPECT_EQ(1u, range1_start);
   EXPECT_EQ(1u, range1_end);
@@ -576,7 +584,7 @@
 
   EXPECT_EQ(5u, ranges.size());
   EXPECT_RANGE(0u, 1u, ranges[0]);
-  auto set_iterator = IteratorForRange(track_collection, /* range_index */ 0);
+  auto set_iterator = IteratorForRange(*track_collection, /* range_index */ 0);
   EXPECT_SET(GridTrackSize(Length::MinContent()), 1u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
   EXPECT_FALSE(
@@ -585,7 +593,7 @@
       TrackSpanProperties::kHasIntrinsicTrack));
 
   EXPECT_RANGE(1u, 2u, ranges[1]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 1);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 1);
   EXPECT_SET(GridTrackSize(Length::Flex(1.0)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::Fixed(2)), 1u, set_iterator);
@@ -596,7 +604,7 @@
       TrackSpanProperties::kHasIntrinsicTrack));
 
   EXPECT_RANGE(3u, 4u, ranges[2]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 2);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 2);
   EXPECT_SET(GridTrackSize(Length::Fixed(3)), 1u, set_iterator);
   EXPECT_TRUE(set_iterator.MoveToNextSet());
   EXPECT_SET(GridTrackSize(Length::MinContent()), 1u, set_iterator);
@@ -611,7 +619,7 @@
       TrackSpanProperties::kHasIntrinsicTrack));
 
   EXPECT_RANGE(7u, 1u, ranges[3]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 3);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 3);
   EXPECT_SET(GridTrackSize(Length::Fixed(3)), 1u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
   EXPECT_FALSE(
@@ -620,7 +628,7 @@
       TrackSpanProperties::kHasIntrinsicTrack));
 
   EXPECT_RANGE(8u, 3u, ranges[4]);
-  set_iterator = IteratorForRange(track_collection, /* range_index */ 4);
+  set_iterator = IteratorForRange(*track_collection, /* range_index */ 4);
   EXPECT_SET(GridTrackSize(Length::Auto()), 3u, set_iterator);
   EXPECT_FALSE(set_iterator.MoveToNextSet());
   EXPECT_FALSE(
diff --git a/third_party/blink/renderer/core/layout/grid/layout_grid.cc b/third_party/blink/renderer/core/layout/grid/layout_grid.cc
index 525d7d331..f22f0b3 100644
--- a/third_party/blink/renderer/core/layout/grid/layout_grid.cc
+++ b/third_party/blink/renderer/core/layout/grid/layout_grid.cc
@@ -121,12 +121,13 @@
 
 const MinMaxSizes& LayoutGrid::CachedSubgridMinMaxSizes() const {
   DCHECK(HasCachedSubgridMinMaxSizes());
-  return **cached_subgrid_min_max_sizes_;
+  return cached_subgrid_min_max_sizes_->CachedMinMaxSizes();
 }
 
 void LayoutGrid::SetSubgridMinMaxSizesCache(MinMaxSizes&& min_max_sizes,
                                             const GridLayoutData& layout_data) {
-  cached_subgrid_min_max_sizes_.emplace(std::move(min_max_sizes), layout_data);
+  cached_subgrid_min_max_sizes_ = MakeGarbageCollected<SubgridMinMaxSizesCache>(
+      std::move(min_max_sizes), layout_data);
   SetSubgridMinMaxSizesCacheDirty(false);
 }
 
diff --git a/third_party/blink/renderer/core/layout/grid/layout_grid.h b/third_party/blink/renderer/core/layout/grid/layout_grid.h
index eac1de5..94dc392 100644
--- a/third_party/blink/renderer/core/layout/grid/layout_grid.h
+++ b/third_party/blink/renderer/core/layout/grid/layout_grid.h
@@ -70,6 +70,11 @@
 
   const GridLayoutData* LayoutData() const;
 
+  void Trace(Visitor* visitor) const override {
+    LayoutBlock::Trace(visitor);
+    visitor->Trace(cached_subgrid_min_max_sizes_);
+  }
+
  private:
   bool IsLayoutGrid() const final {
     NOT_DESTROYED();
@@ -85,7 +90,7 @@
                       const StyleChangeContext&) override;
 
   std::optional<GridPlacementData> cached_placement_data_;
-  std::optional<const SubgridMinMaxSizesCache> cached_subgrid_min_max_sizes_;
+  Member<const SubgridMinMaxSizesCache> cached_subgrid_min_max_sizes_;
 };
 
 // wtf/casting.h helper.
diff --git a/third_party/blink/renderer/core/layout/grid/subgrid_min_max_sizes_cache.h b/third_party/blink/renderer/core/layout/grid/subgrid_min_max_sizes_cache.h
index d73f5b75..5f96052 100644
--- a/third_party/blink/renderer/core/layout/grid/subgrid_min_max_sizes_cache.h
+++ b/third_party/blink/renderer/core/layout/grid/subgrid_min_max_sizes_cache.h
@@ -6,12 +6,12 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_GRID_SUBGRID_MIN_MAX_SIZES_CACHE_H_
 
 #include "third_party/blink/renderer/core/layout/min_max_sizes.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 
 namespace blink {
 
-class SubgridMinMaxSizesCache {
-  DISALLOW_NEW();
-
+class SubgridMinMaxSizesCache
+    : public GarbageCollected<SubgridMinMaxSizesCache> {
  public:
   SubgridMinMaxSizesCache() = delete;
   SubgridMinMaxSizesCache(const SubgridMinMaxSizesCache&) = delete;
@@ -23,18 +23,22 @@
             layout_data.OnlySubgriddedCollection()),
         cached_min_max_sizes_(std::move(min_max_sizes)) {}
 
-  const MinMaxSizes& operator*() const { return cached_min_max_sizes_; }
-
   bool IsValidFor(const GridLayoutData& layout_data) const {
-    return layout_data.OnlySubgriddedCollection() ==
-           opposite_axis_subgridded_tracks_;
+    return *layout_data.OnlySubgriddedCollection() ==
+           *opposite_axis_subgridded_tracks_;
+  }
+
+  const MinMaxSizes& CachedMinMaxSizes() const { return cached_min_max_sizes_; }
+
+  void Trace(Visitor* visitor) const {
+    visitor->Trace(opposite_axis_subgridded_tracks_);
   }
 
  private:
   // The intrinsic sizes of a subgrid's standalone axis might change when the
   // subgridded tracks in the opposite axis change. We keep a copy of these
   // tracks to check if the cache is reusable with the new layout data.
-  GridLayoutTrackCollection opposite_axis_subgridded_tracks_;
+  Member<const GridLayoutTrackCollection> opposite_axis_subgridded_tracks_;
 
   MinMaxSizes cached_min_max_sizes_;
 };
diff --git a/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.cc b/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.cc
index 3786e02..cc3b9c6e 100644
--- a/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.cc
@@ -81,7 +81,7 @@
 
     const bool should_apply_inline_size_containment =
         node.ShouldApplyInlineSizeContainment();
-    GridSizingTrackCollection track_collection = ComputeGridAxisTracks(
+    GridSizingTrackCollection* track_collection = ComputeGridAxisTracks(
         sizing_constraint, /*intrinsic_repeat_track_sizes=*/nullptr,
         should_apply_inline_size_containment, grid_lanes_items,
         needs_intrinsic_track_size);
@@ -96,7 +96,7 @@
     if (needs_intrinsic_track_size) {
       HashMap<GridTrackSize, LayoutUnit> intrinsic_repeat_track_sizes =
           GetIntrinsicRepeaterTrackSizes(!grid_lanes_items.IsEmpty(),
-                                         track_collection);
+                                         *track_collection);
       track_collection = ComputeGridAxisTracks(
           sizing_constraint, &intrinsic_repeat_track_sizes,
           should_apply_inline_size_containment, grid_lanes_items,
@@ -107,7 +107,7 @@
       // Track sizing is done during the guess placement step, which happens in
       // `BuildGridAxisTracks`, so at this point, getting the width of all of
       // the columns should correctly give us the intrinsic inline size.
-      return track_collection.CalculateSetSpanSize();
+      return track_collection->CalculateSetSpanSize();
     } else {
       if (grid_lanes_items.IsEmpty()) {
         // If there are no grid-lanes items, the intrinsic inline size is only
@@ -116,11 +116,11 @@
       }
 
       GridLanesRunningPositions running_positions(
-          track_collection, style,
+          *track_collection, style,
           ResolveFlowToleranceForGridLanes(style, grid_lanes_available_size_));
 
-      PlaceGridLanesItems(track_collection, grid_lanes_items, running_positions,
-                          sizing_constraint);
+      PlaceGridLanesItems(*track_collection, grid_lanes_items,
+                          running_positions, sizing_constraint);
       // `stacking_axis_gap` represents the space between each of the items
       // in the row. We need to subtract this as it is always added to
       // `running_positions` whenever an item is placed, but the very last
@@ -131,7 +131,7 @@
       return running_positions.GetMaxPositionForSpan(
                  GridSpan::TranslatedDefiniteGridSpan(
                      /*start_line=*/0,
-                     /*end_line=*/track_collection.EndLineOfImplicitGrid())) -
+                     /*end_line=*/track_collection->EndLineOfImplicitGrid())) -
              stacking_axis_gap;
     }
   };
@@ -162,7 +162,7 @@
 
   // Never apply inline size containment during the layout pass because size
   // containment is independent from layout.
-  GridSizingTrackCollection track_collection = ComputeGridAxisTracks(
+  GridSizingTrackCollection* track_collection = ComputeGridAxisTracks(
       SizingConstraint::kLayout, /*intrinsic_repeat_track_sizes=*/nullptr,
       /*should_apply_inline_size_containment=*/false, grid_lanes_items,
       needs_intrinsic_track_size, &oof_children);
@@ -177,7 +177,7 @@
   if (needs_intrinsic_track_size) {
     HashMap<GridTrackSize, LayoutUnit> intrinsic_repeat_track_sizes =
         GetIntrinsicRepeaterTrackSizes(!grid_lanes_items.IsEmpty(),
-                                       track_collection);
+                                       *track_collection);
     track_collection = ComputeGridAxisTracks(
         SizingConstraint::kLayout, &intrinsic_repeat_track_sizes,
         /*should_apply_inline_size_containment=*/false, grid_lanes_items,
@@ -186,10 +186,10 @@
 
   if (!grid_lanes_items.IsEmpty()) {
     GridLanesRunningPositions running_positions(
-        track_collection, Style(),
+        *track_collection, Style(),
         ResolveFlowToleranceForGridLanes(Style(), grid_lanes_available_size_));
 
-    PlaceGridLanesItems(track_collection, grid_lanes_items, running_positions,
+    PlaceGridLanesItems(*track_collection, grid_lanes_items, running_positions,
                         SizingConstraint::kLayout);
   }
 
@@ -201,10 +201,8 @@
   }
 
   // Create track layout data to support grid-lanes overlay in DevTools.
-  std::unique_ptr<GridLayoutData> layout_data(
-      std::make_unique<GridLayoutData>());
-  layout_data->SetTrackCollection(
-      std::make_unique<GridLayoutTrackCollection>(track_collection));
+  GridLayoutData* layout_data = MakeGarbageCollected<GridLayoutData>();
+  layout_data->SetTrackCollection(track_collection);
 
   // Account for border, scrollbar, and padding in the intrinsic block size.
   intrinsic_block_size_ += BorderScrollbarPadding().BlockSum();
@@ -224,7 +222,7 @@
     PlaceOutOfFlowItems(*layout_data, block_size, oof_children);
   }
 
-  container_builder_.TransferGridLayoutData(std::move(layout_data));
+  container_builder_.SetGridLayoutData(layout_data);
   container_builder_.HandleOofsAndSpecialDescendants();
   return container_builder_.ToBoxFragment();
 }
@@ -509,10 +507,10 @@
 
     // TODO(almaher): This should use the sizing tree eventually to get this
     // data.
-    std::unique_ptr<GridLayoutData> layout_data(
-        std::make_unique<GridLayoutData>());
+    // TODO(kschmi): Do we need to deep copy the track collection?
+    GridLayoutData* layout_data = MakeGarbageCollected<GridLayoutData>();
     layout_data->SetTrackCollection(
-        std::make_unique<GridLayoutTrackCollection>(track_collection));
+        MakeGarbageCollected<GridLayoutTrackCollection>(track_collection));
     const ConstraintSpace space =
         is_for_layout
             ? CreateConstraintSpaceForLayout(
@@ -1138,7 +1136,7 @@
       // TODO(almaher): Once support for subgrid is added and we have a sizing
       // tree similar to grid, we may need to implement and call something
       // similar to Grid's BuildGridSizingTreeIgnoringChildren() here.
-      GridSizingTrackCollection track_collection =
+      GridSizingTrackCollection* track_collection =
           ComputeGridAxisTracks(SizingConstraint::kLayout,
                                 /*intrinsic_repeat_track_sizes=*/nullptr,
                                 /*should_apply_inline_size_containment=*/false,
@@ -1154,14 +1152,14 @@
       if (needs_intrinsic_track_size) {
         HashMap<GridTrackSize, LayoutUnit> intrinsic_repeat_track_sizes =
             GetIntrinsicRepeaterTrackSizes(!grid_lanes_items.IsEmpty(),
-                                           track_collection);
+                                           *track_collection);
         track_collection = ComputeGridAxisTracks(
             SizingConstraint::kLayout, &intrinsic_repeat_track_sizes,
             /*should_apply_inline_size_containment=*/false, grid_lanes_items,
             needs_intrinsic_track_size);
       }
 
-      override_intrinsic_block_size = track_collection.CalculateSetSpanSize();
+      override_intrinsic_block_size = track_collection->CalculateSetSpanSize();
     } else {
       // If we are in columns, the block size should just be the size of an
       // empty container.
@@ -1252,7 +1250,7 @@
   return baseline_fragment.BlockSize();
 }
 
-GridSizingTrackCollection GridLanesLayoutAlgorithm::ComputeGridAxisTracks(
+GridSizingTrackCollection* GridLanesLayoutAlgorithm::ComputeGridAxisTracks(
     const SizingConstraint sizing_constraint,
     const HashMap<GridTrackSize, LayoutUnit>* intrinsic_repeat_track_sizes,
     const bool should_apply_inline_size_containment,
@@ -1284,7 +1282,7 @@
                              needs_intrinsic_track_size);
 }
 
-GridSizingTrackCollection GridLanesLayoutAlgorithm::BuildGridAxisTracks(
+GridSizingTrackCollection* GridLanesLayoutAlgorithm::BuildGridAxisTracks(
     const GridLineResolver& line_resolver,
     const GridItems& grid_lanes_items,
     SizingConstraint sizing_constraint,
@@ -1325,26 +1323,26 @@
     return range_builder.FinalizeRanges(needs_intrinsic_track_size);
   };
 
-  GridSizingTrackCollection track_collection(
-      BuildRanges(), grid_axis_direction,
-      /*must_create_baselines=*/has_baseline_aligned_items_,
-      /*should_store_collapsed_track_indexes=*/true);
-  track_collection.BuildSets(style, grid_lanes_available_size_);
+  GridSizingTrackCollection* track_collection =
+      MakeGarbageCollected<GridSizingTrackCollection>(
+          BuildRanges(), grid_axis_direction, has_baseline_aligned_items_,
+          /*should_store_collapsed_track_indexes=*/true);
+  track_collection->BuildSets(style, grid_lanes_available_size_);
 
   // Allocate the major/minor baseline vectors now that we know the set count.
   if (has_baseline_aligned_items_) {
-    track_collection.ResetBaselines();
+    track_collection->ResetBaselines();
   }
 
-  if (track_collection.HasNonDefiniteTrack()) {
-    GridTrackSizingAlgorithm::CacheGridItemsProperties(track_collection,
+  if (track_collection->HasNonDefiniteTrack()) {
+    GridTrackSizingAlgorithm::CacheGridItemsProperties(*track_collection,
                                                        &virtual_items);
 
     const GridTrackSizingAlgorithm track_sizing_algorithm(
         style, grid_lanes_available_size_, grid_lanes_min_available_size_,
         sizing_constraint);
 
-    track_collection.CacheInitializedSetsGeometry(
+    track_collection->CacheInitializedSetsGeometry(
         (grid_axis_direction == kForColumns)
             ? BorderScrollbarPadding().inline_start
             : BorderScrollbarPadding().block_start);
@@ -1355,17 +1353,17 @@
         [&](GridItemContributionType contribution_type,
             GridItemData* virtual_item) {
           return ContributionSizeForVirtualItem(
-              track_collection, contribution_type, virtual_item);
+              *track_collection, contribution_type, virtual_item);
         },
-        &track_collection, &virtual_items, needs_intrinsic_track_size);
+        track_collection, &virtual_items, needs_intrinsic_track_size);
   }
 
   auto first_set_geometry = GridTrackSizingAlgorithm::ComputeFirstSetGeometry(
-      track_collection, style, grid_lanes_available_size_,
+      *track_collection, style, grid_lanes_available_size_,
       BorderScrollbarPadding());
 
-  track_collection.FinalizeSetsGeometry(first_set_geometry.start_offset,
-                                        first_set_geometry.gutter_size);
+  track_collection->FinalizeSetsGeometry(first_set_geometry.start_offset,
+                                         first_set_geometry.gutter_size);
 
   return track_collection;
 }
@@ -1545,10 +1543,11 @@
     return range_builder.FinalizeRanges(needs_intrinsic_track_size);
   };
 
-  layout_data.SetTrackCollection(std::make_unique<GridSizingTrackCollection>(
-      BuildRanges(), grid_axis_direction,
-      /*must_create_baselines=*/has_baseline_aligned_items_,
-      /*should_store_collapsed_track_indexes=*/true));
+  layout_data.SetTrackCollection(
+      MakeGarbageCollected<GridSizingTrackCollection>(
+          BuildRanges(), grid_axis_direction,
+          /*must_create_baselines=*/has_baseline_aligned_items_,
+          /*should_store_collapsed_track_indexes=*/true));
 }
 
 ConstraintSpace GridLanesLayoutAlgorithm::CreateConstraintSpace(
diff --git a/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.h b/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.h
index 4e6eb8b3..b6f5467 100644
--- a/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm.h
@@ -144,7 +144,7 @@
   // required once we've computed the intrinsic track size. `opt_oof_children`
   // is an optional vector of out-of-flow direct children of the grid-lanes
   // container that this method will populate.
-  GridSizingTrackCollection ComputeGridAxisTracks(
+  GridSizingTrackCollection* ComputeGridAxisTracks(
       const SizingConstraint sizing_constraint,
       const HashMap<GridTrackSize, LayoutUnit>* intrinsic_repeat_track_sizes,
       const bool should_apply_inline_size_containment,
@@ -152,7 +152,7 @@
       bool& needs_intrinsic_track_size,
       HeapVector<Member<LayoutBox>>* opt_oof_children = nullptr);
 
-  GridSizingTrackCollection BuildGridAxisTracks(
+  GridSizingTrackCollection* BuildGridAxisTracks(
       const GridLineResolver& line_resolver,
       const GridItems& grid_lanes_items,
       SizingConstraint sizing_constraint,
diff --git a/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm_test.cc
index 03b5e21d..c0e8ae0 100644
--- a/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/grid_lanes/grid_lanes_layout_algorithm_test.cc
@@ -40,7 +40,7 @@
     if (needs_intrinsic_track_size) {
       HashMap<GridTrackSize, LayoutUnit> intrinsic_repeat_track_sizes =
           algorithm.GetIntrinsicRepeaterTrackSizes(!grid_lanes_items.IsEmpty(),
-                                                   grid_axis_tracks_.value());
+                                                   *grid_axis_tracks_);
       grid_axis_tracks_ = algorithm.ComputeGridAxisTracks(
           SizingConstraint::kLayout, &intrinsic_repeat_track_sizes,
           /*should_apply_inline_size_containment=*/false, grid_lanes_items,
@@ -123,7 +123,7 @@
     return virtual_items_data_[index];
   }
 
-  std::optional<GridSizingTrackCollection> grid_axis_tracks_;
+  Persistent<GridSizingTrackCollection> grid_axis_tracks_;
 
   // Virtual items represent the contributions of item groups in track sizing
   // and are not directly related to any children of the container.
diff --git a/third_party/blink/renderer/core/layout/layout_result.cc b/third_party/blink/renderer/core/layout/layout_result.cc
index 5345222..a52c0eb 100644
--- a/third_party/blink/renderer/core/layout/layout_result.cc
+++ b/third_party/blink/renderer/core/layout/layout_result.cc
@@ -103,8 +103,7 @@
         builder->math_italic_correction_;
   }
   if (builder->grid_layout_data_) {
-    EnsureRareData()->EnsureGridData()->grid_layout_data =
-        std::move(builder->grid_layout_data_);
+    EnsureRareData()->grid_layout_data = builder->grid_layout_data_;
   }
   if (builder->flex_layout_data_) {
     EnsureRareData()->EnsureFlexData()->flex_layout_data =
@@ -416,6 +415,7 @@
   visitor->Trace(early_break);
   visitor->Trace(non_overflowing_scroll_ranges);
   visitor->Trace(column_spanner_path);
+  visitor->Trace(grid_layout_data);
   visitor->Trace(exclusion_space);
   visitor->Trace(line_clamp_after_layout_object);
   visitor->Trace(accessibility_anchor);
diff --git a/third_party/blink/renderer/core/layout/layout_result.h b/third_party/blink/renderer/core/layout/layout_result.h
index a934af5..fabd6ad 100644
--- a/third_party/blink/renderer/core/layout/layout_result.h
+++ b/third_party/blink/renderer/core/layout/layout_result.h
@@ -407,11 +407,7 @@
   }
 
   const GridLayoutData* GetGridLayoutData() const {
-    if (!rare_data_) {
-      return nullptr;
-    }
-    const RareData::GridData* data = rare_data_->GetGridData();
-    return data ? data->grid_layout_data.get() : nullptr;
+    return rare_data_ ? rare_data_->grid_layout_data.Get() : nullptr;
   }
 
   const DevtoolsFlexInfo* FlexLayoutData() const {
@@ -680,16 +676,6 @@
       std::unique_ptr<const DevtoolsFlexInfo> flex_layout_data;
     };
 
-    struct GridData {
-      GridData() = default;
-      GridData(const GridData& other) {
-        grid_layout_data =
-            std::make_unique<GridLayoutData>(*other.grid_layout_data);
-      }
-
-      std::unique_ptr<const GridLayoutData> grid_layout_data;
-    };
-
     // `LineSmallData` can save allocations When only fields in it are needed.
     struct LineSmallData {
       std::optional<LayoutUnit> ClearanceAfterLine() const {
@@ -806,12 +792,6 @@
     const FlexData* GetFlexData() const {
       return GetData<FlexData>(&flex_data, kFlexData);
     }
-    GridData* EnsureGridData() {
-      return EnsureData<GridData>(&grid_data, kGridData);
-    }
-    const GridData* GetGridData() const {
-      return GetData<GridData>(&grid_data, kGridData);
-    }
     // When both `EnsureLineData()` and `EnsureLineSmallData()` are needed,
     // `EnsureLineData()` must be done first. Upgrading `kLineSmallData` to
     // `kLineData` isn't supported due to the lack of the needs.
@@ -852,6 +832,7 @@
     RareData(const RareData& rare_data)
         : early_break(rare_data.early_break),
           column_spanner_path(rare_data.column_spanner_path),
+          grid_layout_data(rare_data.grid_layout_data),
           end_margin_strut(rare_data.end_margin_strut),
           // This will initialize "both" members of the union.
           tallest_unbreakable_block_size(
@@ -875,9 +856,6 @@
         case kFlexData:
           new (&flex_data) FlexData(rare_data.flex_data);
           break;
-        case kGridData:
-          new (&grid_data) GridData(rare_data.grid_data);
-          break;
         case kLineSmallData:
           new (&line_small_data) LineSmallData(rare_data.line_small_data);
           break;
@@ -902,9 +880,6 @@
         case kFlexData:
           flex_data.~FlexData();
           break;
-        case kGridData:
-          grid_data.~GridData();
-          break;
         case kLineSmallData:
           line_small_data.~LineSmallData();
           break;
@@ -967,6 +942,7 @@
 
     Member<const EarlyBreak> early_break;
     Member<const ColumnSpannerPath> column_spanner_path;
+    Member<const GridLayoutData> grid_layout_data;
     MarginStrut end_margin_strut;
     union {
       // Only set in the initial column balancing layout pass, when we have no
@@ -1005,7 +981,6 @@
 
     union {
       FlexData flex_data;
-      GridData grid_data;
       LineSmallData line_small_data;
       LineDataPtr line_data;
       MathData math_data;
diff --git a/third_party/blink/renderer/core/layout/simplified_layout_algorithm.cc b/third_party/blink/renderer/core/layout/simplified_layout_algorithm.cc
index 60053a7..5e327ba 100644
--- a/third_party/blink/renderer/core/layout/simplified_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/simplified_layout_algorithm.cc
@@ -148,8 +148,7 @@
   }
 
   if (physical_fragment.IsGrid() || physical_fragment.IsGridLanes()) {
-    container_builder_.TransferGridLayoutData(
-        std::make_unique<GridLayoutData>(*result.GetGridLayoutData()));
+    container_builder_.SetGridLayoutData(result.GetGridLayoutData());
   } else if (physical_fragment.IsFrameSet()) {
     container_builder_.TransferFrameSetLayoutData(
         std::make_unique<FrameSetLayoutData>(
diff --git a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc
index 89b45bf..d3b9aff 100644
--- a/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc
+++ b/third_party/blink/renderer/core/page/scrolling/element_fragment_anchor.cc
@@ -26,12 +26,8 @@
 namespace {
 // TODO(bokan): Move this into FragmentDirective after
 // https://crrev.com/c/3216206 lands.
-String RemoveFragmentDirectives(const String& url_fragment) {
-  wtf_size_t directive_delimiter_ix = url_fragment.find(":~:");
-  if (directive_delimiter_ix == kNotFound)
-    return url_fragment;
-
-  return url_fragment.substr(0, directive_delimiter_ix);
+StringView RemoveFragmentDirectives(const StringView& url_fragment) {
+  return url_fragment.substr(0, url_fragment.find(":~:"));
 }
 
 }  // namespace
@@ -51,9 +47,8 @@
   if (!url.HasFragmentIdentifier() && !doc.CssTarget() && !doc.IsSVGDocument())
     return nullptr;
 
-  String fragment =
-      RemoveFragmentDirectives(url.FragmentIdentifier().ToString());
-  Node* anchor_node = doc.FindAnchor(fragment);
+  StringView fragment = RemoveFragmentDirectives(url.FragmentIdentifier());
+  Node* anchor_node = doc.FindAnchor(fragment.ToString());
 
   // Setting to null will clear the current target.
   auto* target = DynamicTo<Element>(anchor_node);
diff --git a/third_party/blink/renderer/core/script_tools/model_context.cc b/third_party/blink/renderer/core/script_tools/model_context.cc
index 1d5fd24a..7b5857e 100644
--- a/third_party/blink/renderer/core/script_tools/model_context.cc
+++ b/third_party/blink/renderer/core/script_tools/model_context.cc
@@ -4,7 +4,9 @@
 
 #include "third_party/blink/renderer/core/script_tools/model_context.h"
 
+#include "base/metrics/histogram_functions.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/time/time.h"
 #include "third_party/blink/public/platform/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/web/web_script_tool_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/capture_source_location.h"
@@ -21,6 +23,7 @@
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/source_location.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/json/json_parser.h"
 #include "third_party/blink/renderer/platform/json/json_values.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -250,6 +253,7 @@
 
   tool_map_.insert(tool->name(), tool_data);
   probe::WebMCPToolAdded(document_, *tool_data);
+  MaybeRecordToolCount();
   OnToolChange();
 }
 
@@ -494,6 +498,7 @@
       base::PassKey<ModelContext>(), std::move(script_tool), declarative_tool);
   tool_map_.insert(name, tool_data);
   probe::WebMCPToolAdded(document_, *tool_data);
+  MaybeRecordToolCount();
   OnToolChange();
 }
 
@@ -520,6 +525,24 @@
   }
 }
 
+void ModelContext::MaybeRecordToolCount() {
+  if (!will_record_tool_count_) {
+    will_record_tool_count_ = true;
+    task_runner_->PostDelayedTask(
+        FROM_HERE,
+        blink::BindOnce(
+            [](ModelContext* context) {
+              if (context) {
+                base::UmaHistogramCounts100(
+                    "Blink.ModelContext.DelayedToolCount",
+                    context->tool_map_.size());
+              }
+            },
+            WrapWeakPersistent(this)),
+        base::Seconds(10));
+  }
+}
+
 void ModelContext::PauseExecution() {
   if (!script_tool_host_remote_.is_bound()) {
     document_->GetExecutionContext()->GetBrowserInterfaceBroker().GetInterface(
diff --git a/third_party/blink/renderer/core/script_tools/model_context.h b/third_party/blink/renderer/core/script_tools/model_context.h
index 1a679474..450590de 100644
--- a/third_party/blink/renderer/core/script_tools/model_context.h
+++ b/third_party/blink/renderer/core/script_tools/model_context.h
@@ -159,6 +159,7 @@
   void OnToolExecuted(uint32_t execution_id, std::optional<String> result);
 
   void OnToolChange();
+  void MaybeRecordToolCount();
 
   HeapHashMap<String, Member<ToolData>> tool_map_;
 
@@ -176,6 +177,10 @@
   Member<Document> document_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
   HeapMojoRemote<mojom::blink::ScriptToolHost> script_tool_host_remote_;
+
+  // true when there is a pending or completed task to record the number
+  // of registered tools.
+  bool will_record_tool_count_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/script_tools/model_context_test.cc b/third_party/blink/renderer/core/script_tools/model_context_test.cc
index d601edd..d562cb4 100644
--- a/third_party/blink/renderer/core/script_tools/model_context_test.cc
+++ b/third_party/blink/renderer/core/script_tools/model_context_test.cc
@@ -8,6 +8,7 @@
 
 #include "base/run_loop.h"
 #include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/run_until.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -1197,4 +1198,64 @@
   EXPECT_EQ("leave-feedback", tools[1]->BackingFormElement()->GetIdAttribute());
 }
 
+class ModelContextMetricsTest : public SimTest {
+ public:
+  ModelContextMetricsTest()
+      : SimTest(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+
+ protected:
+  void EvalJsString(std::string_view script) {
+    MainFrame().ExecuteScript(WebScriptSource(WebString::FromUTF8(script)));
+  }
+
+ private:
+  ScopedWebMCPForTest scoped_webmcp_{true};
+};
+
+TEST_F(ModelContextMetricsTest, RecordToolCountHistogram) {
+  base::HistogramTester histogram_tester;
+
+  SimRequest main_resource("https://example.com/", "text/html");
+  LoadURL("https://example.com/");
+
+  main_resource.Complete(R"(<!DOCTYPE html>
+    <form
+      id=book-table
+      toolname=book-table
+      tooldescription="Book a table">
+    </form>
+    <form
+      id=leave-feedback
+      toolname=leave-feedback
+      tooldescription="leave-feedback">
+    </form>
+  )");
+
+  v8::HandleScope handle_scope(Window().GetIsolate());
+  ScriptState::Scope script_scope(
+      ToScriptStateForMainWorld(Window().GetFrame()));
+
+  task_environment().FastForwardBy(base::Seconds(2));
+  EvalJsString(R"JS(navigator.modelContext.registerTool({
+      execute: () => "true",
+      name: "tool1",
+      description: "Tool1",
+    }))JS");
+
+  task_environment().FastForwardBy(base::Seconds(2));
+  for (int i = 2; i <= 4; i++) {
+    // clang-format off
+    EvalJsString(String::Format(R"JS(navigator.modelContext.registerTool({
+      execute: () => "true",
+      name: "tool%d",
+      description: "Tool%d",
+    }))JS", i, i).Utf8());
+    // clang-format on
+  }
+
+  task_environment().FastForwardBy(base::Seconds(8));
+  histogram_tester.ExpectUniqueSample("Blink.ModelContext.DelayedToolCount", 6,
+                                      1);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/svg/svg_parsing_error.cc b/third_party/blink/renderer/core/svg/svg_parsing_error.cc
index 4ab5957..ad5fa967 100644
--- a/third_party/blink/renderer/core/svg/svg_parsing_error.cc
+++ b/third_party/blink/renderer/core/svg/svg_parsing_error.cc
@@ -94,7 +94,7 @@
     if (context_start != 0)
       builder.Append(uchar::kHorizontalEllipsis);
     EscapeStringForJSON(
-        value.GetString().Substring(context_start, context_end - context_start),
+        StringView(value, context_start, context_end - context_start),
         &builder);
     if (context_end != value.length())
       builder.Append(uchar::kHorizontalEllipsis);
diff --git a/third_party/blink/renderer/platform/json/json_values.cc b/third_party/blink/renderer/platform/json/json_values.cc
index c00f9a6..80341c9c 100644
--- a/third_party/blink/renderer/platform/json/json_values.cc
+++ b/third_party/blink/renderer/platform/json/json_values.cc
@@ -88,7 +88,7 @@
 
 }  // anonymous namespace
 
-void EscapeStringForJSON(const String& str, StringBuilder* dst) {
+void EscapeStringForJSON(const StringView& str, StringBuilder* dst) {
   for (unsigned i = 0; i < str.length(); ++i) {
     UChar c = str[i];
     if (!EscapeChar(c, dst)) {
@@ -102,16 +102,16 @@
   }
 }
 
-void DoubleQuoteStringForJSON(const String& str, StringBuilder* dst) {
+void DoubleQuoteStringForJSON(const StringView& str, StringBuilder* dst) {
   dst->Append('"');
   EscapeStringForJSON(str, dst);
   dst->Append('"');
 }
 
-String JSONValue::QuoteString(const String& input) {
+String JSONValue::QuoteString(const StringView& input) {
   StringBuilder builder;
   DoubleQuoteStringForJSON(input, &builder);
-  return builder.ToString();
+  return builder.ReleaseString();
 }
 
 bool JSONValue::AsBoolean(bool*) const {
diff --git a/third_party/blink/renderer/platform/json/json_values.h b/third_party/blink/renderer/platform/json/json_values.h
index 92dd0e9..fcb5c5b9 100644
--- a/third_party/blink/renderer/platform/json/json_values.h
+++ b/third_party/blink/renderer/platform/json/json_values.h
@@ -90,7 +90,7 @@
   virtual void PrettyWriteJSON(StringBuilder* output) const;
   virtual std::unique_ptr<JSONValue> Clone() const;
 
-  static String QuoteString(const String&);
+  static String QuoteString(const StringView&);
 
  protected:
   JSONValue() : type_(kTypeNull) {}
@@ -309,8 +309,8 @@
 inline constexpr char kJSONTrueString[] = "true";
 inline constexpr char kJSONFalseString[] = "false";
 
-PLATFORM_EXPORT void EscapeStringForJSON(const String&, StringBuilder*);
-void DoubleQuoteStringForJSON(const String&, StringBuilder*);
+PLATFORM_EXPORT void EscapeStringForJSON(const StringView&, StringBuilder*);
+void DoubleQuoteStringForJSON(const StringView&, StringBuilder*);
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index ceb205c..418667f 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -3091,6 +3091,15 @@
       name: "HTMLCommandActionsV2",
       status: "test",
     },
+    // Remove support for the <command> element from the HTML parsing logic.
+    // This shipped in M148 and the flag can be removed in M150.
+    // https://issues.chromium.org/issues/491158089
+    // NOTE: when this flag is removed, be sure to also remove "command"
+    // from third_party/blink/renderer/core/html/html_tag_names.json5.
+    {
+      name: "HTMLCommandElementRemoval",
+      status: "stable",
+    },
     // Adds support for scroll commands such as 'page-up' and 'page-block-end'.
     // https://github.com/danielsakhapov/declarative-scroll-commands-for-html-explainer
     {
diff --git a/third_party/blink/renderer/platform/weborigin/kurl.cc b/third_party/blink/renderer/platform/weborigin/kurl.cc
index a0f511a9..0db8f005 100644
--- a/third_party/blink/renderer/platform/weborigin/kurl.cc
+++ b/third_party/blink/renderer/platform/weborigin/kurl.cc
@@ -471,15 +471,14 @@
 
 }  // namespace
 
-bool KURL::SetProtocol(const String& protocol) {
+bool KURL::SetProtocol(const StringView& protocol) {
   // We should remove whitespace from |protocol| according to spec, but Firefox
   // and Safari don't do it.
   // - https://url.spec.whatwg.org/#dom-url-protocol
   // - https://github.com/whatwg/url/issues/609
 
   // Firefox and IE remove everything after the first ':'.
-  wtf_size_t separator_position = protocol.find(':');
-  String new_protocol = protocol.substr(0, separator_position);
+  StringView new_protocol = protocol.substr(0, protocol.find(':'));
   StringUtf8Adaptor new_protocol_utf8(new_protocol);
 
   // If KURL is given an invalid scheme, it returns failure without modifying
@@ -493,13 +492,13 @@
     return false;
   }
 
-  DCHECK_EQ(protocol_component.begin, 0);
-  const wtf_size_t protocol_length =
-      base::checked_cast<wtf_size_t>(protocol_component.len);
-  const String new_protocol_canon =
-      String(base::span(canon_protocol.view()).first(protocol_length));
-
   if (SchemeRegistry::IsSpecialScheme(Protocol())) {
+    DCHECK_EQ(protocol_component.begin, 0);
+    const wtf_size_t protocol_length =
+        base::checked_cast<wtf_size_t>(protocol_component.len);
+    const String new_protocol_canon(
+        base::span(canon_protocol.view()).first(protocol_length));
+
     // https://url.spec.whatwg.org/#scheme-state
     // 2.1.1 If url’s scheme is a special scheme and buffer is not a special
     //       scheme, then return.
diff --git a/third_party/blink/renderer/platform/weborigin/kurl.h b/third_party/blink/renderer/platform/weborigin/kurl.h
index 7dfb90b..37e3327 100644
--- a/third_party/blink/renderer/platform/weborigin/kurl.h
+++ b/third_party/blink/renderer/platform/weborigin/kurl.h
@@ -198,7 +198,7 @@
   bool IsAboutSrcdocUrl() const;  // Is about:srcdoc, ignoring query/ref
                                   // strings..
 
-  bool SetProtocol(const String&);
+  bool SetProtocol(const StringView&);
   void SetHost(const String&);
 
   void RemovePort();
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 23c35ef8..7d295bb4 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -6079,10 +6079,6 @@
 # Sheriff 2021-05-03
 crbug.com/1205012 external/wpt/html/cross-origin-opener-policy/reporting/access-reporting/reporting-observer.html [ Pass Timeout ]
 
-
-# Sheriff 2021-05-07
-# Some plzServiceWorker and plzDedicatedWorker singled out from generic sheriff rounds above
-
 # For SkiaRenderer on MacOS
 crbug.com/1208173 [ Mac ] svg/custom/svg-root-with-opacity.html [ Failure ]
 crbug.com/1208173 [ Mac ] virtual/prefer_compositing_to_lcd_text/compositing/overflow/nested-render-surfaces-with-rotation.html [ Failure ]
@@ -6343,11 +6339,6 @@
 crbug.com/1066891 virtual/web-bluetooth-new-permissions-backend/external/wpt/bluetooth/legacy/requestDevice/sandboxed_iframe.https.window.html [ Failure Pass Timeout ]
 crbug.com/1066891 virtual/web-bluetooth-new-permissions-backend/external/wpt/bluetooth/legacy/requestLEScan/sandboxed_iframe.https.window.html [ Failure Pass Timeout ]
 
-# Sheriff 2021-07-13
-
-# Flakes that might be caused or aggravated by PlzServiceWorker
-crbug.com/996511 external/wpt/service-workers/service-worker/getregistrations.https.html [ Crash Failure Pass Timeout ]
-
 # Test fails due to more accurate size reporting in heap snapshots.
 crbug.com/40778216 inspector-protocol/heap-profiler/heap-samples-in-snapshot.js [ Failure Timeout ]
 
@@ -9609,7 +9600,7 @@
 # Gardener 2026-01-12
 crbug.com/475047610 [ Debug Linux ] external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_mouse.py [ Failure ]
 crbug.com/475126745 [ Debug Linux ] external/wpt/mathml/presentation-markup/scripts/underover-stretchy-003.html [ Failure ]
-crbug.com/465849685 [ Debug Linux ] media/media-playback-while-not-visible-permission-policy-cant-play-if-hidden.html [ Failure Pass ]
+crbug.com/465849685 [ Debug Linux ] media/media-playback-while-not-visible-permission-policy-cant-play-if-hidden.html [ Failure Pass Timeout ]
 
 # linux-blink-leak-rel failure
 crbug.com/476257809 [ Linux ] external/wpt/fedcm/fedcm-storage-access-api-autogrant.tentative.https.sub.html [ Failure Pass Skip ]
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=uri-expected.txt b/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=uri-expected.txt
deleted file mode 100644
index 96a5f706..0000000
--- a/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=uri-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] html5lib_tests25.html de4aa726e09215ba9c50b97d257e6c6b880107f1
-  assert_equals: expected "#document\\n| <!DOCTYPE html>\\n| <html>\\n|   <head>\\n|   <body>\\n|     <command>\\n|       \\"A\\"" but got "#document\\n| <!DOCTYPE html>\\n| <html>\\n|   <head>\\n|   <body>\\n|     <command>\\n|     \\"A\\""
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=write-expected.txt b/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=write-expected.txt
deleted file mode 100644
index 96a5f706..0000000
--- a/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=write-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] html5lib_tests25.html de4aa726e09215ba9c50b97d257e6c6b880107f1
-  assert_equals: expected "#document\\n| <!DOCTYPE html>\\n| <html>\\n|   <head>\\n|   <body>\\n|     <command>\\n|       \\"A\\"" but got "#document\\n| <!DOCTYPE html>\\n| <html>\\n|   <head>\\n|   <body>\\n|     <command>\\n|     \\"A\\""
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=write_single-expected.txt b/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=write_single-expected.txt
deleted file mode 100644
index 96a5f706..0000000
--- a/third_party/blink/web_tests/external/wpt/html/syntax/parsing/html5lib_tests25_run_type=write_single-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] html5lib_tests25.html de4aa726e09215ba9c50b97d257e6c6b880107f1
-  assert_equals: expected "#document\\n| <!DOCTYPE html>\\n| <html>\\n|   <head>\\n|   <body>\\n|     <command>\\n|       \\"A\\"" but got "#document\\n| <!DOCTYPE html>\\n| <html>\\n|   <head>\\n|   <body>\\n|     <command>\\n|     \\"A\\""
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/html5lib/generated/run-tests25-data-expected.txt b/third_party/blink/web_tests/html5lib/generated/run-tests25-data-expected.txt
index 28b9eae..e08f1f6 100644
--- a/third_party/blink/web_tests/html5lib/generated/run-tests25-data-expected.txt
+++ b/third_party/blink/web_tests/html5lib/generated/run-tests25-data-expected.txt
@@ -1,19 +1 @@
-../resources/tests25.dat:
-8
-
-Test 8 of 26 in ../resources/tests25.dat failed. Input:
-<!DOCTYPE html><body><command>A
-Got:
-| <!DOCTYPE html>
-| <html>
-|   <head>
-|   <body>
-|     <command>
-|     "A"
-Expected:
-| <!DOCTYPE html>
-| <html>
-|   <head>
-|   <body>
-|     <command>
-|       "A"
+../resources/tests25.dat: PASS
diff --git a/third_party/blink/web_tests/html5lib/generated/run-tests25-write-expected.txt b/third_party/blink/web_tests/html5lib/generated/run-tests25-write-expected.txt
index 28b9eae..e08f1f6 100644
--- a/third_party/blink/web_tests/html5lib/generated/run-tests25-write-expected.txt
+++ b/third_party/blink/web_tests/html5lib/generated/run-tests25-write-expected.txt
@@ -1,19 +1 @@
-../resources/tests25.dat:
-8
-
-Test 8 of 26 in ../resources/tests25.dat failed. Input:
-<!DOCTYPE html><body><command>A
-Got:
-| <!DOCTYPE html>
-| <html>
-|   <head>
-|   <body>
-|     <command>
-|     "A"
-Expected:
-| <!DOCTYPE html>
-| <html>
-|   <head>
-|   <body>
-|     <command>
-|       "A"
+../resources/tests25.dat: PASS
diff --git a/third_party/blink/web_tests/wpt_internal/webmcp/execute_tool_change_event.html b/third_party/blink/web_tests/wpt_internal/webmcp/execute_tool_change_event.html
new file mode 100644
index 0000000..212e109
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/webmcp/execute_tool_change_event.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<form toolname="fill_text" tooldescription="Fill some text"
+  toolautosubmit target="hidden_iframe" action="about:blank">
+  <input id="myinput" name="text" type="text">
+  <input id="mynumber" name="number" type="number">
+  <input id="mycheckbox" name="checkbox" type="checkbox" value="check1">
+  <input id="myradio" name="radio" type="radio" value="radio1">
+  <input id="myradio2" name="radio" type="radio" value="radio2">
+  <textarea id="mytextarea" name="textarea"></textarea>
+  <select id="myselect" name="select">
+    <option value="opt1">Opt 1</option>
+    <option value="opt2">Opt 2</option>
+  </select>
+</form>
+<iframe name=hidden_iframe style="display:none"></iframe>
+
+<script>
+promise_test(async t => {
+  assert_true(!!navigator.modelContextTesting);
+
+  let form = document.querySelector('form');
+  let inputs = form.querySelectorAll('input, textarea, select');
+  let events = {};
+
+  for (let el of inputs) {
+    if (el.id) {
+      events[el.id] = { inputFired: false, changeFired: false };
+      el.addEventListener('change', () => { events[el.id].changeFired = true; });
+      el.addEventListener('input', () => { events[el.id].inputFired = true; });
+    }
+  }
+
+  let inputArgs = JSON.stringify({
+    text: "regular text",
+    number: 42,
+    checkbox: true,
+    radio: "radio2",
+    textarea: "some text area content",
+    select: "opt2"
+  });
+
+  await navigator.modelContextTesting.executeTool('fill_text', inputArgs);
+
+  assert_equals(document.getElementById('myinput').value, 'regular text');
+  assert_true(events['myinput'].inputFired, 'text input event should fire');
+  assert_true(events['myinput'].changeFired, 'text change event should fire');
+
+  assert_equals(document.getElementById('mynumber').value, '42');
+  assert_true(events['mynumber'].inputFired, 'number input event should fire');
+  assert_true(events['mynumber'].changeFired, 'number change event should fire');
+
+  assert_true(document.getElementById('mycheckbox').checked);
+  assert_true(events['mycheckbox'].inputFired, 'checkbox input event should fire');
+  assert_true(events['mycheckbox'].changeFired, 'checkbox change event should fire');
+
+  assert_false(document.getElementById('myradio').checked);
+  assert_true(document.getElementById('myradio2').checked);
+  assert_true(events['myradio2'].inputFired, 'radio input event should fire');
+  assert_true(events['myradio2'].changeFired, 'radio change event should fire');
+
+  assert_equals(document.getElementById('mytextarea').value, 'some text area content');
+  assert_true(events['mytextarea'].inputFired, 'textarea input event should fire');
+  assert_true(events['mytextarea'].changeFired, 'textarea change event should fire');
+
+  assert_equals(document.getElementById('myselect').value, 'opt2');
+  assert_true(events['myselect'].inputFired, 'select input event should fire');
+  assert_true(events['myselect'].changeFired, 'select change event should fire');
+}, 'executeTool triggers input and change events');
+</script>
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index b2a86d0..48f9650 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit b2a86d09d5c4fa554a9a46cb1e94cf8ab52b2d0c
+Subproject commit 48f965098f72daf2d89b5ded468d3264299fb095
diff --git a/third_party/glslang/src b/third_party/glslang/src
index d1f52c8..959ff4a 160000
--- a/third_party/glslang/src
+++ b/third_party/glslang/src
@@ -1 +1 @@
-Subproject commit d1f52c8993a501bd52d4fbd044bfeb9ecdceb9f4
+Subproject commit 959ff4a0dba11111c5c999f1235c798431475322
diff --git a/third_party/perfetto b/third_party/perfetto
index 08b509b..3a764e6 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit 08b509bdad26c91f5f34d9ff97c9362a111f3a72
+Subproject commit 3a764e6f6339ae06c19540682dad4a1a9021f072
diff --git a/third_party/readability/README.chromium b/third_party/readability/README.chromium
index d48458d..1f3daf09 100644
--- a/third_party/readability/README.chromium
+++ b/third_party/readability/README.chromium
@@ -29,3 +29,5 @@
 - Remove <label for="id-of-removed-input"> (crbug.com/483369387).
 - When extracting title from JSON-LD, fallback to checking if the original
   document title includes the name or headline (crbug.com/487369435).
+- Add recipe keywords to regexes, and refine single-child promotion to ignore
+  insignificant siblings and include all content (crbug.com/483735928).
diff --git a/third_party/readability/modded_src/Readability-readerable.js b/third_party/readability/modded_src/Readability-readerable.js
index fa24803..2455456 100644
--- a/third_party/readability/modded_src/Readability-readerable.js
+++ b/third_party/readability/modded_src/Readability-readerable.js
@@ -24,7 +24,12 @@
   // Readability.js. Please keep both copies in sync.
   unlikelyCandidates:
     /-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i,
-  okMaybeItsACandidate: /and|article|body|column|content|main|mathjax|shadow/i,
+  okMaybeItsACandidate: new RegExp(
+    "and|article|body|column|content|main|mathjax|" +
+      "shadow|recipe|ingredients|instructions|" +
+      "directions|steps",
+    "i"
+  ),
 };
 
 function isNodeVisible(node) {
diff --git a/third_party/readability/modded_src/Readability.js b/third_party/readability/modded_src/Readability.js
index 9585afb6..d8d1543e 100644
--- a/third_party/readability/modded_src/Readability.js
+++ b/third_party/readability/modded_src/Readability.js
@@ -139,11 +139,20 @@
     // Readability-readerable.js. Please keep both copies in sync.
     unlikelyCandidates:
       /-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|footer|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i,
-    okMaybeItsACandidate:
-      /and|article|body|column|content|main|mathjax|shadow/i,
+    okMaybeItsACandidate: new RegExp(
+      "and|article|body|column|content|main|mathjax|" +
+        "shadow|recipe|ingredients|instructions|" +
+        "directions|steps",
+      "i"
+    ),
 
-    positive:
-      /article|body|content|entry|hentry|h-entry|main|page|pagination|post|text|blog|story/i,
+    positive: new RegExp(
+      "article|body|content|entry|hentry|h-entry|" +
+        "main|page|pagination|post|text|blog|story|" +
+        "recipe|ingredients|instructions|" +
+        "directions|steps",
+      "i"
+    ),
     negative:
       /-ad-|hidden|^hid$| hid$| hid |^hid |banner|combx|comment|com-|contact|footer|gdpr|masthead|media|meta|outbrain|promo|related|scroll|share|shoutbox|sidebar|skyscraper|sponsor|shopping|tags|widget/i,
     extraneous:
@@ -380,6 +389,25 @@
   },
 
   /**
+   * Check if the provided candidate has any significant siblings.
+   *
+   * @param  Element candidate The candidate element.
+   * @param  Number  threshold The content score threshold.
+   * @return Boolean
+   */
+  _hasSignificantSibling(candidate, threshold) {
+    return this._someNode(candidate.parentNode.children, function (sibling) {
+      if (sibling === candidate) {
+        return false;
+      }
+      if (sibling.readability) {
+        return sibling.readability.contentScore >= threshold;
+      }
+      return sibling.textContent.trim().length > 25;
+    });
+  },
+
+  /**
    * Iterate over a NodeList, return true if all of the provided iterate
    * function calls return true, false otherwise.
    *
@@ -1433,13 +1461,19 @@
           parentOfTopCandidate = parentOfTopCandidate.parentNode;
         }
 
-        // If the top candidate is the only child, use parent instead. This will help sibling
-        // joining logic when adjacent content is actually located in parent's sibling node.
+        // If the top candidate is the only child (or only child with
+        // significant content), use parent instead. This will help sibling
+        // joining logic when adjacent content is actually located in parent's
+        // sibling node.
         parentOfTopCandidate = topCandidate.parentNode;
-        while (
-          parentOfTopCandidate.tagName != "BODY" &&
-          parentOfTopCandidate.children.length == 1
-        ) {
+        const promotionThreshold = Math.max(
+          10,
+          topCandidate.readability.contentScore * 0.2
+        );
+        while (parentOfTopCandidate.tagName !== "BODY") {
+          if (this._hasSignificantSibling(topCandidate, promotionThreshold)) {
+            break;
+          }
           topCandidate = parentOfTopCandidate;
           parentOfTopCandidate = topCandidate.parentNode;
         }
diff --git a/third_party/rust/chromium_crates_io/Cargo.lock b/third_party/rust/chromium_crates_io/Cargo.lock
index 625ba99..48f5897 100644
--- a/third_party/rust/chromium_crates_io/Cargo.lock
+++ b/third_party/rust/chromium_crates_io/Cargo.lock
@@ -764,7 +764,7 @@
 
 [[package]]
 name = "itertools"
-version = "0.11.0"
+version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "either",
diff --git a/third_party/rust/chromium_crates_io/Cargo.toml b/third_party/rust/chromium_crates_io/Cargo.toml
index 2a894f12..bd166d0 100644
--- a/third_party/rust/chromium_crates_io/Cargo.toml
+++ b/third_party/rust/chromium_crates_io/Cargo.toml
@@ -28,7 +28,7 @@
 hex = "0.4"
 hmac-sha256 = "1.1.12"
 indexmap = "2.12.1"
-itertools = "0.11.0"
+itertools = "0.14.0"
 lazy_static = "1"
 libc = "0.2"
 minijinja = "2.14.0"
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.cargo_vcs_info.json
deleted file mode 100644
index 432bb37..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.cargo_vcs_info.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "git": {
-    "sha1": "62a6401afd6d45e1c2aea94c05cb5c70076b2ca4"
-  },
-  "path_in_vcs": ""
-}
\ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.github/workflows/ci.yml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.github/workflows/ci.yml
deleted file mode 100644
index 92122cca..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.github/workflows/ci.yml
+++ /dev/null
@@ -1,57 +0,0 @@
-name: CI
-
-on:
-  pull_request:
-  push:
-    branches:
-      - staging
-      - trying
-
-jobs:
-  check:
-    name: check
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        build: [msrv, stable]
-        features:
-          [
-            "",
-            "--no-default-features",
-            "--no-default-features --features use_alloc",
-            "--all-targets --all-features",
-          ]
-        include:
-          - build: msrv
-            rust: 1.62.1
-          - build: stable
-            rust: stable
-        exclude:
-          - build: msrv
-            # we only care about the MSRV with respect to the lib target
-            features: "--all-targets --all-features"
-    steps:
-      - uses: actions/checkout@v3
-      - uses: dtolnay/rust-toolchain@master
-        with:
-          toolchain: ${{ matrix.rust }}
-      - run: cargo check ${{ matrix.features }}
-
-  test:
-    name: test
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v3
-      - uses: dtolnay/rust-toolchain@stable
-      - run: cargo test --all-features
-
-  # https://github.com/rust-lang/crater/blob/9ab6f9697c901c4a44025cf0a39b73ad5b37d198/.github/workflows/bors.yml#L125-L149
-  end-success:
-    name: bors build finished
-    if: success()
-    runs-on: ubuntu-latest
-    needs: [check, test]
-
-    steps:
-      - name: Mark the job as successful
-        run: exit 0
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.rustfmt.toml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.rustfmt.toml
deleted file mode 100644
index 06eb57a17..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.rustfmt.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-# Temporarily disable rustfmt completely to avoid conflicts of newly formatted
-# code with old PRs.
-ignore = ["/"]
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.toml
deleted file mode 100644
index df3cbd8..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.toml
+++ /dev/null
@@ -1,101 +0,0 @@
-# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
-#
-# When uploading crates to the registry Cargo will automatically
-# "normalize" Cargo.toml files for maximal compatibility
-# with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies.
-#
-# If you are reading this file be aware that the original Cargo.toml
-# will likely look very different (and much more reasonable).
-# See Cargo.toml.orig for the original contents.
-
-[package]
-edition = "2018"
-rust-version = "1.36.0"
-name = "itertools"
-version = "0.11.0"
-authors = ["bluss"]
-exclude = ["/bors.toml"]
-description = "Extra iterator adaptors, iterator methods, free functions, and macros."
-documentation = "https://docs.rs/itertools/"
-readme = "README.md"
-keywords = [
-    "iterator",
-    "data-structure",
-    "zip",
-    "product",
-    "group-by",
-]
-categories = [
-    "algorithms",
-    "rust-patterns",
-]
-license = "MIT OR Apache-2.0"
-repository = "https://github.com/rust-itertools/itertools"
-
-[profile.bench]
-debug = 2
-
-[lib]
-test = false
-bench = false
-
-[[bench]]
-name = "tuple_combinations"
-harness = false
-
-[[bench]]
-name = "tuples"
-harness = false
-
-[[bench]]
-name = "fold_specialization"
-harness = false
-
-[[bench]]
-name = "combinations_with_replacement"
-harness = false
-
-[[bench]]
-name = "tree_fold1"
-harness = false
-
-[[bench]]
-name = "bench1"
-harness = false
-
-[[bench]]
-name = "combinations"
-harness = false
-
-[[bench]]
-name = "powerset"
-harness = false
-
-[dependencies.either]
-version = "1.0"
-default-features = false
-
-[dev-dependencies.criterion]
-version = "0.4.0"
-
-[dev-dependencies.paste]
-version = "1.0.0"
-
-[dev-dependencies.permutohedron]
-version = "0.2"
-
-[dev-dependencies.quickcheck]
-version = "0.9"
-default_features = false
-
-[dev-dependencies.rand]
-version = "0.7"
-
-[features]
-default = ["use_std"]
-use_alloc = []
-use_std = [
-    "use_alloc",
-    "either/use_std",
-]
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/README.md b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/README.md
deleted file mode 100644
index 626d10d..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/README.md
+++ /dev/null
@@ -1,44 +0,0 @@
-# Itertools
-
-Extra iterator adaptors, functions and macros.
-
-Please read the [API documentation here](https://docs.rs/itertools/).
-
-[![build_status](https://github.com/rust-itertools/itertools/actions/workflows/ci.yml/badge.svg)](https://github.com/rust-itertools/itertools/actions)
-[![crates.io](https://img.shields.io/crates/v/itertools.svg)](https://crates.io/crates/itertools)
-
-How to use with Cargo:
-
-```toml
-[dependencies]
-itertools = "0.11.0"
-```
-
-How to use in your crate:
-
-```rust
-use itertools::Itertools;
-```
-
-## How to contribute
-
-- Fix a bug or implement a new thing
-- Include tests for your new feature, preferably a QuickCheck test
-- Make a Pull Request
-
-For new features, please first consider filing a PR to [rust-lang/rust](https://github.com/rust-lang/rust),
-adding your new feature to the `Iterator` trait of the standard library, if you believe it is reasonable.
-If it isn't accepted there, proposing it for inclusion in ``itertools`` is a good idea.
-The reason for doing is this is so that we avoid future breakage as with ``.flatten()``.
-However, if your feature involves heap allocation, such as storing elements in a ``Vec<T>``,
-then it can't be accepted into ``libcore``, and you should propose it for ``itertools`` directly instead.
-
-## License
-
-Dual-licensed to be compatible with the Rust project.
-
-Licensed under the Apache License, Version 2.0
-https://www.apache.org/licenses/LICENSE-2.0 or the MIT license
-https://opensource.org/licenses/MIT, at your
-option. This file may not be copied, modified, or distributed
-except according to those terms.
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/extra/mod.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/extra/mod.rs
deleted file mode 100644
index 52fe5cc3..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/extra/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub use self::zipslices::ZipSlices;
-mod zipslices;
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/extra/zipslices.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/extra/zipslices.rs
deleted file mode 100644
index 633be59..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/extra/zipslices.rs
+++ /dev/null
@@ -1,188 +0,0 @@
-use std::cmp;
-
-// Note: There are different ways to implement ZipSlices.
-// This version performed the best in benchmarks.
-//
-// I also implemented a version with three pointers (tptr, tend, uptr),
-// that mimiced slice::Iter and only checked bounds by using tptr == tend,
-// but that was inferior to this solution.
-
-/// An iterator which iterates two slices simultaneously.
-///
-/// `ZipSlices` acts like a double-ended `.zip()` iterator.
-///
-/// It was intended to be more efficient than `.zip()`, and it was, then
-/// rustc changed how it optimizes so it can not promise improved performance
-/// at this time.
-///
-/// Note that elements past the end of the shortest of the two slices are ignored.
-///
-/// Iterator element type for `ZipSlices<T, U>` is `(T::Item, U::Item)`. For example,
-/// for a `ZipSlices<&'a [A], &'b mut [B]>`, the element type is `(&'a A, &'b mut B)`.
-#[derive(Clone)]
-pub struct ZipSlices<T, U> {
-    t: T,
-    u: U,
-    len: usize,
-    index: usize,
-}
-
-impl<'a, 'b, A, B> ZipSlices<&'a [A], &'b [B]> {
-    /// Create a new `ZipSlices` from slices `a` and `b`.
-    ///
-    /// Act like a double-ended `.zip()` iterator, but more efficiently.
-    ///
-    /// Note that elements past the end of the shortest of the two slices are ignored.
-    #[inline(always)]
-    pub fn new(a: &'a [A], b: &'b [B]) -> Self {
-        let minl = cmp::min(a.len(), b.len());
-        ZipSlices {
-            t: a,
-            u: b,
-            len: minl,
-            index: 0,
-        }
-    }
-}
-
-impl<T, U> ZipSlices<T, U>
-    where T: Slice,
-          U: Slice
-{
-    /// Create a new `ZipSlices` from slices `a` and `b`.
-    ///
-    /// Act like a double-ended `.zip()` iterator, but more efficiently.
-    ///
-    /// Note that elements past the end of the shortest of the two slices are ignored.
-    #[inline(always)]
-    pub fn from_slices(a: T, b: U) -> Self {
-        let minl = cmp::min(a.len(), b.len());
-        ZipSlices {
-            t: a,
-            u: b,
-            len: minl,
-            index: 0,
-        }
-    }
-}
-
-impl<T, U> Iterator for ZipSlices<T, U>
-    where T: Slice,
-          U: Slice
-{
-    type Item = (T::Item, U::Item);
-
-    #[inline(always)]
-    fn next(&mut self) -> Option<Self::Item> {
-        unsafe {
-            if self.index >= self.len {
-                None
-            } else {
-                let i = self.index;
-                self.index += 1;
-                Some((
-                    self.t.get_unchecked(i),
-                    self.u.get_unchecked(i)))
-            }
-        }
-    }
-
-    #[inline]
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let len = self.len - self.index;
-        (len, Some(len))
-    }
-}
-
-impl<T, U> DoubleEndedIterator for ZipSlices<T, U>
-    where T: Slice,
-          U: Slice
-{
-    #[inline(always)]
-    fn next_back(&mut self) -> Option<Self::Item> {
-        unsafe {
-            if self.index >= self.len {
-                None
-            } else {
-                self.len -= 1;
-                let i = self.len;
-                Some((
-                    self.t.get_unchecked(i),
-                    self.u.get_unchecked(i)))
-            }
-        }
-    }
-}
-
-impl<T, U> ExactSizeIterator for ZipSlices<T, U>
-    where T: Slice,
-          U: Slice
-{}
-
-unsafe impl<T, U> Slice for ZipSlices<T, U>
-    where T: Slice,
-          U: Slice
-{
-    type Item = (T::Item, U::Item);
-
-    fn len(&self) -> usize {
-        self.len - self.index
-    }
-
-    unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item {
-        (self.t.get_unchecked(i),
-         self.u.get_unchecked(i))
-    }
-}
-
-/// A helper trait to let `ZipSlices` accept both `&[T]` and `&mut [T]`.
-///
-/// Unsafe trait because:
-///
-/// - Implementors must guarantee that `get_unchecked` is valid for all indices `0..len()`.
-pub unsafe trait Slice {
-    /// The type of a reference to the slice's elements
-    type Item;
-    #[doc(hidden)]
-    fn len(&self) -> usize;
-    #[doc(hidden)]
-    unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item;
-}
-
-unsafe impl<'a, T> Slice for &'a [T] {
-    type Item = &'a T;
-    #[inline(always)]
-    fn len(&self) -> usize { (**self).len() }
-    #[inline(always)]
-    unsafe fn get_unchecked(&mut self, i: usize) -> &'a T {
-        debug_assert!(i < self.len());
-        (**self).get_unchecked(i)
-    }
-}
-
-unsafe impl<'a, T> Slice for &'a mut [T] {
-    type Item = &'a mut T;
-    #[inline(always)]
-    fn len(&self) -> usize { (**self).len() }
-    #[inline(always)]
-    unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut T {
-        debug_assert!(i < self.len());
-        // override the lifetime constraints of &mut &'a mut [T]
-        (*(*self as *mut [T])).get_unchecked_mut(i)
-    }
-}
-
-#[test]
-fn zipslices() {
-
-    let xs = [1, 2, 3, 4, 5, 6];
-    let ys = [1, 2, 3, 7];
-    ::itertools::assert_equal(ZipSlices::new(&xs, &ys), xs.iter().zip(&ys));
-
-    let xs = [1, 2, 3, 4, 5, 6];
-    let mut ys = [0; 6];
-    for (x, y) in ZipSlices::from_slices(&xs[..], &mut ys[..]) {
-        *y = *x;
-    }
-    ::itertools::assert_equal(&xs, &ys);
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/powerset.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/powerset.rs
deleted file mode 100644
index 074550b..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/powerset.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-use criterion::{black_box, criterion_group, criterion_main, Criterion};
-use itertools::Itertools;
-
-// Keep aggregate generated elements the same, regardless of powerset length.
-const TOTAL_ELEMENTS: usize = 1 << 12;
-const fn calc_iters(n: usize) -> usize {
-    TOTAL_ELEMENTS / (1 << n)
-}
-
-fn powerset_n(c: &mut Criterion, n: usize) {
-    let id = format!("powerset {}", n);
-    c.bench_function(id.as_str(), move |b| {
-        b.iter(|| {
-            for _ in 0..calc_iters(n) {
-                for elt in (0..n).powerset() {
-                    black_box(elt);
-                }
-            }
-        })
-    });
-}
-
-fn powerset_0(c: &mut Criterion) { powerset_n(c, 0); }
-
-fn powerset_1(c: &mut Criterion) { powerset_n(c, 1); }
-
-fn powerset_2(c: &mut Criterion) { powerset_n(c, 2); }
-
-fn powerset_4(c: &mut Criterion) { powerset_n(c, 4); }
-
-fn powerset_8(c: &mut Criterion) { powerset_n(c, 8); }
-
-fn powerset_12(c: &mut Criterion) { powerset_n(c, 12); }
-
-criterion_group!(
-    benches,
-    powerset_0,
-    powerset_1,
-    powerset_2,
-    powerset_4,
-    powerset_8,
-    powerset_12,
-);
-criterion_main!(benches);
\ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tree_fold1.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tree_fold1.rs
deleted file mode 100644
index f12995d..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tree_fold1.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use criterion::{criterion_group, criterion_main, Criterion};
-use itertools::{Itertools, cloned};
-
-trait IterEx : Iterator {
-    // Another efficient implementation against which to compare,
-    // but needs `std` so is less desirable.
-    fn tree_fold1_vec<F>(self, mut f: F) -> Option<Self::Item>
-        where F: FnMut(Self::Item, Self::Item) -> Self::Item,
-              Self: Sized,
-    {
-        let hint = self.size_hint().0;
-        let cap = std::mem::size_of::<usize>() * 8 - hint.leading_zeros() as usize;
-        let mut stack = Vec::with_capacity(cap);
-        self.enumerate().for_each(|(mut i, mut x)| {
-            while (i & 1) != 0 {
-                x = f(stack.pop().unwrap(), x);
-                i >>= 1;
-            }
-            stack.push(x);
-        });
-        stack.into_iter().fold1(f)
-    }
-}
-impl<T:Iterator> IterEx for T {}
-
-macro_rules! def_benchs {
-    ($N:expr,
-     $FUN:ident,
-     $BENCH_NAME:ident,
-     ) => (
-        mod $BENCH_NAME {
-            use super::*;
-
-            pub fn sum(c: &mut Criterion) {
-                let v: Vec<u32> = (0.. $N).collect();
-
-                c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " sum"), move |b| {
-                    b.iter(|| {
-                        cloned(&v).$FUN(|x, y| x + y)
-                    })
-                });
-            }
-
-            pub fn complex_iter(c: &mut Criterion) {
-                let u = (3..).take($N / 2);
-                let v = (5..).take($N / 2);
-                let it = u.chain(v);
-
-                c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " complex iter"), move |b| {
-                    b.iter(|| {
-                        it.clone().map(|x| x as f32).$FUN(f32::atan2)
-                    })
-                });
-            }
-
-            pub fn string_format(c: &mut Criterion) {
-                // This goes quadratic with linear `fold1`, so use a smaller
-                // size to not waste too much time in travis.  The allocations
-                // in here are so expensive anyway that it'll still take
-                // way longer per iteration than the other two benchmarks.
-                let v: Vec<u32> = (0.. ($N/4)).collect();
-
-                c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " string format"), move |b| {
-                    b.iter(|| {
-                        cloned(&v).map(|x| x.to_string()).$FUN(|x, y| format!("{} + {}", x, y))
-                    })
-                });
-            }
-        }
-
-        criterion_group!(
-            $BENCH_NAME,
-            $BENCH_NAME::sum,
-            $BENCH_NAME::complex_iter,
-            $BENCH_NAME::string_format,
-        );
-    )
-}
-
-def_benchs!{
-    10_000,
-    fold1,
-    fold1_10k,
-}
-
-def_benchs!{
-    10_000,
-    tree_fold1,
-    tree_fold1_stack_10k,
-}
-
-def_benchs!{
-    10_000,
-    tree_fold1_vec,
-    tree_fold1_vec_10k,
-}
-
-def_benchs!{
-    100,
-    fold1,
-    fold1_100,
-}
-
-def_benchs!{
-    100,
-    tree_fold1,
-    tree_fold1_stack_100,
-}
-
-def_benchs!{
-    100,
-    tree_fold1_vec,
-    tree_fold1_vec_100,
-}
-
-def_benchs!{
-    8,
-    fold1,
-    fold1_08,
-}
-
-def_benchs!{
-    8,
-    tree_fold1,
-    tree_fold1_stack_08,
-}
-
-def_benchs!{
-    8,
-    tree_fold1_vec,
-    tree_fold1_vec_08,
-}
-
-criterion_main!(
-    fold1_10k,
-    tree_fold1_stack_10k,
-    tree_fold1_vec_10k,
-    fold1_100,
-    tree_fold1_stack_100,
-    tree_fold1_vec_100,
-    fold1_08,
-    tree_fold1_stack_08,
-    tree_fold1_vec_08,
-);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/coalesce.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/coalesce.rs
deleted file mode 100644
index 3df7cc5..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/coalesce.rs
+++ /dev/null
@@ -1,235 +0,0 @@
-use std::fmt;
-use std::iter::FusedIterator;
-
-use crate::size_hint;
-
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct CoalesceBy<I, F, T>
-where
-    I: Iterator,
-{
-    iter: I,
-    last: Option<T>,
-    f: F,
-}
-
-impl<I: Clone, F: Clone, T: Clone> Clone for CoalesceBy<I, F, T>
-where
-    I: Iterator,
-{
-    clone_fields!(last, iter, f);
-}
-
-impl<I, F, T> fmt::Debug for CoalesceBy<I, F, T>
-where
-    I: Iterator + fmt::Debug,
-    T: fmt::Debug,
-{
-    debug_fmt_fields!(CoalesceBy, iter);
-}
-
-pub trait CoalescePredicate<Item, T> {
-    fn coalesce_pair(&mut self, t: T, item: Item) -> Result<T, (T, T)>;
-}
-
-impl<I, F, T> Iterator for CoalesceBy<I, F, T>
-where
-    I: Iterator,
-    F: CoalescePredicate<I::Item, T>,
-{
-    type Item = T;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        // this fuses the iterator
-        let last = self.last.take()?;
-
-        let self_last = &mut self.last;
-        let self_f = &mut self.f;
-        Some(
-            self.iter
-                .try_fold(last, |last, next| match self_f.coalesce_pair(last, next) {
-                    Ok(joined) => Ok(joined),
-                    Err((last_, next_)) => {
-                        *self_last = Some(next_);
-                        Err(last_)
-                    }
-                })
-                .unwrap_or_else(|x| x),
-        )
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let (low, hi) = size_hint::add_scalar(self.iter.size_hint(), self.last.is_some() as usize);
-        ((low > 0) as usize, hi)
-    }
-
-    fn fold<Acc, FnAcc>(self, acc: Acc, mut fn_acc: FnAcc) -> Acc
-    where
-        FnAcc: FnMut(Acc, Self::Item) -> Acc,
-    {
-        if let Some(last) = self.last {
-            let mut f = self.f;
-            let (last, acc) = self.iter.fold((last, acc), |(last, acc), elt| {
-                match f.coalesce_pair(last, elt) {
-                    Ok(joined) => (joined, acc),
-                    Err((last_, next_)) => (next_, fn_acc(acc, last_)),
-                }
-            });
-            fn_acc(acc, last)
-        } else {
-            acc
-        }
-    }
-}
-
-impl<I: Iterator, F: CoalescePredicate<I::Item, T>, T> FusedIterator for CoalesceBy<I, F, T> {}
-
-/// An iterator adaptor that may join together adjacent elements.
-///
-/// See [`.coalesce()`](crate::Itertools::coalesce) for more information.
-pub type Coalesce<I, F> = CoalesceBy<I, F, <I as Iterator>::Item>;
-
-impl<F, Item, T> CoalescePredicate<Item, T> for F
-where
-    F: FnMut(T, Item) -> Result<T, (T, T)>,
-{
-    fn coalesce_pair(&mut self, t: T, item: Item) -> Result<T, (T, T)> {
-        self(t, item)
-    }
-}
-
-/// Create a new `Coalesce`.
-pub fn coalesce<I, F>(mut iter: I, f: F) -> Coalesce<I, F>
-where
-    I: Iterator,
-{
-    Coalesce {
-        last: iter.next(),
-        iter,
-        f,
-    }
-}
-
-/// An iterator adaptor that removes repeated duplicates, determining equality using a comparison function.
-///
-/// See [`.dedup_by()`](crate::Itertools::dedup_by) or [`.dedup()`](crate::Itertools::dedup) for more information.
-pub type DedupBy<I, Pred> = CoalesceBy<I, DedupPred2CoalescePred<Pred>, <I as Iterator>::Item>;
-
-#[derive(Clone)]
-pub struct DedupPred2CoalescePred<DP>(DP);
-
-impl<DP> fmt::Debug for DedupPred2CoalescePred<DP> {
-    debug_fmt_fields!(DedupPred2CoalescePred,);
-}
-
-pub trait DedupPredicate<T> {
-    // TODO replace by Fn(&T, &T)->bool once Rust supports it
-    fn dedup_pair(&mut self, a: &T, b: &T) -> bool;
-}
-
-impl<DP, T> CoalescePredicate<T, T> for DedupPred2CoalescePred<DP>
-where
-    DP: DedupPredicate<T>,
-{
-    fn coalesce_pair(&mut self, t: T, item: T) -> Result<T, (T, T)> {
-        if self.0.dedup_pair(&t, &item) {
-            Ok(t)
-        } else {
-            Err((t, item))
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct DedupEq;
-
-impl<T: PartialEq> DedupPredicate<T> for DedupEq {
-    fn dedup_pair(&mut self, a: &T, b: &T) -> bool {
-        a == b
-    }
-}
-
-impl<T, F: FnMut(&T, &T) -> bool> DedupPredicate<T> for F {
-    fn dedup_pair(&mut self, a: &T, b: &T) -> bool {
-        self(a, b)
-    }
-}
-
-/// Create a new `DedupBy`.
-pub fn dedup_by<I, Pred>(mut iter: I, dedup_pred: Pred) -> DedupBy<I, Pred>
-where
-    I: Iterator,
-{
-    DedupBy {
-        last: iter.next(),
-        iter,
-        f: DedupPred2CoalescePred(dedup_pred),
-    }
-}
-
-/// An iterator adaptor that removes repeated duplicates.
-///
-/// See [`.dedup()`](crate::Itertools::dedup) for more information.
-pub type Dedup<I> = DedupBy<I, DedupEq>;
-
-/// Create a new `Dedup`.
-pub fn dedup<I>(iter: I) -> Dedup<I>
-where
-    I: Iterator,
-{
-    dedup_by(iter, DedupEq)
-}
-
-/// An iterator adaptor that removes repeated duplicates, while keeping a count of how many
-/// repeated elements were present. This will determine equality using a comparison function.
-///
-/// See [`.dedup_by_with_count()`](crate::Itertools::dedup_by_with_count) or
-/// [`.dedup_with_count()`](crate::Itertools::dedup_with_count) for more information.
-pub type DedupByWithCount<I, Pred> =
-    CoalesceBy<I, DedupPredWithCount2CoalescePred<Pred>, (usize, <I as Iterator>::Item)>;
-
-#[derive(Clone, Debug)]
-pub struct DedupPredWithCount2CoalescePred<DP>(DP);
-
-impl<DP, T> CoalescePredicate<T, (usize, T)> for DedupPredWithCount2CoalescePred<DP>
-where
-    DP: DedupPredicate<T>,
-{
-    fn coalesce_pair(
-        &mut self,
-        (c, t): (usize, T),
-        item: T,
-    ) -> Result<(usize, T), ((usize, T), (usize, T))> {
-        if self.0.dedup_pair(&t, &item) {
-            Ok((c + 1, t))
-        } else {
-            Err(((c, t), (1, item)))
-        }
-    }
-}
-
-/// An iterator adaptor that removes repeated duplicates, while keeping a count of how many
-/// repeated elements were present.
-///
-/// See [`.dedup_with_count()`](crate::Itertools::dedup_with_count) for more information.
-pub type DedupWithCount<I> = DedupByWithCount<I, DedupEq>;
-
-/// Create a new `DedupByWithCount`.
-pub fn dedup_by_with_count<I, Pred>(mut iter: I, dedup_pred: Pred) -> DedupByWithCount<I, Pred>
-where
-    I: Iterator,
-{
-    DedupByWithCount {
-        last: iter.next().map(|v| (1, v)),
-        iter,
-        f: DedupPredWithCount2CoalescePred(dedup_pred),
-    }
-}
-
-/// Create a new `DedupWithCount`.
-pub fn dedup_with_count<I>(iter: I) -> DedupWithCount<I>
-where
-    I: Iterator,
-{
-    dedup_by_with_count(iter, DedupEq)
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/mod.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/mod.rs
deleted file mode 100644
index 1695bbd..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/mod.rs
+++ /dev/null
@@ -1,1151 +0,0 @@
-//! Licensed under the Apache License, Version 2.0
-//! <https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-//! <https://opensource.org/licenses/MIT>, at your
-//! option. This file may not be copied, modified, or distributed
-//! except according to those terms.
-
-mod coalesce;
-mod map;
-mod multi_product;
-pub use self::coalesce::*;
-pub use self::map::{map_into, map_ok, MapInto, MapOk};
-#[allow(deprecated)]
-pub use self::map::MapResults;
-#[cfg(feature = "use_alloc")]
-pub use self::multi_product::*;
-
-use std::fmt;
-use std::iter::{Fuse, Peekable, FromIterator, FusedIterator};
-use std::marker::PhantomData;
-use crate::size_hint;
-
-/// An iterator adaptor that alternates elements from two iterators until both
-/// run out.
-///
-/// This iterator is *fused*.
-///
-/// See [`.interleave()`](crate::Itertools::interleave) for more information.
-#[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct Interleave<I, J> {
-    a: Fuse<I>,
-    b: Fuse<J>,
-    flag: bool,
-}
-
-/// Create an iterator that interleaves elements in `i` and `j`.
-///
-/// [`IntoIterator`] enabled version of `[Itertools::interleave]`.
-pub fn interleave<I, J>(i: I, j: J) -> Interleave<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
-    where I: IntoIterator,
-          J: IntoIterator<Item = I::Item>
-{
-    Interleave {
-        a: i.into_iter().fuse(),
-        b: j.into_iter().fuse(),
-        flag: false,
-    }
-}
-
-impl<I, J> Iterator for Interleave<I, J>
-    where I: Iterator,
-          J: Iterator<Item = I::Item>
-{
-    type Item = I::Item;
-    #[inline]
-    fn next(&mut self) -> Option<Self::Item> {
-        self.flag = !self.flag;
-        if self.flag {
-            match self.a.next() {
-                None => self.b.next(),
-                r => r,
-            }
-        } else {
-            match self.b.next() {
-                None => self.a.next(),
-                r => r,
-            }
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        size_hint::add(self.a.size_hint(), self.b.size_hint())
-    }
-}
-
-impl<I, J> FusedIterator for Interleave<I, J>
-    where I: Iterator,
-          J: Iterator<Item = I::Item>
-{}
-
-/// An iterator adaptor that alternates elements from the two iterators until
-/// one of them runs out.
-///
-/// This iterator is *fused*.
-///
-/// See [`.interleave_shortest()`](crate::Itertools::interleave_shortest)
-/// for more information.
-#[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct InterleaveShortest<I, J>
-    where I: Iterator,
-          J: Iterator<Item = I::Item>
-{
-    it0: I,
-    it1: J,
-    phase: bool, // false ==> it0, true ==> it1
-}
-
-/// Create a new `InterleaveShortest` iterator.
-pub fn interleave_shortest<I, J>(a: I, b: J) -> InterleaveShortest<I, J>
-    where I: Iterator,
-          J: Iterator<Item = I::Item>
-{
-    InterleaveShortest {
-        it0: a,
-        it1: b,
-        phase: false,
-    }
-}
-
-impl<I, J> Iterator for InterleaveShortest<I, J>
-    where I: Iterator,
-          J: Iterator<Item = I::Item>
-{
-    type Item = I::Item;
-
-    #[inline]
-    fn next(&mut self) -> Option<Self::Item> {
-        let e = if self.phase { self.it1.next() } else { self.it0.next() };
-        if e.is_some() {
-            self.phase = !self.phase;
-        }
-        e
-    }
-
-    #[inline]
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let (curr_hint, next_hint) = {
-            let it0_hint = self.it0.size_hint();
-            let it1_hint = self.it1.size_hint();
-            if self.phase {
-                (it1_hint, it0_hint)
-            } else {
-                (it0_hint, it1_hint)
-            }
-        };
-        let (curr_lower, curr_upper) = curr_hint;
-        let (next_lower, next_upper) = next_hint;
-        let (combined_lower, combined_upper) =
-            size_hint::mul_scalar(size_hint::min(curr_hint, next_hint), 2);
-        let lower =
-            if curr_lower > next_lower {
-                combined_lower + 1
-            } else {
-                combined_lower
-            };
-        let upper = {
-            let extra_elem = match (curr_upper, next_upper) {
-                (_, None) => false,
-                (None, Some(_)) => true,
-                (Some(curr_max), Some(next_max)) => curr_max > next_max,
-            };
-            if extra_elem {
-                combined_upper.and_then(|x| x.checked_add(1))
-            } else {
-                combined_upper
-            }
-        };
-        (lower, upper)
-    }
-}
-
-impl<I, J> FusedIterator for InterleaveShortest<I, J>
-    where I: FusedIterator,
-          J: FusedIterator<Item = I::Item>
-{}
-
-#[derive(Clone, Debug)]
-/// An iterator adaptor that allows putting back a single
-/// item to the front of the iterator.
-///
-/// Iterator element type is `I::Item`.
-pub struct PutBack<I>
-    where I: Iterator
-{
-    top: Option<I::Item>,
-    iter: I,
-}
-
-/// Create an iterator where you can put back a single item
-pub fn put_back<I>(iterable: I) -> PutBack<I::IntoIter>
-    where I: IntoIterator
-{
-    PutBack {
-        top: None,
-        iter: iterable.into_iter(),
-    }
-}
-
-impl<I> PutBack<I>
-    where I: Iterator
-{
-    /// put back value `value` (builder method)
-    pub fn with_value(mut self, value: I::Item) -> Self {
-        self.put_back(value);
-        self
-    }
-
-    /// Split the `PutBack` into its parts.
-    #[inline]
-    pub fn into_parts(self) -> (Option<I::Item>, I) {
-        let PutBack{top, iter} = self;
-        (top, iter)
-    }
-
-    /// Put back a single value to the front of the iterator.
-    ///
-    /// If a value is already in the put back slot, it is overwritten.
-    #[inline]
-    pub fn put_back(&mut self, x: I::Item) {
-        self.top = Some(x);
-    }
-}
-
-impl<I> Iterator for PutBack<I>
-    where I: Iterator
-{
-    type Item = I::Item;
-    #[inline]
-    fn next(&mut self) -> Option<Self::Item> {
-        match self.top {
-            None => self.iter.next(),
-            ref mut some => some.take(),
-        }
-    }
-    #[inline]
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        // Not ExactSizeIterator because size may be larger than usize
-        size_hint::add_scalar(self.iter.size_hint(), self.top.is_some() as usize)
-    }
-
-    fn count(self) -> usize {
-        self.iter.count() + (self.top.is_some() as usize)
-    }
-
-    fn last(self) -> Option<Self::Item> {
-        self.iter.last().or(self.top)
-    }
-
-    fn nth(&mut self, n: usize) -> Option<Self::Item> {
-        match self.top {
-            None => self.iter.nth(n),
-            ref mut some => {
-                if n == 0 {
-                    some.take()
-                } else {
-                    *some = None;
-                    self.iter.nth(n - 1)
-                }
-            }
-        }
-    }
-
-    fn all<G>(&mut self, mut f: G) -> bool
-        where G: FnMut(Self::Item) -> bool
-    {
-        if let Some(elt) = self.top.take() {
-            if !f(elt) {
-                return false;
-            }
-        }
-        self.iter.all(f)
-    }
-
-    fn fold<Acc, G>(mut self, init: Acc, mut f: G) -> Acc
-        where G: FnMut(Acc, Self::Item) -> Acc,
-    {
-        let mut accum = init;
-        if let Some(elt) = self.top.take() {
-            accum = f(accum, elt);
-        }
-        self.iter.fold(accum, f)
-    }
-}
-
-#[derive(Debug, Clone)]
-/// An iterator adaptor that iterates over the cartesian product of
-/// the element sets of two iterators `I` and `J`.
-///
-/// Iterator element type is `(I::Item, J::Item)`.
-///
-/// See [`.cartesian_product()`](crate::Itertools::cartesian_product) for more information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct Product<I, J>
-    where I: Iterator
-{
-    a: I,
-    a_cur: Option<I::Item>,
-    b: J,
-    b_orig: J,
-}
-
-/// Create a new cartesian product iterator
-///
-/// Iterator element type is `(I::Item, J::Item)`.
-pub fn cartesian_product<I, J>(mut i: I, j: J) -> Product<I, J>
-    where I: Iterator,
-          J: Clone + Iterator,
-          I::Item: Clone
-{
-    Product {
-        a_cur: i.next(),
-        a: i,
-        b: j.clone(),
-        b_orig: j,
-    }
-}
-
-impl<I, J> Iterator for Product<I, J>
-    where I: Iterator,
-          J: Clone + Iterator,
-          I::Item: Clone
-{
-    type Item = (I::Item, J::Item);
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let elt_b = match self.b.next() {
-            None => {
-                self.b = self.b_orig.clone();
-                match self.b.next() {
-                    None => return None,
-                    Some(x) => {
-                        self.a_cur = self.a.next();
-                        x
-                    }
-                }
-            }
-            Some(x) => x
-        };
-        self.a_cur.as_ref().map(|a| (a.clone(), elt_b))
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let has_cur = self.a_cur.is_some() as usize;
-        // Not ExactSizeIterator because size may be larger than usize
-        let (b_min, b_max) = self.b.size_hint();
-
-        // Compute a * b_orig + b for both lower and upper bound
-        size_hint::add(
-            size_hint::mul(self.a.size_hint(), self.b_orig.size_hint()),
-            (b_min * has_cur, b_max.map(move |x| x * has_cur)))
-    }
-
-    fn fold<Acc, G>(mut self, mut accum: Acc, mut f: G) -> Acc
-        where G: FnMut(Acc, Self::Item) -> Acc,
-    {
-        // use a split loop to handle the loose a_cur as well as avoiding to
-        // clone b_orig at the end.
-        if let Some(mut a) = self.a_cur.take() {
-            let mut b = self.b;
-            loop {
-                accum = b.fold(accum, |acc, elt| f(acc, (a.clone(), elt)));
-
-                // we can only continue iterating a if we had a first element;
-                if let Some(next_a) = self.a.next() {
-                    b = self.b_orig.clone();
-                    a = next_a;
-                } else {
-                    break;
-                }
-            }
-        }
-        accum
-    }
-}
-
-impl<I, J> FusedIterator for Product<I, J>
-    where I: FusedIterator,
-          J: Clone + FusedIterator,
-          I::Item: Clone
-{}
-
-/// A “meta iterator adaptor”. Its closure receives a reference to the iterator
-/// and may pick off as many elements as it likes, to produce the next iterator element.
-///
-/// Iterator element type is *X*, if the return type of `F` is *Option\<X\>*.
-///
-/// See [`.batching()`](crate::Itertools::batching) for more information.
-#[derive(Clone)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct Batching<I, F> {
-    f: F,
-    iter: I,
-}
-
-impl<I, F> fmt::Debug for Batching<I, F> where I: fmt::Debug {
-    debug_fmt_fields!(Batching, iter);
-}
-
-/// Create a new Batching iterator.
-pub fn batching<I, F>(iter: I, f: F) -> Batching<I, F> {
-    Batching { f, iter }
-}
-
-impl<B, F, I> Iterator for Batching<I, F>
-    where I: Iterator,
-          F: FnMut(&mut I) -> Option<B>
-{
-    type Item = B;
-    #[inline]
-    fn next(&mut self) -> Option<Self::Item> {
-        (self.f)(&mut self.iter)
-    }
-}
-
-/// An iterator adaptor that steps a number elements in the base iterator
-/// for each iteration.
-///
-/// The iterator steps by yielding the next element from the base iterator,
-/// then skipping forward *n-1* elements.
-///
-/// See [`.step()`](crate::Itertools::step) for more information.
-#[deprecated(note="Use std .step_by() instead", since="0.8.0")]
-#[allow(deprecated)]
-#[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct Step<I> {
-    iter: Fuse<I>,
-    skip: usize,
-}
-
-/// Create a `Step` iterator.
-///
-/// **Panics** if the step is 0.
-#[allow(deprecated)]
-pub fn step<I>(iter: I, step: usize) -> Step<I>
-    where I: Iterator
-{
-    assert!(step != 0);
-    Step {
-        iter: iter.fuse(),
-        skip: step - 1,
-    }
-}
-
-#[allow(deprecated)]
-impl<I> Iterator for Step<I>
-    where I: Iterator
-{
-    type Item = I::Item;
-    #[inline]
-    fn next(&mut self) -> Option<Self::Item> {
-        let elt = self.iter.next();
-        if self.skip > 0 {
-            self.iter.nth(self.skip - 1);
-        }
-        elt
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let (low, high) = self.iter.size_hint();
-        let div = |x: usize| {
-            if x == 0 {
-                0
-            } else {
-                1 + (x - 1) / (self.skip + 1)
-            }
-        };
-        (div(low), high.map(div))
-    }
-}
-
-// known size
-#[allow(deprecated)]
-impl<I> ExactSizeIterator for Step<I>
-    where I: ExactSizeIterator
-{}
-
-pub trait MergePredicate<T> {
-    fn merge_pred(&mut self, a: &T, b: &T) -> bool;
-}
-
-#[derive(Clone, Debug)]
-pub struct MergeLte;
-
-impl<T: PartialOrd> MergePredicate<T> for MergeLte {
-    fn merge_pred(&mut self, a: &T, b: &T) -> bool {
-        a <= b
-    }
-}
-
-/// An iterator adaptor that merges the two base iterators in ascending order.
-/// If both base iterators are sorted (ascending), the result is sorted.
-///
-/// Iterator element type is `I::Item`.
-///
-/// See [`.merge()`](crate::Itertools::merge_by) for more information.
-pub type Merge<I, J> = MergeBy<I, J, MergeLte>;
-
-/// Create an iterator that merges elements in `i` and `j`.
-///
-/// [`IntoIterator`] enabled version of [`Itertools::merge`](crate::Itertools::merge).
-///
-/// ```
-/// use itertools::merge;
-///
-/// for elt in merge(&[1, 2, 3], &[2, 3, 4]) {
-///     /* loop body */
-/// }
-/// ```
-pub fn merge<I, J>(i: I, j: J) -> Merge<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
-    where I: IntoIterator,
-          J: IntoIterator<Item = I::Item>,
-          I::Item: PartialOrd
-{
-    merge_by_new(i, j, MergeLte)
-}
-
-/// An iterator adaptor that merges the two base iterators in ascending order.
-/// If both base iterators are sorted (ascending), the result is sorted.
-///
-/// Iterator element type is `I::Item`.
-///
-/// See [`.merge_by()`](crate::Itertools::merge_by) for more information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct MergeBy<I, J, F>
-    where I: Iterator,
-          J: Iterator<Item = I::Item>
-{
-    a: Peekable<I>,
-    b: Peekable<J>,
-    fused: Option<bool>,
-    cmp: F,
-}
-
-impl<I, J, F> fmt::Debug for MergeBy<I, J, F>
-    where I: Iterator + fmt::Debug, J: Iterator<Item = I::Item> + fmt::Debug,
-          I::Item: fmt::Debug,
-{
-    debug_fmt_fields!(MergeBy, a, b);
-}
-
-impl<T, F: FnMut(&T, &T)->bool> MergePredicate<T> for F {
-    fn merge_pred(&mut self, a: &T, b: &T) -> bool {
-        self(a, b)
-    }
-}
-
-/// Create a `MergeBy` iterator.
-pub fn merge_by_new<I, J, F>(a: I, b: J, cmp: F) -> MergeBy<I::IntoIter, J::IntoIter, F>
-    where I: IntoIterator,
-          J: IntoIterator<Item = I::Item>,
-          F: MergePredicate<I::Item>,
-{
-    MergeBy {
-        a: a.into_iter().peekable(),
-        b: b.into_iter().peekable(),
-        fused: None,
-        cmp,
-    }
-}
-
-impl<I, J, F> Clone for MergeBy<I, J, F>
-    where I: Iterator,
-          J: Iterator<Item = I::Item>,
-          Peekable<I>: Clone,
-          Peekable<J>: Clone,
-          F: Clone
-{
-    clone_fields!(a, b, fused, cmp);
-}
-
-impl<I, J, F> Iterator for MergeBy<I, J, F>
-    where I: Iterator,
-          J: Iterator<Item = I::Item>,
-          F: MergePredicate<I::Item>
-{
-    type Item = I::Item;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let less_than = match self.fused {
-            Some(lt) => lt,
-            None => match (self.a.peek(), self.b.peek()) {
-                (Some(a), Some(b)) => self.cmp.merge_pred(a, b),
-                (Some(_), None) => {
-                    self.fused = Some(true);
-                    true
-                }
-                (None, Some(_)) => {
-                    self.fused = Some(false);
-                    false
-                }
-                (None, None) => return None,
-            }
-        };
-        if less_than {
-            self.a.next()
-        } else {
-            self.b.next()
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        // Not ExactSizeIterator because size may be larger than usize
-        size_hint::add(self.a.size_hint(), self.b.size_hint())
-    }
-}
-
-impl<I, J, F> FusedIterator for MergeBy<I, J, F>
-    where I: FusedIterator,
-          J: FusedIterator<Item = I::Item>,
-          F: MergePredicate<I::Item>
-{}
-
-/// An iterator adaptor that borrows from a `Clone`-able iterator
-/// to only pick off elements while the predicate returns `true`.
-///
-/// See [`.take_while_ref()`](crate::Itertools::take_while_ref) for more information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct TakeWhileRef<'a, I: 'a, F> {
-    iter: &'a mut I,
-    f: F,
-}
-
-impl<'a, I, F> fmt::Debug for TakeWhileRef<'a, I, F>
-    where I: Iterator + fmt::Debug,
-{
-    debug_fmt_fields!(TakeWhileRef, iter);
-}
-
-/// Create a new `TakeWhileRef` from a reference to clonable iterator.
-pub fn take_while_ref<I, F>(iter: &mut I, f: F) -> TakeWhileRef<I, F>
-    where I: Iterator + Clone
-{
-    TakeWhileRef { iter, f }
-}
-
-impl<'a, I, F> Iterator for TakeWhileRef<'a, I, F>
-    where I: Iterator + Clone,
-          F: FnMut(&I::Item) -> bool
-{
-    type Item = I::Item;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let old = self.iter.clone();
-        match self.iter.next() {
-            None => None,
-            Some(elt) => {
-                if (self.f)(&elt) {
-                    Some(elt)
-                } else {
-                    *self.iter = old;
-                    None
-                }
-            }
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        (0, self.iter.size_hint().1)
-    }
-}
-
-/// An iterator adaptor that filters `Option<A>` iterator elements
-/// and produces `A`. Stops on the first `None` encountered.
-///
-/// See [`.while_some()`](crate::Itertools::while_some) for more information.
-#[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct WhileSome<I> {
-    iter: I,
-}
-
-/// Create a new `WhileSome<I>`.
-pub fn while_some<I>(iter: I) -> WhileSome<I> {
-    WhileSome { iter }
-}
-
-impl<I, A> Iterator for WhileSome<I>
-    where I: Iterator<Item = Option<A>>
-{
-    type Item = A;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        match self.iter.next() {
-            None | Some(None) => None,
-            Some(elt) => elt,
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        (0, self.iter.size_hint().1)
-    }
-}
-
-/// An iterator to iterate through all combinations in a `Clone`-able iterator that produces tuples
-/// of a specific size.
-///
-/// See [`.tuple_combinations()`](crate::Itertools::tuple_combinations) for more
-/// information.
-#[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct TupleCombinations<I, T>
-    where I: Iterator,
-          T: HasCombination<I>
-{
-    iter: T::Combination,
-    _mi: PhantomData<I>,
-}
-
-pub trait HasCombination<I>: Sized {
-    type Combination: From<I> + Iterator<Item = Self>;
-}
-
-/// Create a new `TupleCombinations` from a clonable iterator.
-pub fn tuple_combinations<T, I>(iter: I) -> TupleCombinations<I, T>
-    where I: Iterator + Clone,
-          I::Item: Clone,
-          T: HasCombination<I>,
-{
-    TupleCombinations {
-        iter: T::Combination::from(iter),
-        _mi: PhantomData,
-    }
-}
-
-impl<I, T> Iterator for TupleCombinations<I, T>
-    where I: Iterator,
-          T: HasCombination<I>,
-{
-    type Item = T;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next()
-    }
-}
-
-impl<I, T> FusedIterator for TupleCombinations<I, T>
-    where I: FusedIterator,
-          T: HasCombination<I>,
-{}
-
-#[derive(Clone, Debug)]
-pub struct Tuple1Combination<I> {
-    iter: I,
-}
-
-impl<I> From<I> for Tuple1Combination<I> {
-    fn from(iter: I) -> Self {
-        Tuple1Combination { iter }
-    }
-}
-
-impl<I: Iterator> Iterator for Tuple1Combination<I> {
-    type Item = (I::Item,);
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next().map(|x| (x,))
-    }
-}
-
-impl<I: Iterator> HasCombination<I> for (I::Item,) {
-    type Combination = Tuple1Combination<I>;
-}
-
-macro_rules! impl_tuple_combination {
-    ($C:ident $P:ident ; $($X:ident)*) => (
-        #[derive(Clone, Debug)]
-        pub struct $C<I: Iterator> {
-            item: Option<I::Item>,
-            iter: I,
-            c: $P<I>,
-        }
-
-        impl<I: Iterator + Clone> From<I> for $C<I> {
-            fn from(mut iter: I) -> Self {
-                Self {
-                    item: iter.next(),
-                    iter: iter.clone(),
-                    c: iter.into(),
-                }
-            }
-        }
-
-        impl<I: Iterator + Clone> From<I> for $C<Fuse<I>> {
-            fn from(iter: I) -> Self {
-                Self::from(iter.fuse())
-            }
-        }
-
-        impl<I, A> Iterator for $C<I>
-            where I: Iterator<Item = A> + Clone,
-                  I::Item: Clone
-        {
-            type Item = (A, $(ignore_ident!($X, A)),*);
-
-            fn next(&mut self) -> Option<Self::Item> {
-                if let Some(($($X),*,)) = self.c.next() {
-                    let z = self.item.clone().unwrap();
-                    Some((z, $($X),*))
-                } else {
-                    self.item = self.iter.next();
-                    self.item.clone().and_then(|z| {
-                        self.c = self.iter.clone().into();
-                        self.c.next().map(|($($X),*,)| (z, $($X),*))
-                    })
-                }
-            }
-        }
-
-        impl<I, A> HasCombination<I> for (A, $(ignore_ident!($X, A)),*)
-            where I: Iterator<Item = A> + Clone,
-                  I::Item: Clone
-        {
-            type Combination = $C<Fuse<I>>;
-        }
-    )
-}
-
-// This snippet generates the twelve `impl_tuple_combination!` invocations:
-//    use core::iter;
-//    use itertools::Itertools;
-//
-//    for i in 2..=12 {
-//        println!("impl_tuple_combination!(Tuple{arity}Combination Tuple{prev}Combination; {idents});",
-//            arity = i,
-//            prev = i - 1,
-//            idents = ('a'..'z').take(i - 1).join(" "),
-//        );
-//    }
-// It could probably be replaced by a bit more macro cleverness.
-impl_tuple_combination!(Tuple2Combination Tuple1Combination; a);
-impl_tuple_combination!(Tuple3Combination Tuple2Combination; a b);
-impl_tuple_combination!(Tuple4Combination Tuple3Combination; a b c);
-impl_tuple_combination!(Tuple5Combination Tuple4Combination; a b c d);
-impl_tuple_combination!(Tuple6Combination Tuple5Combination; a b c d e);
-impl_tuple_combination!(Tuple7Combination Tuple6Combination; a b c d e f);
-impl_tuple_combination!(Tuple8Combination Tuple7Combination; a b c d e f g);
-impl_tuple_combination!(Tuple9Combination Tuple8Combination; a b c d e f g h);
-impl_tuple_combination!(Tuple10Combination Tuple9Combination; a b c d e f g h i);
-impl_tuple_combination!(Tuple11Combination Tuple10Combination; a b c d e f g h i j);
-impl_tuple_combination!(Tuple12Combination Tuple11Combination; a b c d e f g h i j k);
-
-/// An iterator adapter to filter values within a nested `Result::Ok`.
-///
-/// See [`.filter_ok()`](crate::Itertools::filter_ok) for more information.
-#[derive(Clone)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct FilterOk<I, F> {
-    iter: I,
-    f: F
-}
-
-impl<I, F> fmt::Debug for FilterOk<I, F>
-where
-    I: fmt::Debug,
-{
-    debug_fmt_fields!(FilterOk, iter);
-}
-
-/// Create a new `FilterOk` iterator.
-pub fn filter_ok<I, F, T, E>(iter: I, f: F) -> FilterOk<I, F>
-    where I: Iterator<Item = Result<T, E>>,
-          F: FnMut(&T) -> bool,
-{
-    FilterOk {
-        iter,
-        f,
-    }
-}
-
-impl<I, F, T, E> Iterator for FilterOk<I, F>
-    where I: Iterator<Item = Result<T, E>>,
-          F: FnMut(&T) -> bool,
-{
-    type Item = Result<T, E>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        loop {
-            match self.iter.next() {
-                Some(Ok(v)) => {
-                    if (self.f)(&v) {
-                        return Some(Ok(v));
-                    }
-                },
-                Some(Err(e)) => return Some(Err(e)),
-                None => return None,
-            }
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        (0, self.iter.size_hint().1)
-    }
-
-    fn fold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc
-        where Fold: FnMut(Acc, Self::Item) -> Acc,
-    {
-        let mut f = self.f;
-        self.iter.filter(|v| {
-            v.as_ref().map(&mut f).unwrap_or(true)
-        }).fold(init, fold_f)
-    }
-
-    fn collect<C>(self) -> C
-        where C: FromIterator<Self::Item>
-    {
-        let mut f = self.f;
-        self.iter.filter(|v| {
-            v.as_ref().map(&mut f).unwrap_or(true)
-        }).collect()
-    }
-}
-
-impl<I, F, T, E> FusedIterator for FilterOk<I, F>
-    where I: FusedIterator<Item = Result<T, E>>,
-          F: FnMut(&T) -> bool,
-{}
-
-/// An iterator adapter to filter and apply a transformation on values within a nested `Result::Ok`.
-///
-/// See [`.filter_map_ok()`](crate::Itertools::filter_map_ok) for more information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct FilterMapOk<I, F> {
-    iter: I,
-    f: F
-}
-
-impl<I, F> fmt::Debug for FilterMapOk<I, F>
-where
-    I: fmt::Debug,
-{
-    debug_fmt_fields!(FilterMapOk, iter);
-}
-
-fn transpose_result<T, E>(result: Result<Option<T>, E>) -> Option<Result<T, E>> {
-    match result {
-        Ok(Some(v)) => Some(Ok(v)),
-        Ok(None) => None,
-        Err(e) => Some(Err(e)),
-    }
-}
-
-/// Create a new `FilterOk` iterator.
-pub fn filter_map_ok<I, F, T, U, E>(iter: I, f: F) -> FilterMapOk<I, F>
-    where I: Iterator<Item = Result<T, E>>,
-          F: FnMut(T) -> Option<U>,
-{
-    FilterMapOk {
-        iter,
-        f,
-    }
-}
-
-impl<I, F, T, U, E> Iterator for FilterMapOk<I, F>
-    where I: Iterator<Item = Result<T, E>>,
-          F: FnMut(T) -> Option<U>,
-{
-    type Item = Result<U, E>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        loop {
-            match self.iter.next() {
-                Some(Ok(v)) => {
-                    if let Some(v) = (self.f)(v) {
-                        return Some(Ok(v));
-                    }
-                },
-                Some(Err(e)) => return Some(Err(e)),
-                None => return None,
-            }
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        (0, self.iter.size_hint().1)
-    }
-
-    fn fold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc
-        where Fold: FnMut(Acc, Self::Item) -> Acc,
-    {
-        let mut f = self.f;
-        self.iter.filter_map(|v| {
-            transpose_result(v.map(&mut f))
-        }).fold(init, fold_f)
-    }
-
-    fn collect<C>(self) -> C
-        where C: FromIterator<Self::Item>
-    {
-        let mut f = self.f;
-        self.iter.filter_map(|v| {
-            transpose_result(v.map(&mut f))
-        }).collect()
-    }
-}
-
-impl<I, F, T, U, E> FusedIterator for FilterMapOk<I, F>
-    where I: FusedIterator<Item = Result<T, E>>,
-          F: FnMut(T) -> Option<U>,
-{}
-
-/// An iterator adapter to get the positions of each element that matches a predicate.
-///
-/// See [`.positions()`](crate::Itertools::positions) for more information.
-#[derive(Clone)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct Positions<I, F> {
-    iter: I,
-    f: F,
-    count: usize,
-}
-
-impl<I, F> fmt::Debug for Positions<I, F>
-where
-    I: fmt::Debug,
-{
-    debug_fmt_fields!(Positions, iter, count);
-}
-
-/// Create a new `Positions` iterator.
-pub fn positions<I, F>(iter: I, f: F) -> Positions<I, F>
-    where I: Iterator,
-          F: FnMut(I::Item) -> bool,
-{
-    Positions {
-        iter,
-        f,
-        count: 0
-    }
-}
-
-impl<I, F> Iterator for Positions<I, F>
-    where I: Iterator,
-          F: FnMut(I::Item) -> bool,
-{
-    type Item = usize;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        while let Some(v) = self.iter.next() {
-            let i = self.count;
-            self.count = i + 1;
-            if (self.f)(v) {
-                return Some(i);
-            }
-        }
-        None
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        (0, self.iter.size_hint().1)
-    }
-}
-
-impl<I, F> DoubleEndedIterator for Positions<I, F>
-    where I: DoubleEndedIterator + ExactSizeIterator,
-          F: FnMut(I::Item) -> bool,
-{
-    fn next_back(&mut self) -> Option<Self::Item> {
-        while let Some(v) = self.iter.next_back() {
-            if (self.f)(v) {
-                return Some(self.count + self.iter.len())
-            }
-        }
-        None
-    }
-}
-
-impl<I, F> FusedIterator for Positions<I, F>
-    where I: FusedIterator,
-          F: FnMut(I::Item) -> bool,
-{}
-
-/// An iterator adapter to apply a mutating function to each element before yielding it.
-///
-/// See [`.update()`](crate::Itertools::update) for more information.
-#[derive(Clone)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct Update<I, F> {
-    iter: I,
-    f: F,
-}
-
-impl<I, F> fmt::Debug for Update<I, F>
-where
-    I: fmt::Debug,
-{
-    debug_fmt_fields!(Update, iter);
-}
-
-/// Create a new `Update` iterator.
-pub fn update<I, F>(iter: I, f: F) -> Update<I, F>
-where
-    I: Iterator,
-    F: FnMut(&mut I::Item),
-{
-    Update { iter, f }
-}
-
-impl<I, F> Iterator for Update<I, F>
-where
-    I: Iterator,
-    F: FnMut(&mut I::Item),
-{
-    type Item = I::Item;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if let Some(mut v) = self.iter.next() {
-            (self.f)(&mut v);
-            Some(v)
-        } else {
-            None
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.iter.size_hint()
-    }
-
-    fn fold<Acc, G>(self, init: Acc, mut g: G) -> Acc
-        where G: FnMut(Acc, Self::Item) -> Acc,
-    {
-        let mut f = self.f;
-        self.iter.fold(init, move |acc, mut v| { f(&mut v); g(acc, v) })
-    }
-
-    // if possible, re-use inner iterator specializations in collect
-    fn collect<C>(self) -> C
-        where C: FromIterator<Self::Item>
-    {
-        let mut f = self.f;
-        self.iter.map(move |mut v| { f(&mut v); v }).collect()
-    }
-}
-
-impl<I, F> ExactSizeIterator for Update<I, F>
-where
-    I: ExactSizeIterator,
-    F: FnMut(&mut I::Item),
-{}
-
-impl<I, F> DoubleEndedIterator for Update<I, F>
-where
-    I: DoubleEndedIterator,
-    F: FnMut(&mut I::Item),
-{
-    fn next_back(&mut self) -> Option<Self::Item> {
-        if let Some(mut v) = self.iter.next_back() {
-            (self.f)(&mut v);
-            Some(v)
-        } else {
-            None
-        }
-    }
-}
-
-impl<I, F> FusedIterator for Update<I, F>
-where
-    I: FusedIterator,
-    F: FnMut(&mut I::Item),
-{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/multi_product.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/multi_product.rs
deleted file mode 100644
index 0b38406..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/multi_product.rs
+++ /dev/null
@@ -1,230 +0,0 @@
-#![cfg(feature = "use_alloc")]
-
-use crate::size_hint;
-use crate::Itertools;
-
-use alloc::vec::Vec;
-
-#[derive(Clone)]
-/// An iterator adaptor that iterates over the cartesian product of
-/// multiple iterators of type `I`.
-///
-/// An iterator element type is `Vec<I>`.
-///
-/// See [`.multi_cartesian_product()`](crate::Itertools::multi_cartesian_product)
-/// for more information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct MultiProduct<I>(Vec<MultiProductIter<I>>)
-    where I: Iterator + Clone,
-          I::Item: Clone;
-
-impl<I> std::fmt::Debug for MultiProduct<I>
-where
-    I: Iterator + Clone + std::fmt::Debug,
-    I::Item: Clone + std::fmt::Debug,
-{
-    debug_fmt_fields!(CoalesceBy, 0);
-}
-
-/// Create a new cartesian product iterator over an arbitrary number
-/// of iterators of the same type.
-///
-/// Iterator element is of type `Vec<H::Item::Item>`.
-pub fn multi_cartesian_product<H>(iters: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter>
-    where H: Iterator,
-          H::Item: IntoIterator,
-          <H::Item as IntoIterator>::IntoIter: Clone,
-          <H::Item as IntoIterator>::Item: Clone
-{
-    MultiProduct(iters.map(|i| MultiProductIter::new(i.into_iter())).collect())
-}
-
-#[derive(Clone, Debug)]
-/// Holds the state of a single iterator within a `MultiProduct`.
-struct MultiProductIter<I>
-    where I: Iterator + Clone,
-          I::Item: Clone
-{
-    cur: Option<I::Item>,
-    iter: I,
-    iter_orig: I,
-}
-
-/// Holds the current state during an iteration of a `MultiProduct`.
-#[derive(Debug)]
-enum MultiProductIterState {
-    StartOfIter,
-    MidIter { on_first_iter: bool },
-}
-
-impl<I> MultiProduct<I>
-    where I: Iterator + Clone,
-          I::Item: Clone
-{
-    /// Iterates the rightmost iterator, then recursively iterates iterators
-    /// to the left if necessary.
-    ///
-    /// Returns true if the iteration succeeded, else false.
-    fn iterate_last(
-        multi_iters: &mut [MultiProductIter<I>],
-        mut state: MultiProductIterState
-    ) -> bool {
-        use self::MultiProductIterState::*;
-
-        if let Some((last, rest)) = multi_iters.split_last_mut() {
-            let on_first_iter = match state {
-                StartOfIter => {
-                    let on_first_iter = !last.in_progress();
-                    state = MidIter { on_first_iter };
-                    on_first_iter
-                },
-                MidIter { on_first_iter } => on_first_iter
-            };
-
-            if !on_first_iter {
-                last.iterate();
-            }
-
-            if last.in_progress() {
-                true
-            } else if MultiProduct::iterate_last(rest, state) {
-                last.reset();
-                last.iterate();
-                // If iterator is None twice consecutively, then iterator is
-                // empty; whole product is empty.
-                last.in_progress()
-            } else {
-                false
-            }
-        } else {
-            // Reached end of iterator list. On initialisation, return true.
-            // At end of iteration (final iterator finishes), finish.
-            match state {
-                StartOfIter => false,
-                MidIter { on_first_iter } => on_first_iter
-            }
-        }
-    }
-
-    /// Returns the unwrapped value of the next iteration.
-    fn curr_iterator(&self) -> Vec<I::Item> {
-        self.0.iter().map(|multi_iter| {
-            multi_iter.cur.clone().unwrap()
-        }).collect()
-    }
-
-    /// Returns true if iteration has started and has not yet finished; false
-    /// otherwise.
-    fn in_progress(&self) -> bool {
-        if let Some(last) = self.0.last() {
-            last.in_progress()
-        } else {
-            false
-        }
-    }
-}
-
-impl<I> MultiProductIter<I>
-    where I: Iterator + Clone,
-          I::Item: Clone
-{
-    fn new(iter: I) -> Self {
-        MultiProductIter {
-            cur: None,
-            iter: iter.clone(),
-            iter_orig: iter
-        }
-    }
-
-    /// Iterate the managed iterator.
-    fn iterate(&mut self) {
-        self.cur = self.iter.next();
-    }
-
-    /// Reset the managed iterator.
-    fn reset(&mut self) {
-        self.iter = self.iter_orig.clone();
-    }
-
-    /// Returns true if the current iterator has been started and has not yet
-    /// finished; false otherwise.
-    fn in_progress(&self) -> bool {
-        self.cur.is_some()
-    }
-}
-
-impl<I> Iterator for MultiProduct<I>
-    where I: Iterator + Clone,
-          I::Item: Clone
-{
-    type Item = Vec<I::Item>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if MultiProduct::iterate_last(
-            &mut self.0,
-            MultiProductIterState::StartOfIter
-        ) {
-            Some(self.curr_iterator())
-        } else {
-            None
-        }
-    }
-
-    fn count(self) -> usize {
-        if self.0.is_empty() {
-            return 0;
-        }
-
-        if !self.in_progress() {
-            return self.0.into_iter().fold(1, |acc, multi_iter| {
-                acc * multi_iter.iter.count()
-            });
-        }
-
-        self.0.into_iter().fold(
-            0,
-            |acc, MultiProductIter { iter, iter_orig, cur: _ }| {
-                let total_count = iter_orig.count();
-                let cur_count = iter.count();
-                acc * total_count + cur_count
-            }
-        )
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        // Not ExactSizeIterator because size may be larger than usize
-        if self.0.is_empty() {
-            return (0, Some(0));
-        }
-
-        if !self.in_progress() {
-            return self.0.iter().fold((1, Some(1)), |acc, multi_iter| {
-                size_hint::mul(acc, multi_iter.iter.size_hint())
-            });
-        }
-
-        self.0.iter().fold(
-            (0, Some(0)),
-            |acc, &MultiProductIter { ref iter, ref iter_orig, cur: _ }| {
-                let cur_size = iter.size_hint();
-                let total_size = iter_orig.size_hint();
-                size_hint::add(size_hint::mul(acc, total_size), cur_size)
-            }
-        )
-    }
-
-    fn last(self) -> Option<Self::Item> {
-        let iter_count = self.0.len();
-
-        let lasts: Self::Item = self.0.into_iter()
-            .map(|multi_iter| multi_iter.iter.last())
-            .while_some()
-            .collect();
-
-        if lasts.len() == iter_count {
-            Some(lasts)
-        } else {
-            None
-        }
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations.rs
deleted file mode 100644
index 68a59c5..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations.rs
+++ /dev/null
@@ -1,128 +0,0 @@
-use std::fmt;
-use std::iter::FusedIterator;
-
-use super::lazy_buffer::LazyBuffer;
-use alloc::vec::Vec;
-
-/// An iterator to iterate through all the `k`-length combinations in an iterator.
-///
-/// See [`.combinations()`](crate::Itertools::combinations) for more information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct Combinations<I: Iterator> {
-    indices: Vec<usize>,
-    pool: LazyBuffer<I>,
-    first: bool,
-}
-
-impl<I> Clone for Combinations<I>
-    where I: Clone + Iterator,
-          I::Item: Clone,
-{
-    clone_fields!(indices, pool, first);
-}
-
-impl<I> fmt::Debug for Combinations<I>
-    where I: Iterator + fmt::Debug,
-          I::Item: fmt::Debug,
-{
-    debug_fmt_fields!(Combinations, indices, pool, first);
-}
-
-/// Create a new `Combinations` from a clonable iterator.
-pub fn combinations<I>(iter: I, k: usize) -> Combinations<I>
-    where I: Iterator
-{
-    let mut pool = LazyBuffer::new(iter);
-    pool.prefill(k);
-
-    Combinations {
-        indices: (0..k).collect(),
-        pool,
-        first: true,
-    }
-}
-
-impl<I: Iterator> Combinations<I> {
-    /// Returns the length of a combination produced by this iterator.
-    #[inline]
-    pub fn k(&self) -> usize { self.indices.len() }
-
-    /// Returns the (current) length of the pool from which combination elements are
-    /// selected. This value can change between invocations of [`next`](Combinations::next).
-    #[inline]
-    pub fn n(&self) -> usize { self.pool.len() }
-
-    /// Returns a reference to the source iterator.
-    #[inline]
-    pub(crate) fn src(&self) -> &I { &self.pool.it }
-
-    /// Resets this `Combinations` back to an initial state for combinations of length
-    /// `k` over the same pool data source. If `k` is larger than the current length
-    /// of the data pool an attempt is made to prefill the pool so that it holds `k`
-    /// elements.
-    pub(crate) fn reset(&mut self, k: usize) {
-        self.first = true;
-
-        if k < self.indices.len() {
-            self.indices.truncate(k);
-            for i in 0..k {
-                self.indices[i] = i;
-            }
-
-        } else {
-            for i in 0..self.indices.len() {
-                self.indices[i] = i;
-            }
-            self.indices.extend(self.indices.len()..k);
-            self.pool.prefill(k);
-        }
-    }
-}
-
-impl<I> Iterator for Combinations<I>
-    where I: Iterator,
-          I::Item: Clone
-{
-    type Item = Vec<I::Item>;
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.first {
-            if self.k() > self.n() {
-                return None;
-            }
-            self.first = false;
-        } else if self.indices.is_empty() {
-            return None;
-        } else {
-            // Scan from the end, looking for an index to increment
-            let mut i: usize = self.indices.len() - 1;
-
-            // Check if we need to consume more from the iterator
-            if self.indices[i] == self.pool.len() - 1 {
-                self.pool.get_next(); // may change pool size
-            }
-
-            while self.indices[i] == i + self.pool.len() - self.indices.len() {
-                if i > 0 {
-                    i -= 1;
-                } else {
-                    // Reached the last combination
-                    return None;
-                }
-            }
-
-            // Increment index, and reset the ones to its right
-            self.indices[i] += 1;
-            for j in i+1..self.indices.len() {
-                self.indices[j] = self.indices[j - 1] + 1;
-            }
-        }
-
-        // Create result vector based on the indices
-        Some(self.indices.iter().map(|i| self.pool[*i].clone()).collect())
-    }
-}
-
-impl<I> FusedIterator for Combinations<I>
-    where I: Iterator,
-          I::Item: Clone
-{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations_with_replacement.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations_with_replacement.rs
deleted file mode 100644
index 0fec967..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations_with_replacement.rs
+++ /dev/null
@@ -1,109 +0,0 @@
-use alloc::vec::Vec;
-use std::fmt;
-use std::iter::FusedIterator;
-
-use super::lazy_buffer::LazyBuffer;
-
-/// An iterator to iterate through all the `n`-length combinations in an iterator, with replacement.
-///
-/// See [`.combinations_with_replacement()`](crate::Itertools::combinations_with_replacement)
-/// for more information.
-#[derive(Clone)]
-pub struct CombinationsWithReplacement<I>
-where
-    I: Iterator,
-    I::Item: Clone,
-{
-    indices: Vec<usize>,
-    pool: LazyBuffer<I>,
-    first: bool,
-}
-
-impl<I> fmt::Debug for CombinationsWithReplacement<I>
-where
-    I: Iterator + fmt::Debug,
-    I::Item: fmt::Debug + Clone,
-{
-    debug_fmt_fields!(Combinations, indices, pool, first);
-}
-
-impl<I> CombinationsWithReplacement<I>
-where
-    I: Iterator,
-    I::Item: Clone,
-{
-    /// Map the current mask over the pool to get an output combination
-    fn current(&self) -> Vec<I::Item> {
-        self.indices.iter().map(|i| self.pool[*i].clone()).collect()
-    }
-}
-
-/// Create a new `CombinationsWithReplacement` from a clonable iterator.
-pub fn combinations_with_replacement<I>(iter: I, k: usize) -> CombinationsWithReplacement<I>
-where
-    I: Iterator,
-    I::Item: Clone,
-{
-    let indices: Vec<usize> = alloc::vec![0; k];
-    let pool: LazyBuffer<I> = LazyBuffer::new(iter);
-
-    CombinationsWithReplacement {
-        indices,
-        pool,
-        first: true,
-    }
-}
-
-impl<I> Iterator for CombinationsWithReplacement<I>
-where
-    I: Iterator,
-    I::Item: Clone,
-{
-    type Item = Vec<I::Item>;
-    fn next(&mut self) -> Option<Self::Item> {
-        // If this is the first iteration, return early
-        if self.first {
-            // In empty edge cases, stop iterating immediately
-            return if !(self.indices.is_empty() || self.pool.get_next()) {
-                None
-            // Otherwise, yield the initial state
-            } else {
-                self.first = false;
-                Some(self.current())
-            };
-        }
-
-        // Check if we need to consume more from the iterator
-        // This will run while we increment our first index digit
-        self.pool.get_next();
-
-        // Work out where we need to update our indices
-        let mut increment: Option<(usize, usize)> = None;
-        for (i, indices_int) in self.indices.iter().enumerate().rev() {
-            if *indices_int < self.pool.len()-1 {
-                increment = Some((i, indices_int + 1));
-                break;
-            }
-        }
-
-        match increment {
-            // If we can update the indices further
-            Some((increment_from, increment_value)) => {
-                // We need to update the rightmost non-max value
-                // and all those to the right
-                for indices_index in increment_from..self.indices.len() {
-                    self.indices[indices_index] = increment_value;
-                }
-                Some(self.current())
-            }
-            // Otherwise, we're done
-            None => None,
-        }
-    }
-}
-
-impl<I> FusedIterator for CombinationsWithReplacement<I>
-where
-    I: Iterator,
-    I::Item: Clone,
-{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/concat_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/concat_impl.rs
deleted file mode 100644
index f022ec9..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/concat_impl.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-use crate::Itertools;
-
-/// Combine all an iterator's elements into one element by using [`Extend`].
-///
-/// [`IntoIterator`]-enabled version of [`Itertools::concat`].
-///
-/// This combinator will extend the first item with each of the rest of the
-/// items of the iterator. If the iterator is empty, the default value of
-/// `I::Item` is returned.
-///
-/// ```rust
-/// use itertools::concat;
-/// 
-/// let input = vec![vec![1], vec![2, 3], vec![4, 5, 6]];
-/// assert_eq!(concat(input), vec![1, 2, 3, 4, 5, 6]);
-/// ```
-pub fn concat<I>(iterable: I) -> I::Item
-    where I: IntoIterator,
-          I::Item: Extend<<<I as IntoIterator>::Item as IntoIterator>::Item> + IntoIterator + Default
-{
-    #[allow(deprecated)] //TODO: once msrv hits 1.51. replace `fold1` with `reduce`
-    iterable.into_iter().fold1(|mut a, b| { a.extend(b); a }).unwrap_or_default()
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/cons_tuples_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/cons_tuples_impl.rs
deleted file mode 100644
index ae0f48f..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/cons_tuples_impl.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-
-macro_rules! impl_cons_iter(
-    ($_A:ident, $_B:ident, ) => (); // stop
-
-    ($A:ident, $($B:ident,)*) => (
-        impl_cons_iter!($($B,)*);
-        #[allow(non_snake_case)]
-        impl<X, Iter, $($B),*> Iterator for ConsTuples<Iter, (($($B,)*), X)>
-            where Iter: Iterator<Item = (($($B,)*), X)>,
-        {
-            type Item = ($($B,)* X, );
-            fn next(&mut self) -> Option<Self::Item> {
-                self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, ))
-            }
-
-            fn size_hint(&self) -> (usize, Option<usize>) {
-                self.iter.size_hint()
-            }
-            fn fold<Acc, Fold>(self, accum: Acc, mut f: Fold) -> Acc
-                where Fold: FnMut(Acc, Self::Item) -> Acc,
-            {
-                self.iter.fold(accum, move |acc, (($($B,)*), x)| f(acc, ($($B,)* x, )))
-            }
-        }
-
-        #[allow(non_snake_case)]
-        impl<X, Iter, $($B),*> DoubleEndedIterator for ConsTuples<Iter, (($($B,)*), X)>
-            where Iter: DoubleEndedIterator<Item = (($($B,)*), X)>,
-        {
-            fn next_back(&mut self) -> Option<Self::Item> {
-                self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, ))
-            }
-        }
-
-    );
-);
-
-impl_cons_iter!(A, B, C, D, E, F, G, H, I, J, K, L,);
-
-/// An iterator that maps an iterator of tuples like
-/// `((A, B), C)` to an iterator of `(A, B, C)`.
-///
-/// Used by the `iproduct!()` macro.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-#[derive(Debug)]
-pub struct ConsTuples<I, J>
-    where I: Iterator<Item=J>,
-{
-    iter: I,
-}
-
-impl<I, J> Clone for ConsTuples<I, J>
-    where I: Clone + Iterator<Item=J>,
-{
-    clone_fields!(iter);
-}
-
-/// Create an iterator that maps for example iterators of
-/// `((A, B), C)` to `(A, B, C)`.
-pub fn cons_tuples<I, J>(iterable: I) -> ConsTuples<I::IntoIter, J>
-    where I: IntoIterator<Item=J>
-{
-    ConsTuples { iter: iterable.into_iter() }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/diff.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/diff.rs
deleted file mode 100644
index 1731f06..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/diff.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-//! "Diff"ing iterators for caching elements to sequential collections without requiring the new
-//! elements' iterator to be `Clone`.
-//!
-//! - [`Diff`] (produced by the [`diff_with`] function)
-//! describes the difference between two non-`Clone` iterators `I` and `J` after breaking ASAP from
-//! a lock-step comparison.
-
-use crate::free::put_back;
-use crate::structs::PutBack;
-
-/// A type returned by the [`diff_with`] function.
-///
-/// `Diff` represents the way in which the elements yielded by the iterator `I` differ to some
-/// iterator `J`.
-pub enum Diff<I, J>
-    where I: Iterator,
-          J: Iterator
-{
-    /// The index of the first non-matching element along with both iterator's remaining elements
-    /// starting with the first mis-match.
-    FirstMismatch(usize, PutBack<I>, PutBack<J>),
-    /// The total number of elements that were in `J` along with the remaining elements of `I`.
-    Shorter(usize, PutBack<I>),
-    /// The total number of elements that were in `I` along with the remaining elements of `J`.
-    Longer(usize, PutBack<J>),
-}
-
-/// Compares every element yielded by both `i` and `j` with the given function in lock-step and
-/// returns a [`Diff`] which describes how `j` differs from `i`.
-///
-/// If the number of elements yielded by `j` is less than the number of elements yielded by `i`,
-/// the number of `j` elements yielded will be returned along with `i`'s remaining elements as
-/// `Diff::Shorter`.
-///
-/// If the two elements of a step differ, the index of those elements along with the remaining
-/// elements of both `i` and `j` are returned as `Diff::FirstMismatch`.
-///
-/// If `i` becomes exhausted before `j` becomes exhausted, the number of elements in `i` along with
-/// the remaining `j` elements will be returned as `Diff::Longer`.
-pub fn diff_with<I, J, F>(i: I, j: J, is_equal: F)
-    -> Option<Diff<I::IntoIter, J::IntoIter>>
-    where I: IntoIterator,
-          J: IntoIterator,
-          F: Fn(&I::Item, &J::Item) -> bool
-{
-    let mut i = i.into_iter();
-    let mut j = j.into_iter();
-    let mut idx = 0;
-    while let Some(i_elem) = i.next() {
-        match j.next() {
-            None => return Some(Diff::Shorter(idx, put_back(i).with_value(i_elem))),
-            Some(j_elem) => if !is_equal(&i_elem, &j_elem) {
-                let remaining_i = put_back(i).with_value(i_elem);
-                let remaining_j = put_back(j).with_value(j_elem);
-                return Some(Diff::FirstMismatch(idx, remaining_i, remaining_j));
-            },
-        }
-        idx += 1;
-    }
-    j.next().map(|j_elem| Diff::Longer(idx, put_back(j).with_value(j_elem)))
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/intersperse.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/intersperse.rs
deleted file mode 100644
index 10a3a538..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/intersperse.rs
+++ /dev/null
@@ -1,118 +0,0 @@
-use std::iter::{Fuse, FusedIterator};
-use super::size_hint;
-
-pub trait IntersperseElement<Item> {
-    fn generate(&mut self) -> Item;
-}
-
-#[derive(Debug, Clone)]
-pub struct IntersperseElementSimple<Item>(Item);
-
-impl<Item: Clone> IntersperseElement<Item> for IntersperseElementSimple<Item> {
-    fn generate(&mut self) -> Item {
-        self.0.clone()
-    }
-}
-
-/// An iterator adaptor to insert a particular value
-/// between each element of the adapted iterator.
-///
-/// Iterator element type is `I::Item`
-///
-/// This iterator is *fused*.
-///
-/// See [`.intersperse()`](crate::Itertools::intersperse) for more information.
-pub type Intersperse<I> = IntersperseWith<I, IntersperseElementSimple<<I as Iterator>::Item>>;
-
-/// Create a new Intersperse iterator
-pub fn intersperse<I>(iter: I, elt: I::Item) -> Intersperse<I>
-    where I: Iterator,
-{
-    intersperse_with(iter, IntersperseElementSimple(elt))
-}
-
-impl<Item, F: FnMut()->Item> IntersperseElement<Item> for F {
-    fn generate(&mut self) -> Item {
-        self()
-    }
-}
-
-/// An iterator adaptor to insert a particular value created by a function
-/// between each element of the adapted iterator.
-///
-/// Iterator element type is `I::Item`
-///
-/// This iterator is *fused*.
-///
-/// See [`.intersperse_with()`](crate::Itertools::intersperse_with) for more information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-#[derive(Clone, Debug)]
-pub struct IntersperseWith<I, ElemF>
-    where I: Iterator,
-{
-    element: ElemF,
-    iter: Fuse<I>,
-    peek: Option<I::Item>,
-}
-
-/// Create a new `IntersperseWith` iterator
-pub fn intersperse_with<I, ElemF>(iter: I, elt: ElemF) -> IntersperseWith<I, ElemF>
-    where I: Iterator,
-{
-    let mut iter = iter.fuse();
-    IntersperseWith {
-        peek: iter.next(),
-        iter,
-        element: elt,
-    }
-}
-
-impl<I, ElemF> Iterator for IntersperseWith<I, ElemF>
-    where I: Iterator,
-          ElemF: IntersperseElement<I::Item>
-{
-    type Item = I::Item;
-    #[inline]
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.peek.is_some() {
-            self.peek.take()
-        } else {
-            self.peek = self.iter.next();
-            if self.peek.is_some() {
-                Some(self.element.generate())
-            } else {
-                None
-            }
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        // 2 * SH + { 1 or 0 }
-        let has_peek = self.peek.is_some() as usize;
-        let sh = self.iter.size_hint();
-        size_hint::add_scalar(size_hint::add(sh, sh), has_peek)
-    }
-
-    fn fold<B, F>(mut self, init: B, mut f: F) -> B where
-        Self: Sized, F: FnMut(B, Self::Item) -> B,
-    {
-        let mut accum = init;
-
-        if let Some(x) = self.peek.take() {
-            accum = f(accum, x);
-        }
-
-        let element = &mut self.element;
-
-        self.iter.fold(accum,
-            |accum, x| {
-                let accum = f(accum, element.generate());
-                f(accum, x)
-        })
-    }
-}
-
-impl<I, ElemF> FusedIterator for IntersperseWith<I, ElemF>
-    where I: Iterator,
-          ElemF: IntersperseElement<I::Item>
-{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/k_smallest.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/k_smallest.rs
deleted file mode 100644
index acaea59..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/k_smallest.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use alloc::collections::BinaryHeap;
-use core::cmp::Ord;
-
-pub(crate) fn k_smallest<T: Ord, I: Iterator<Item = T>>(mut iter: I, k: usize) -> BinaryHeap<T> {
-    if k == 0 { return BinaryHeap::new(); }
-
-    let mut heap = iter.by_ref().take(k).collect::<BinaryHeap<_>>();
-
-    iter.for_each(|i| {
-        debug_assert_eq!(heap.len(), k);
-        // Equivalent to heap.push(min(i, heap.pop())) but more efficient.
-        // This should be done with a single `.peek_mut().unwrap()` but
-        //  `PeekMut` sifts-down unconditionally on Rust 1.46.0 and prior.
-        if *heap.peek().unwrap() > i {
-            *heap.peek_mut().unwrap() = i;
-        }
-    });
-
-    heap
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lazy_buffer.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lazy_buffer.rs
deleted file mode 100644
index ca24062..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lazy_buffer.rs
+++ /dev/null
@@ -1,63 +0,0 @@
-use std::ops::Index;
-use alloc::vec::Vec;
-
-#[derive(Debug, Clone)]
-pub struct LazyBuffer<I: Iterator> {
-    pub it: I,
-    done: bool,
-    buffer: Vec<I::Item>,
-}
-
-impl<I> LazyBuffer<I>
-where
-    I: Iterator,
-{
-    pub fn new(it: I) -> LazyBuffer<I> {
-        LazyBuffer {
-            it,
-            done: false,
-            buffer: Vec::new(),
-        }
-    }
-
-    pub fn len(&self) -> usize {
-        self.buffer.len()
-    }
-
-    pub fn get_next(&mut self) -> bool {
-        if self.done {
-            return false;
-        }
-        if let Some(x) = self.it.next() {
-            self.buffer.push(x);
-            true
-        } else {
-            self.done = true;
-            false
-        }
-    }
-
-    pub fn prefill(&mut self, len: usize) {
-        let buffer_len = self.buffer.len();
-
-        if !self.done && len > buffer_len {
-            let delta = len - buffer_len;
-
-            self.buffer.extend(self.it.by_ref().take(delta));
-            self.done = self.buffer.len() < len;
-        }
-    }
-}
-
-impl<I, J> Index<J> for LazyBuffer<I>
-where
-    I: Iterator,
-    I::Item: Sized,
-    Vec<I::Item>: Index<J>
-{
-    type Output = <Vec<I::Item> as Index<J>>::Output;
-
-    fn index(&self, index: J) -> &Self::Output {
-        self.buffer.index(index)
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/merge_join.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/merge_join.rs
deleted file mode 100644
index 84f7d033..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/merge_join.rs
+++ /dev/null
@@ -1,220 +0,0 @@
-use std::cmp::Ordering;
-use std::iter::Fuse;
-use std::fmt;
-
-use either::Either;
-
-use super::adaptors::{PutBack, put_back};
-use crate::either_or_both::EitherOrBoth;
-use crate::size_hint::{self, SizeHint};
-#[cfg(doc)]
-use crate::Itertools;
-
-/// Return an iterator adaptor that merge-joins items from the two base iterators in ascending order.
-///
-/// [`IntoIterator`] enabled version of [`Itertools::merge_join_by`].
-pub fn merge_join_by<I, J, F, T>(left: I, right: J, cmp_fn: F)
-    -> MergeJoinBy<I::IntoIter, J::IntoIter, F>
-    where I: IntoIterator,
-          J: IntoIterator,
-          F: FnMut(&I::Item, &J::Item) -> T,
-          T: OrderingOrBool<I::Item, J::Item>,
-{
-    MergeJoinBy {
-        left: put_back(left.into_iter().fuse()),
-        right: put_back(right.into_iter().fuse()),
-        cmp_fn,
-    }
-}
-
-/// An iterator adaptor that merge-joins items from the two base iterators in ascending order.
-///
-/// See [`.merge_join_by()`](crate::Itertools::merge_join_by) for more information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct MergeJoinBy<I: Iterator, J: Iterator, F> {
-    left: PutBack<Fuse<I>>,
-    right: PutBack<Fuse<J>>,
-    cmp_fn: F,
-}
-
-pub trait OrderingOrBool<L, R> {
-    type MergeResult;
-    fn left(left: L) -> Self::MergeResult;
-    fn right(right: R) -> Self::MergeResult;
-    // "merge" never returns (Some(...), Some(...), ...) so Option<Either<I::Item, J::Item>>
-    // is appealing but it is always followed by two put_backs, so we think the compiler is
-    // smart enough to optimize it. Or we could move put_backs into "merge".
-    fn merge(self, left: L, right: R) -> (Option<L>, Option<R>, Self::MergeResult);
-    fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint;
-}
-
-impl<L, R> OrderingOrBool<L, R> for Ordering {
-    type MergeResult = EitherOrBoth<L, R>;
-    fn left(left: L) -> Self::MergeResult {
-        EitherOrBoth::Left(left)
-    }
-    fn right(right: R) -> Self::MergeResult {
-        EitherOrBoth::Right(right)
-    }
-    fn merge(self, left: L, right: R) -> (Option<L>, Option<R>, Self::MergeResult) {
-        match self {
-            Ordering::Equal => (None, None, EitherOrBoth::Both(left, right)),
-            Ordering::Less => (None, Some(right), EitherOrBoth::Left(left)),
-            Ordering::Greater => (Some(left), None, EitherOrBoth::Right(right)),
-        }
-    }
-    fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint {
-        let (a_lower, a_upper) = left;
-        let (b_lower, b_upper) = right;
-        let lower = ::std::cmp::max(a_lower, b_lower);
-        let upper = match (a_upper, b_upper) {
-            (Some(x), Some(y)) => x.checked_add(y),
-            _ => None,
-        };
-        (lower, upper)
-    }
-}
-
-impl<L, R> OrderingOrBool<L, R> for bool {
-    type MergeResult = Either<L, R>;
-    fn left(left: L) -> Self::MergeResult {
-        Either::Left(left)
-    }
-    fn right(right: R) -> Self::MergeResult {
-        Either::Right(right)
-    }
-    fn merge(self, left: L, right: R) -> (Option<L>, Option<R>, Self::MergeResult) {
-        if self {
-            (None, Some(right), Either::Left(left))
-        } else {
-            (Some(left), None, Either::Right(right))
-        }
-    }
-    fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint {
-        // Not ExactSizeIterator because size may be larger than usize
-        size_hint::add(left, right)
-    }
-}
-
-impl<I, J, F> Clone for MergeJoinBy<I, J, F>
-    where I: Iterator,
-          J: Iterator,
-          PutBack<Fuse<I>>: Clone,
-          PutBack<Fuse<J>>: Clone,
-          F: Clone,
-{
-    clone_fields!(left, right, cmp_fn);
-}
-
-impl<I, J, F> fmt::Debug for MergeJoinBy<I, J, F>
-    where I: Iterator + fmt::Debug,
-          I::Item: fmt::Debug,
-          J: Iterator + fmt::Debug,
-          J::Item: fmt::Debug,
-{
-    debug_fmt_fields!(MergeJoinBy, left, right);
-}
-
-impl<I, J, F, T> Iterator for MergeJoinBy<I, J, F>
-    where I: Iterator,
-          J: Iterator,
-          F: FnMut(&I::Item, &J::Item) -> T,
-          T: OrderingOrBool<I::Item, J::Item>,
-{
-    type Item = T::MergeResult;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        match (self.left.next(), self.right.next()) {
-            (None, None) => None,
-            (Some(left), None) => Some(T::left(left)),
-            (None, Some(right)) => Some(T::right(right)),
-            (Some(left), Some(right)) => {
-                let (left, right, next) = (self.cmp_fn)(&left, &right).merge(left, right);
-                if let Some(left) = left {
-                    self.left.put_back(left);
-                }
-                if let Some(right) = right {
-                    self.right.put_back(right);
-                }
-                Some(next)
-            }
-        }
-    }
-
-    fn size_hint(&self) -> SizeHint {
-        T::size_hint(self.left.size_hint(), self.right.size_hint())
-    }
-
-    fn count(mut self) -> usize {
-        let mut count = 0;
-        loop {
-            match (self.left.next(), self.right.next()) {
-                (None, None) => break count,
-                (Some(_left), None) => break count + 1 + self.left.into_parts().1.count(),
-                (None, Some(_right)) => break count + 1 + self.right.into_parts().1.count(),
-                (Some(left), Some(right)) => {
-                    count += 1;
-                    let (left, right, _) = (self.cmp_fn)(&left, &right).merge(left, right);
-                    if let Some(left) = left {
-                        self.left.put_back(left);
-                    }
-                    if let Some(right) = right {
-                        self.right.put_back(right);
-                    }
-                }
-            }
-        }
-    }
-
-    fn last(mut self) -> Option<Self::Item> {
-        let mut previous_element = None;
-        loop {
-            match (self.left.next(), self.right.next()) {
-                (None, None) => break previous_element,
-                (Some(left), None) => {
-                    break Some(T::left(
-                        self.left.into_parts().1.last().unwrap_or(left),
-                    ))
-                }
-                (None, Some(right)) => {
-                    break Some(T::right(
-                        self.right.into_parts().1.last().unwrap_or(right),
-                    ))
-                }
-                (Some(left), Some(right)) => {
-                    let (left, right, elem) = (self.cmp_fn)(&left, &right).merge(left, right);
-                    if let Some(left) = left {
-                        self.left.put_back(left);
-                    }
-                    if let Some(right) = right {
-                        self.right.put_back(right);
-                    }
-                    previous_element = Some(elem);
-                }
-            }
-        }
-    }
-
-    fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
-        loop {
-            if n == 0 {
-                break self.next();
-            }
-            n -= 1;
-            match (self.left.next(), self.right.next()) {
-                (None, None) => break None,
-                (Some(_left), None) => break self.left.nth(n).map(T::left),
-                (None, Some(_right)) => break self.right.nth(n).map(T::right),
-                (Some(left), Some(right)) => {
-                    let (left, right, _) = (self.cmp_fn)(&left, &right).merge(left, right);
-                    if let Some(left) = left {
-                        self.left.put_back(left);
-                    }
-                    if let Some(right) = right {
-                        self.right.put_back(right);
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peek_nth.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peek_nth.rs
deleted file mode 100644
index bcca4583..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peek_nth.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-use crate::size_hint;
-use crate::PeekingNext;
-use alloc::collections::VecDeque;
-use std::iter::Fuse;
-
-/// See [`peek_nth()`] for more information.
-#[derive(Clone, Debug)]
-pub struct PeekNth<I>
-where
-    I: Iterator,
-{
-    iter: Fuse<I>,
-    buf: VecDeque<I::Item>,
-}
-
-/// A drop-in replacement for [`std::iter::Peekable`] which adds a `peek_nth`
-/// method allowing the user to `peek` at a value several iterations forward
-/// without advancing the base iterator.
-///
-/// This differs from `multipeek` in that subsequent calls to `peek` or
-/// `peek_nth` will always return the same value until `next` is called
-/// (making `reset_peek` unnecessary).
-pub fn peek_nth<I>(iterable: I) -> PeekNth<I::IntoIter>
-where
-    I: IntoIterator,
-{
-    PeekNth {
-        iter: iterable.into_iter().fuse(),
-        buf: VecDeque::new(),
-    }
-}
-
-impl<I> PeekNth<I>
-where
-    I: Iterator,
-{
-    /// Works exactly like the `peek` method in `std::iter::Peekable`
-    pub fn peek(&mut self) -> Option<&I::Item> {
-        self.peek_nth(0)
-    }
-
-    /// Returns a reference to the `nth` value without advancing the iterator.
-    ///
-    /// # Examples
-    ///
-    /// Basic usage:
-    ///
-    /// ```rust
-    /// use itertools::peek_nth;
-    ///
-    /// let xs = vec![1,2,3];
-    /// let mut iter = peek_nth(xs.iter());
-    ///
-    /// assert_eq!(iter.peek_nth(0), Some(&&1));
-    /// assert_eq!(iter.next(), Some(&1));
-    ///
-    /// // The iterator does not advance even if we call `peek_nth` multiple times
-    /// assert_eq!(iter.peek_nth(0), Some(&&2));
-    /// assert_eq!(iter.peek_nth(1), Some(&&3));
-    /// assert_eq!(iter.next(), Some(&2));
-    ///
-    /// // Calling `peek_nth` past the end of the iterator will return `None`
-    /// assert_eq!(iter.peek_nth(1), None);
-    /// ```
-    pub fn peek_nth(&mut self, n: usize) -> Option<&I::Item> {
-        let unbuffered_items = (n + 1).saturating_sub(self.buf.len());
-
-        self.buf.extend(self.iter.by_ref().take(unbuffered_items));
-
-        self.buf.get(n)
-    }
-}
-
-impl<I> Iterator for PeekNth<I>
-where
-    I: Iterator,
-{
-    type Item = I::Item;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.buf.pop_front().or_else(|| self.iter.next())
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        size_hint::add_scalar(self.iter.size_hint(), self.buf.len())
-    }
-}
-
-impl<I> ExactSizeIterator for PeekNth<I> where I: ExactSizeIterator {}
-
-impl<I> PeekingNext for PeekNth<I>
-where
-    I: Iterator,
-{
-    fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
-    where
-        F: FnOnce(&Self::Item) -> bool,
-    {
-        self.peek().filter(|item| accept(item))?;
-        self.next()
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/permutations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/permutations.rs
deleted file mode 100644
index d03b852..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/permutations.rs
+++ /dev/null
@@ -1,277 +0,0 @@
-use alloc::vec::Vec;
-use std::fmt;
-use std::iter::once;
-
-use super::lazy_buffer::LazyBuffer;
-
-/// An iterator adaptor that iterates through all the `k`-permutations of the
-/// elements from an iterator.
-///
-/// See [`.permutations()`](crate::Itertools::permutations) for
-/// more information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct Permutations<I: Iterator> {
-    vals: LazyBuffer<I>,
-    state: PermutationState,
-}
-
-impl<I> Clone for Permutations<I>
-    where I: Clone + Iterator,
-          I::Item: Clone,
-{
-    clone_fields!(vals, state);
-}
-
-#[derive(Clone, Debug)]
-enum PermutationState {
-    StartUnknownLen {
-        k: usize,
-    },
-    OngoingUnknownLen {
-        k: usize,
-        min_n: usize,
-    },
-    Complete(CompleteState),
-    Empty,
-}
-
-#[derive(Clone, Debug)]
-enum CompleteState {
-    Start {
-        n: usize,
-        k: usize,
-    },
-    Ongoing {
-        indices: Vec<usize>,
-        cycles: Vec<usize>,
-    }
-}
-
-enum CompleteStateRemaining {
-    Known(usize),
-    Overflow,
-}
-
-impl<I> fmt::Debug for Permutations<I>
-    where I: Iterator + fmt::Debug,
-          I::Item: fmt::Debug,
-{
-    debug_fmt_fields!(Permutations, vals, state);
-}
-
-pub fn permutations<I: Iterator>(iter: I, k: usize) -> Permutations<I> {
-    let mut vals = LazyBuffer::new(iter);
-
-    if k == 0 {
-        // Special case, yields single empty vec; `n` is irrelevant
-        let state = PermutationState::Complete(CompleteState::Start { n: 0, k: 0 });
-
-        return Permutations {
-            vals,
-            state
-        };
-    }
-
-    let mut enough_vals = true;
-
-    while vals.len() < k {
-        if !vals.get_next() {
-            enough_vals = false;
-            break;
-        }
-    }
-
-    let state = if enough_vals {
-        PermutationState::StartUnknownLen { k }
-    } else {
-        PermutationState::Empty
-    };
-
-    Permutations {
-        vals,
-        state
-    }
-}
-
-impl<I> Iterator for Permutations<I>
-where
-    I: Iterator,
-    I::Item: Clone
-{
-    type Item = Vec<I::Item>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.advance();
-
-        let &mut Permutations { ref vals, ref state } = self;
-
-        match *state {
-            PermutationState::StartUnknownLen { .. } => panic!("unexpected iterator state"),
-            PermutationState::OngoingUnknownLen { k, min_n } => {
-                let latest_idx = min_n - 1;
-                let indices = (0..(k - 1)).chain(once(latest_idx));
-
-                Some(indices.map(|i| vals[i].clone()).collect())
-            }
-            PermutationState::Complete(CompleteState::Ongoing { ref indices, ref cycles }) => {
-                let k = cycles.len();
-                Some(indices[0..k].iter().map(|&i| vals[i].clone()).collect())
-            },
-            PermutationState::Complete(CompleteState::Start { .. }) | PermutationState::Empty => None
-        }
-    }
-
-    fn count(self) -> usize {
-        fn from_complete(complete_state: CompleteState) -> usize {
-            match complete_state.remaining() {
-                CompleteStateRemaining::Known(count) => count,
-                CompleteStateRemaining::Overflow => {
-                    panic!("Iterator count greater than usize::MAX");
-                }
-            }
-        }
-
-        let Permutations { vals, state } = self;
-        match state {
-            PermutationState::StartUnknownLen { k } => {
-                let n = vals.len() + vals.it.count();
-                let complete_state = CompleteState::Start { n, k };
-
-                from_complete(complete_state)
-            }
-            PermutationState::OngoingUnknownLen { k, min_n } => {
-                let prev_iteration_count = min_n - k + 1;
-                let n = vals.len() + vals.it.count();
-                let complete_state = CompleteState::Start { n, k };
-
-                from_complete(complete_state) - prev_iteration_count
-            },
-            PermutationState::Complete(state) => from_complete(state),
-            PermutationState::Empty => 0
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        match self.state {
-            PermutationState::StartUnknownLen { .. } |
-            PermutationState::OngoingUnknownLen { .. } => (0, None), // TODO can we improve this lower bound?
-            PermutationState::Complete(ref state) => match state.remaining() {
-                CompleteStateRemaining::Known(count) => (count, Some(count)),
-                CompleteStateRemaining::Overflow => (::std::usize::MAX, None)
-            }
-            PermutationState::Empty => (0, Some(0))
-        }
-    }
-}
-
-impl<I> Permutations<I>
-where
-    I: Iterator,
-    I::Item: Clone
-{
-    fn advance(&mut self) {
-        let &mut Permutations { ref mut vals, ref mut state } = self;
-
-        *state = match *state {
-            PermutationState::StartUnknownLen { k } => {
-                PermutationState::OngoingUnknownLen { k, min_n: k }
-            }
-            PermutationState::OngoingUnknownLen { k, min_n } => {
-                if vals.get_next() {
-                    PermutationState::OngoingUnknownLen { k, min_n: min_n + 1 }
-                } else {
-                    let n = min_n;
-                    let prev_iteration_count = n - k + 1;
-                    let mut complete_state = CompleteState::Start { n, k };
-
-                    // Advance the complete-state iterator to the correct point
-                    for _ in 0..(prev_iteration_count + 1) {
-                        complete_state.advance();
-                    }
-
-                    PermutationState::Complete(complete_state)
-                }
-            }
-            PermutationState::Complete(ref mut state) => {
-                state.advance();
-
-                return;
-            }
-            PermutationState::Empty => { return; }
-        };
-    }
-}
-
-impl CompleteState {
-    fn advance(&mut self) {
-        *self = match *self {
-            CompleteState::Start { n, k } => {
-                let indices = (0..n).collect();
-                let cycles = ((n - k)..n).rev().collect();
-
-                CompleteState::Ongoing {
-                    cycles,
-                    indices
-                }
-            },
-            CompleteState::Ongoing { ref mut indices, ref mut cycles } => {
-                let n = indices.len();
-                let k = cycles.len();
-
-                for i in (0..k).rev() {
-                    if cycles[i] == 0 {
-                        cycles[i] = n - i - 1;
-
-                        let to_push = indices.remove(i);
-                        indices.push(to_push);
-                    } else {
-                        let swap_index = n - cycles[i];
-                        indices.swap(i, swap_index);
-
-                        cycles[i] -= 1;
-                        return;
-                    }
-                }
-
-                CompleteState::Start { n, k }
-            }
-        }
-    }
-
-    fn remaining(&self) -> CompleteStateRemaining {
-        use self::CompleteStateRemaining::{Known, Overflow};
-
-        match *self {
-            CompleteState::Start { n, k } => {
-                if n < k {
-                    return Known(0);
-                }
-
-                let count: Option<usize> = (n - k + 1..n + 1).fold(Some(1), |acc, i| {
-                    acc.and_then(|acc| acc.checked_mul(i))
-                });
-
-                match count {
-                    Some(count) => Known(count),
-                    None => Overflow
-                }
-            }
-            CompleteState::Ongoing { ref indices, ref cycles } => {
-                let mut count: usize = 0;
-
-                for (i, &c) in cycles.iter().enumerate() {
-                    let radix = indices.len() - i;
-                    let next_count = count.checked_mul(radix)
-                        .and_then(|count| count.checked_add(c));
-
-                    count = match next_count {
-                        Some(count) => count,
-                        None => { return Overflow; }
-                    };
-                }
-
-                Known(count)
-            }
-        }
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/powerset.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/powerset.rs
deleted file mode 100644
index 4d7685b1..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/powerset.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-use std::fmt;
-use std::iter::FusedIterator;
-use std::usize;
-use alloc::vec::Vec;
-
-use super::combinations::{Combinations, combinations};
-use super::size_hint;
-
-/// An iterator to iterate through the powerset of the elements from an iterator.
-///
-/// See [`.powerset()`](crate::Itertools::powerset) for more
-/// information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct Powerset<I: Iterator> {
-    combs: Combinations<I>,
-    // Iterator `position` (equal to count of yielded elements).
-    pos: usize,
-}
-
-impl<I> Clone for Powerset<I>
-    where I: Clone + Iterator,
-          I::Item: Clone,
-{
-    clone_fields!(combs, pos);
-}
-
-impl<I> fmt::Debug for Powerset<I>
-    where I: Iterator + fmt::Debug,
-          I::Item: fmt::Debug,
-{
-    debug_fmt_fields!(Powerset, combs, pos);
-}
-
-/// Create a new `Powerset` from a clonable iterator.
-pub fn powerset<I>(src: I) -> Powerset<I>
-    where I: Iterator,
-          I::Item: Clone,
-{
-    Powerset {
-        combs: combinations(src, 0),
-        pos: 0,
-    }
-}
-
-impl<I> Iterator for Powerset<I>
-    where
-        I: Iterator,
-        I::Item: Clone,
-{
-    type Item = Vec<I::Item>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if let Some(elt) = self.combs.next() {
-            self.pos = self.pos.saturating_add(1);
-            Some(elt)
-        } else if self.combs.k() < self.combs.n()
-            || self.combs.k() == 0
-        {
-            self.combs.reset(self.combs.k() + 1);
-            self.combs.next().map(|elt| {
-                self.pos = self.pos.saturating_add(1);
-                elt
-            })
-        } else {
-            None
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        // Total bounds for source iterator.
-        let src_total = size_hint::add_scalar(self.combs.src().size_hint(), self.combs.n());
-
-        // Total bounds for self ( length(powerset(set) == 2 ^ length(set) )
-        let self_total = size_hint::pow_scalar_base(2, src_total);
-
-        if self.pos < usize::MAX {
-            // Subtract count of elements already yielded from total.
-            size_hint::sub_scalar(self_total, self.pos)
-        } else {
-            // Fallback: self.pos is saturated and no longer reliable.
-            (0, self_total.1)
-        }
-    }
-}
-
-impl<I> FusedIterator for Powerset<I>
-    where
-        I: Iterator,
-        I::Item: Clone,
-{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/process_results_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/process_results_impl.rs
deleted file mode 100644
index 713db45..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/process_results_impl.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-#[cfg(doc)]
-use crate::Itertools;
-
-/// An iterator that produces only the `T` values as long as the
-/// inner iterator produces `Ok(T)`.
-///
-/// Used by [`process_results`](crate::process_results), see its docs
-/// for more information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-#[derive(Debug)]
-pub struct ProcessResults<'a, I, E: 'a> {
-    error: &'a mut Result<(), E>,
-    iter: I,
-}
-
-impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E>
-    where I: Iterator<Item = Result<T, E>>
-{
-    type Item = T;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        match self.iter.next() {
-            Some(Ok(x)) => Some(x),
-            Some(Err(e)) => {
-                *self.error = Err(e);
-                None
-            }
-            None => None,
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        (0, self.iter.size_hint().1)
-    }
-
-    fn fold<B, F>(mut self, init: B, mut f: F) -> B
-    where
-        Self: Sized,
-        F: FnMut(B, Self::Item) -> B,
-    {
-        let error = self.error;
-        self.iter
-            .try_fold(init, |acc, opt| match opt {
-                Ok(x) => Ok(f(acc, x)),
-                Err(e) => {
-                    *error = Err(e);
-                    Err(acc)
-                }
-            })
-            .unwrap_or_else(|e| e)
-    }
-}
-
-/// “Lift” a function of the values of an iterator so that it can process
-/// an iterator of `Result` values instead.
-///
-/// [`IntoIterator`] enabled version of [`Itertools::process_results`].
-pub fn process_results<I, F, T, E, R>(iterable: I, processor: F) -> Result<R, E>
-    where I: IntoIterator<Item = Result<T, E>>,
-          F: FnOnce(ProcessResults<I::IntoIter, E>) -> R
-{
-    let iter = iterable.into_iter();
-    let mut error = Ok(());
-
-    let result = processor(ProcessResults { error: &mut error, iter });
-
-    error.map(|_| result)
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/repeatn.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/repeatn.rs
deleted file mode 100644
index e025f6f..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/repeatn.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use std::iter::FusedIterator;
-
-/// An iterator that produces *n* repetitions of an element.
-///
-/// See [`repeat_n()`](crate::repeat_n) for more information.
-#[must_use = "iterators are lazy and do nothing unless consumed"]
-#[derive(Clone, Debug)]
-pub struct RepeatN<A> {
-    elt: Option<A>,
-    n: usize,
-}
-
-/// Create an iterator that produces `n` repetitions of `element`.
-pub fn repeat_n<A>(element: A, n: usize) -> RepeatN<A>
-    where A: Clone,
-{
-    if n == 0 {
-        RepeatN { elt: None, n, }
-    } else {
-        RepeatN { elt: Some(element), n, }
-    }
-}
-
-impl<A> Iterator for RepeatN<A>
-    where A: Clone
-{
-    type Item = A;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.n > 1 {
-            self.n -= 1;
-            self.elt.as_ref().cloned()
-        } else {
-            self.n = 0;
-            self.elt.take()
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        (self.n, Some(self.n))
-    }
-}
-
-impl<A> DoubleEndedIterator for RepeatN<A>
-    where A: Clone
-{
-    #[inline]
-    fn next_back(&mut self) -> Option<Self::Item> {
-        self.next()
-    }
-}
-
-impl<A> ExactSizeIterator for RepeatN<A>
-    where A: Clone
-{}
-
-impl<A> FusedIterator for RepeatN<A>
-    where A: Clone
-{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/take_while_inclusive.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/take_while_inclusive.rs
deleted file mode 100644
index e2a7479..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/take_while_inclusive.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-use core::iter::FusedIterator;
-use std::fmt;
-
-/// An iterator adaptor that consumes elements while the given predicate is
-/// `true`, including the element for which the predicate first returned
-/// `false`.
-///
-/// See [`.take_while_inclusive()`](crate::Itertools::take_while_inclusive)
-/// for more information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct TakeWhileInclusive<'a, I: 'a, F> {
-    iter: &'a mut I,
-    predicate: F,
-    done: bool,
-}
-
-impl<'a, I, F> TakeWhileInclusive<'a, I, F>
-where
-    I: Iterator,
-    F: FnMut(&I::Item) -> bool,
-{
-    /// Create a new [`TakeWhileInclusive`] from an iterator and a predicate.
-    pub fn new(iter: &'a mut I, predicate: F) -> Self {
-        Self { iter, predicate, done: false}
-    }
-}
-
-impl<'a, I, F> fmt::Debug for TakeWhileInclusive<'a, I, F>
-    where I: Iterator + fmt::Debug,
-{
-    debug_fmt_fields!(TakeWhileInclusive, iter);
-}
-
-impl<'a, I, F> Iterator for TakeWhileInclusive<'a, I, F>
-where
-    I: Iterator,
-    F: FnMut(&I::Item) -> bool
-{
-    type Item = I::Item;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.done {
-            None
-        } else {
-            self.iter.next().map(|item| {
-                if !(self.predicate)(&item) {
-                    self.done = true;
-                }
-                item
-            })
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        if self.done {
-            (0, Some(0))
-        } else {
-            (0, self.iter.size_hint().1)
-        }
-    }
-}
-
-impl<I, F> FusedIterator for TakeWhileInclusive<'_, I, F>
-where
-    I: Iterator,
-    F: FnMut(&I::Item) -> bool
-{
-}
\ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tuple_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tuple_impl.rs
deleted file mode 100644
index fdf0865..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tuple_impl.rs
+++ /dev/null
@@ -1,331 +0,0 @@
-//! Some iterator that produces tuples
-
-use std::iter::Fuse;
-use std::iter::FusedIterator;
-use std::iter::Take;
-use std::iter::Cycle;
-use std::marker::PhantomData;
-
-// `HomogeneousTuple` is a public facade for `TupleCollect`, allowing
-// tuple-related methods to be used by clients in generic contexts, while
-// hiding the implementation details of `TupleCollect`.
-// See https://github.com/rust-itertools/itertools/issues/387
-
-/// Implemented for homogeneous tuples of size up to 12.
-pub trait HomogeneousTuple
-    : TupleCollect
-{}
-
-impl<T: TupleCollect> HomogeneousTuple for T {}
-
-/// An iterator over a incomplete tuple.
-///
-/// See [`.tuples()`](crate::Itertools::tuples) and
-/// [`Tuples::into_buffer()`].
-#[derive(Clone, Debug)]
-pub struct TupleBuffer<T>
-    where T: HomogeneousTuple
-{
-    cur: usize,
-    buf: T::Buffer,
-}
-
-impl<T> TupleBuffer<T>
-    where T: HomogeneousTuple
-{
-    fn new(buf: T::Buffer) -> Self {
-        TupleBuffer {
-            cur: 0,
-            buf,
-        }
-    }
-}
-
-impl<T> Iterator for TupleBuffer<T>
-    where T: HomogeneousTuple
-{
-    type Item = T::Item;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let s = self.buf.as_mut();
-        if let Some(ref mut item) = s.get_mut(self.cur) {
-            self.cur += 1;
-            item.take()
-        } else {
-            None
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let buffer = &self.buf.as_ref()[self.cur..];
-        let len = if buffer.is_empty() {
-            0
-        } else {
-            buffer.iter()
-                  .position(|x| x.is_none())
-                  .unwrap_or_else(|| buffer.len())
-        };
-        (len, Some(len))
-    }
-}
-
-impl<T> ExactSizeIterator for TupleBuffer<T>
-    where T: HomogeneousTuple
-{
-}
-
-/// An iterator that groups the items in tuples of a specific size.
-///
-/// See [`.tuples()`](crate::Itertools::tuples) for more information.
-#[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct Tuples<I, T>
-    where I: Iterator<Item = T::Item>,
-          T: HomogeneousTuple
-{
-    iter: Fuse<I>,
-    buf: T::Buffer,
-}
-
-/// Create a new tuples iterator.
-pub fn tuples<I, T>(iter: I) -> Tuples<I, T>
-    where I: Iterator<Item = T::Item>,
-          T: HomogeneousTuple
-{
-    Tuples {
-        iter: iter.fuse(),
-        buf: Default::default(),
-    }
-}
-
-impl<I, T> Iterator for Tuples<I, T>
-    where I: Iterator<Item = T::Item>,
-          T: HomogeneousTuple
-{
-    type Item = T;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        T::collect_from_iter(&mut self.iter, &mut self.buf)
-    }
-}
-
-impl<I, T> Tuples<I, T>
-    where I: Iterator<Item = T::Item>,
-          T: HomogeneousTuple
-{
-    /// Return a buffer with the produced items that was not enough to be grouped in a tuple.
-    ///
-    /// ```
-    /// use itertools::Itertools;
-    ///
-    /// let mut iter = (0..5).tuples();
-    /// assert_eq!(Some((0, 1, 2)), iter.next());
-    /// assert_eq!(None, iter.next());
-    /// itertools::assert_equal(vec![3, 4], iter.into_buffer());
-    /// ```
-    pub fn into_buffer(self) -> TupleBuffer<T> {
-        TupleBuffer::new(self.buf)
-    }
-}
-
-
-/// An iterator over all contiguous windows that produces tuples of a specific size.
-///
-/// See [`.tuple_windows()`](crate::Itertools::tuple_windows) for more
-/// information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-#[derive(Clone, Debug)]
-pub struct TupleWindows<I, T>
-    where I: Iterator<Item = T::Item>,
-          T: HomogeneousTuple
-{
-    iter: I,
-    last: Option<T>,
-}
-
-/// Create a new tuple windows iterator.
-pub fn tuple_windows<I, T>(mut iter: I) -> TupleWindows<I, T>
-    where I: Iterator<Item = T::Item>,
-          T: HomogeneousTuple,
-          T::Item: Clone
-{
-    use std::iter::once;
-
-    let mut last = None;
-    if T::num_items() != 1 {
-        // put in a duplicate item in front of the tuple; this simplifies
-        // .next() function.
-        if let Some(item) = iter.next() {
-            let iter = once(item.clone()).chain(once(item)).chain(&mut iter);
-            last = T::collect_from_iter_no_buf(iter);
-        }
-    }
-
-    TupleWindows {
-        iter,
-        last,
-    }
-}
-
-impl<I, T> Iterator for TupleWindows<I, T>
-    where I: Iterator<Item = T::Item>,
-          T: HomogeneousTuple + Clone,
-          T::Item: Clone
-{
-    type Item = T;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if T::num_items() == 1 {
-            return T::collect_from_iter_no_buf(&mut self.iter)
-        }
-        if let Some(ref mut last) = self.last {
-            if let Some(new) = self.iter.next() {
-                last.left_shift_push(new);
-                return Some(last.clone());
-            }
-        }
-        None
-    }
-}
-
-impl<I, T> FusedIterator for TupleWindows<I, T>
-    where I: FusedIterator<Item = T::Item>,
-          T: HomogeneousTuple + Clone,
-          T::Item: Clone
-{}
-
-/// An iterator over all windows, wrapping back to the first elements when the
-/// window would otherwise exceed the length of the iterator, producing tuples
-/// of a specific size.
-///
-/// See [`.circular_tuple_windows()`](crate::Itertools::circular_tuple_windows) for more
-/// information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-#[derive(Debug, Clone)]
-pub struct CircularTupleWindows<I, T: Clone>
-    where I: Iterator<Item = T::Item> + Clone,
-          T: TupleCollect + Clone
-{
-    iter: Take<TupleWindows<Cycle<I>, T>>,
-    phantom_data: PhantomData<T>
-}
-
-pub fn circular_tuple_windows<I, T>(iter: I) -> CircularTupleWindows<I, T>
-    where I: Iterator<Item = T::Item> + Clone + ExactSizeIterator,
-          T: TupleCollect + Clone,
-          T::Item: Clone
-{
-    let len = iter.len();
-    let iter = tuple_windows(iter.cycle()).take(len);
-
-    CircularTupleWindows {
-        iter,
-        phantom_data: PhantomData{}
-    }
-}
-
-impl<I, T> Iterator for CircularTupleWindows<I, T>
-    where I: Iterator<Item = T::Item> + Clone,
-          T: TupleCollect + Clone,
-          T::Item: Clone
-{
-    type Item = T;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.iter.next()
-    }
-}
-
-pub trait TupleCollect: Sized {
-    type Item;
-    type Buffer: Default + AsRef<[Option<Self::Item>]> + AsMut<[Option<Self::Item>]>;
-
-    fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self>
-        where I: IntoIterator<Item = Self::Item>;
-
-    fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self>
-        where I: IntoIterator<Item = Self::Item>;
-
-    fn num_items() -> usize;
-
-    fn left_shift_push(&mut self, item: Self::Item);
-}
-
-macro_rules! count_ident{
-    () => {0};
-    ($i0:ident, $($i:ident,)*) => {1 + count_ident!($($i,)*)};
-}
-macro_rules! rev_for_each_ident{
-    ($m:ident, ) => {};
-    ($m:ident, $i0:ident, $($i:ident,)*) => {
-        rev_for_each_ident!($m, $($i,)*);
-        $m!($i0);
-    };
-}
-
-macro_rules! impl_tuple_collect {
-    ($dummy:ident,) => {}; // stop
-    ($dummy:ident, $($Y:ident,)*) => (
-        impl_tuple_collect!($($Y,)*);
-        impl<A> TupleCollect for ($(ignore_ident!($Y, A),)*) {
-            type Item = A;
-            type Buffer = [Option<A>; count_ident!($($Y,)*) - 1];
-
-            #[allow(unused_assignments, unused_mut)]
-            fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self>
-                where I: IntoIterator<Item = A>
-            {
-                let mut iter = iter.into_iter();
-                $(
-                    let mut $Y = None;
-                )*
-
-                loop {
-                    $(
-                        $Y = iter.next();
-                        if $Y.is_none() {
-                            break
-                        }
-                    )*
-                    return Some(($($Y.unwrap()),*,))
-                }
-
-                let mut i = 0;
-                let mut s = buf.as_mut();
-                $(
-                    if i < s.len() {
-                        s[i] = $Y;
-                        i += 1;
-                    }
-                )*
-                return None;
-            }
-
-            fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self>
-                where I: IntoIterator<Item = A>
-            {
-                let mut iter = iter.into_iter();
-
-                Some(($(
-                    { let $Y = iter.next()?; $Y },
-                )*))
-            }
-
-            fn num_items() -> usize {
-                count_ident!($($Y,)*)
-            }
-
-            fn left_shift_push(&mut self, mut item: A) {
-                use std::mem::replace;
-
-                let &mut ($(ref mut $Y),*,) = self;
-                macro_rules! replace_item{($i:ident) => {
-                    item = replace($i, item);
-                }}
-                rev_for_each_ident!(replace_item, $($Y,)*);
-                drop(item);
-            }
-        }
-    )
-}
-impl_tuple_collect!(dummy, a, b, c, d, e, f, g, h, i, j, k, l,);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/with_position.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/with_position.rs
deleted file mode 100644
index dda9b25..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/with_position.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-use std::iter::{Fuse,Peekable, FusedIterator};
-
-/// An iterator adaptor that wraps each element in an [`Position`].
-///
-/// Iterator element type is `(Position, I::Item)`.
-///
-/// See [`.with_position()`](crate::Itertools::with_position) for more information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct WithPosition<I>
-    where I: Iterator,
-{
-    handled_first: bool,
-    peekable: Peekable<Fuse<I>>,
-}
-
-impl<I> Clone for WithPosition<I>
-    where I: Clone + Iterator,
-          I::Item: Clone,
-{
-    clone_fields!(handled_first, peekable);
-}
-
-/// Create a new `WithPosition` iterator.
-pub fn with_position<I>(iter: I) -> WithPosition<I>
-    where I: Iterator,
-{
-    WithPosition {
-        handled_first: false,
-        peekable: iter.fuse().peekable(),
-    }
-}
-
-/// The first component of the value yielded by `WithPosition`.
-/// Indicates the position of this element in the iterator results.
-///
-/// See [`.with_position()`](crate::Itertools::with_position) for more information.
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum Position {
-    /// This is the first element.
-    First,
-    /// This is neither the first nor the last element.
-    Middle,
-    /// This is the last element.
-    Last,
-    /// This is the only element.
-    Only,
-}
-
-impl<I: Iterator> Iterator for WithPosition<I> {
-    type Item = (Position, I::Item);
-
-    fn next(&mut self) -> Option<Self::Item> {
-        match self.peekable.next() {
-            Some(item) => {
-                if !self.handled_first {
-                    // Haven't seen the first item yet, and there is one to give.
-                    self.handled_first = true;
-                    // Peek to see if this is also the last item,
-                    // in which case tag it as `Only`.
-                    match self.peekable.peek() {
-                        Some(_) => Some((Position::First, item)),
-                        None => Some((Position::Only, item)),
-                    }
-                } else {
-                    // Have seen the first item, and there's something left.
-                    // Peek to see if this is the last item.
-                    match self.peekable.peek() {
-                        Some(_) => Some((Position::Middle, item)),
-                        None => Some((Position::Last, item)),
-                    }
-                }
-            }
-            // Iterator is finished.
-            None => None,
-        }
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.peekable.size_hint()
-    }
-}
-
-impl<I> ExactSizeIterator for WithPosition<I>
-    where I: ExactSizeIterator,
-{ }
-
-impl<I: Iterator> FusedIterator for WithPosition<I> 
-{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_longest.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_longest.rs
deleted file mode 100644
index cb9a7bac..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_longest.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-use std::cmp::Ordering::{Equal, Greater, Less};
-use super::size_hint;
-use std::iter::{Fuse, FusedIterator};
-
-use crate::either_or_both::EitherOrBoth;
-
-// ZipLongest originally written by SimonSapin,
-// and dedicated to itertools https://github.com/rust-lang/rust/pull/19283
-
-/// An iterator which iterates two other iterators simultaneously
-///
-/// This iterator is *fused*.
-///
-/// See [`.zip_longest()`](crate::Itertools::zip_longest) for more information.
-#[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct ZipLongest<T, U> {
-    a: Fuse<T>,
-    b: Fuse<U>,
-}
-
-/// Create a new `ZipLongest` iterator.
-pub fn zip_longest<T, U>(a: T, b: U) -> ZipLongest<T, U>
-    where T: Iterator,
-          U: Iterator
-{
-    ZipLongest {
-        a: a.fuse(),
-        b: b.fuse(),
-    }
-}
-
-impl<T, U> Iterator for ZipLongest<T, U>
-    where T: Iterator,
-          U: Iterator
-{
-    type Item = EitherOrBoth<T::Item, U::Item>;
-
-    #[inline]
-    fn next(&mut self) -> Option<Self::Item> {
-        match (self.a.next(), self.b.next()) {
-            (None, None) => None,
-            (Some(a), None) => Some(EitherOrBoth::Left(a)),
-            (None, Some(b)) => Some(EitherOrBoth::Right(b)),
-            (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)),
-        }
-    }
-
-    #[inline]
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        size_hint::max(self.a.size_hint(), self.b.size_hint())
-    }
-}
-
-impl<T, U> DoubleEndedIterator for ZipLongest<T, U>
-    where T: DoubleEndedIterator + ExactSizeIterator,
-          U: DoubleEndedIterator + ExactSizeIterator
-{
-    #[inline]
-    fn next_back(&mut self) -> Option<Self::Item> {
-        match self.a.len().cmp(&self.b.len()) {
-            Equal => match (self.a.next_back(), self.b.next_back()) {
-                (None, None) => None,
-                (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)),
-                // These can only happen if .len() is inconsistent with .next_back()
-                (Some(a), None) => Some(EitherOrBoth::Left(a)),
-                (None, Some(b)) => Some(EitherOrBoth::Right(b)),
-            },
-            Greater => self.a.next_back().map(EitherOrBoth::Left),
-            Less => self.b.next_back().map(EitherOrBoth::Right),
-        }
-    }
-}
-
-impl<T, U> ExactSizeIterator for ZipLongest<T, U>
-    where T: ExactSizeIterator,
-          U: ExactSizeIterator
-{}
-
-impl<T, U> FusedIterator for ZipLongest<T, U>
-    where T: Iterator,
-          U: Iterator
-{}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/flatten_ok.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/flatten_ok.rs
deleted file mode 100644
index bf835b5..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/flatten_ok.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-use itertools::{assert_equal, Itertools};
-use std::{ops::Range, vec::IntoIter};
-
-fn mix_data() -> IntoIter<Result<Range<i32>, bool>> {
-    vec![Ok(0..2), Err(false), Ok(2..4), Err(true), Ok(4..6)].into_iter()
-}
-
-fn ok_data() -> IntoIter<Result<Range<i32>, bool>> {
-    vec![Ok(0..2), Ok(2..4), Ok(4..6)].into_iter()
-}
-
-#[test]
-fn flatten_ok_mixed_expected_forward() {
-    assert_equal(
-        mix_data().flatten_ok(),
-        vec![
-            Ok(0),
-            Ok(1),
-            Err(false),
-            Ok(2),
-            Ok(3),
-            Err(true),
-            Ok(4),
-            Ok(5),
-        ],
-    );
-}
-
-#[test]
-fn flatten_ok_mixed_expected_reverse() {
-    assert_equal(
-        mix_data().flatten_ok().rev(),
-        vec![
-            Ok(5),
-            Ok(4),
-            Err(true),
-            Ok(3),
-            Ok(2),
-            Err(false),
-            Ok(1),
-            Ok(0),
-        ],
-    );
-}
-
-#[test]
-fn flatten_ok_collect_mixed_forward() {
-    assert_eq!(
-        mix_data().flatten_ok().collect::<Result<Vec<_>, _>>(),
-        Err(false)
-    );
-}
-
-#[test]
-fn flatten_ok_collect_mixed_reverse() {
-    assert_eq!(
-        mix_data().flatten_ok().rev().collect::<Result<Vec<_>, _>>(),
-        Err(true)
-    );
-}
-
-#[test]
-fn flatten_ok_collect_ok_forward() {
-    assert_eq!(
-        ok_data().flatten_ok().collect::<Result<Vec<_>, _>>(),
-        Ok((0..6).collect())
-    );
-}
-
-#[test]
-fn flatten_ok_collect_ok_reverse() {
-    assert_eq!(
-        ok_data().flatten_ok().rev().collect::<Result<Vec<_>, _>>(),
-        Ok((0..6).rev().collect())
-    );
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/macros_hygiene.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/macros_hygiene.rs
deleted file mode 100644
index d111124..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/macros_hygiene.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-#[test]
-fn iproduct_hygiene() {
-    let _ = itertools::iproduct!(0..6);
-    let _ = itertools::iproduct!(0..6, 0..9);
-    let _ = itertools::iproduct!(0..6, 0..9, 0..12);
-}
-
-#[test]
-fn izip_hygiene() {
-    let _ = itertools::izip!(0..6);
-    let _ = itertools::izip!(0..6, 0..9);
-    let _ = itertools::izip!(0..6, 0..9, 0..12);
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/merge_join.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/merge_join.rs
deleted file mode 100644
index 3280b7d..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/merge_join.rs
+++ /dev/null
@@ -1,108 +0,0 @@
-use itertools::EitherOrBoth;
-use itertools::free::merge_join_by;
-
-#[test]
-fn empty() {
-    let left: Vec<u32> = vec![];
-    let right: Vec<u32> = vec![];
-    let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![];
-    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
-        .collect::<Vec<_>>();
-    assert_eq!(expected_result, actual_result);
-}
-
-#[test]
-fn left_only() {
-    let left: Vec<u32> = vec![1,2,3];
-    let right: Vec<u32> = vec![];
-    let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
-        EitherOrBoth::Left(1),
-        EitherOrBoth::Left(2),
-        EitherOrBoth::Left(3)
-    ];
-    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
-        .collect::<Vec<_>>();
-    assert_eq!(expected_result, actual_result);
-}
-
-#[test]
-fn right_only() {
-    let left: Vec<u32> = vec![];
-    let right: Vec<u32> = vec![1,2,3];
-    let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
-        EitherOrBoth::Right(1),
-        EitherOrBoth::Right(2),
-        EitherOrBoth::Right(3)
-    ];
-    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
-        .collect::<Vec<_>>();
-    assert_eq!(expected_result, actual_result);
-}
-
-#[test]
-fn first_left_then_right() {
-    let left: Vec<u32> = vec![1,2,3];
-    let right: Vec<u32> = vec![4,5,6];
-    let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
-        EitherOrBoth::Left(1),
-        EitherOrBoth::Left(2),
-        EitherOrBoth::Left(3),
-        EitherOrBoth::Right(4),
-        EitherOrBoth::Right(5),
-        EitherOrBoth::Right(6)
-    ];
-    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
-        .collect::<Vec<_>>();
-    assert_eq!(expected_result, actual_result);
-}
-
-#[test]
-fn first_right_then_left() {
-    let left: Vec<u32> = vec![4,5,6];
-    let right: Vec<u32> = vec![1,2,3];
-    let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
-        EitherOrBoth::Right(1),
-        EitherOrBoth::Right(2),
-        EitherOrBoth::Right(3),
-        EitherOrBoth::Left(4),
-        EitherOrBoth::Left(5),
-        EitherOrBoth::Left(6)
-    ];
-    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
-        .collect::<Vec<_>>();
-    assert_eq!(expected_result, actual_result);
-}
-
-#[test]
-fn interspersed_left_and_right() {
-    let left: Vec<u32> = vec![1,3,5];
-    let right: Vec<u32> = vec![2,4,6];
-    let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
-        EitherOrBoth::Left(1),
-        EitherOrBoth::Right(2),
-        EitherOrBoth::Left(3),
-        EitherOrBoth::Right(4),
-        EitherOrBoth::Left(5),
-        EitherOrBoth::Right(6)
-    ];
-    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
-        .collect::<Vec<_>>();
-    assert_eq!(expected_result, actual_result);
-}
-
-#[test]
-fn overlapping_left_and_right() {
-    let left: Vec<u32> = vec![1,3,4,6];
-    let right: Vec<u32> = vec![2,3,4,5];
-    let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
-        EitherOrBoth::Left(1),
-        EitherOrBoth::Right(2),
-        EitherOrBoth::Both(3, 3),
-        EitherOrBoth::Both(4, 4),
-        EitherOrBoth::Right(5),
-        EitherOrBoth::Left(6)
-    ];
-    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
-        .collect::<Vec<_>>();
-    assert_eq!(expected_result, actual_result);
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/specializations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/specializations.rs
deleted file mode 100644
index 057e11c..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/specializations.rs
+++ /dev/null
@@ -1,153 +0,0 @@
-use itertools::Itertools;
-use std::fmt::Debug;
-use quickcheck::quickcheck;
-
-struct Unspecialized<I>(I);
-impl<I> Iterator for Unspecialized<I>
-where
-    I: Iterator,
-{
-    type Item = I::Item;
-
-    #[inline(always)]
-    fn next(&mut self) -> Option<Self::Item> {
-        self.0.next()
-    }
-}
-
-macro_rules! check_specialized {
-    ($src:expr, |$it:pat| $closure:expr) => {
-        let $it = $src.clone();
-        let v1 = $closure;
-
-        let $it = Unspecialized($src.clone());
-        let v2 = $closure;
-
-        assert_eq!(v1, v2);
-    }
-}
-
-fn test_specializations<IterItem, Iter>(
-    it: &Iter,
-) where
-    IterItem: Eq + Debug + Clone,
-    Iter: Iterator<Item = IterItem> + Clone,
-{
-    check_specialized!(it, |i| i.count());
-    check_specialized!(it, |i| i.last());
-    check_specialized!(it, |i| i.collect::<Vec<_>>());
-    check_specialized!(it, |i| {
-        let mut parameters_from_fold = vec![];
-        let fold_result = i.fold(vec![], |mut acc, v: IterItem| {
-            parameters_from_fold.push((acc.clone(), v.clone()));
-            acc.push(v);
-            acc
-        });
-        (parameters_from_fold, fold_result)
-    });
-    check_specialized!(it, |mut i| {
-        let mut parameters_from_all = vec![];
-        let first = i.next();
-        let all_result = i.all(|x| {
-            parameters_from_all.push(x.clone());
-            Some(x)==first
-        });
-        (parameters_from_all, all_result)
-    });
-    let size = it.clone().count();
-    for n in 0..size + 2 {
-        check_specialized!(it, |mut i| i.nth(n));
-    }
-    // size_hint is a bit harder to check
-    let mut it_sh = it.clone();
-    for n in 0..size + 2 {
-        let len = it_sh.clone().count();
-        let (min, max) = it_sh.size_hint();
-        assert_eq!(size - n.min(size), len);
-        assert!(min <= len);
-        if let Some(max) = max {
-            assert!(len <= max);
-        }
-        it_sh.next();
-    }
-}
-
-quickcheck! {
-    fn intersperse(v: Vec<u8>) -> () {
-        test_specializations(&v.into_iter().intersperse(0));
-    }
-}
-
-quickcheck! {
-    fn put_back_qc(test_vec: Vec<i32>) -> () {
-        test_specializations(&itertools::put_back(test_vec.iter()));
-        let mut pb = itertools::put_back(test_vec.into_iter());
-        pb.put_back(1);
-        test_specializations(&pb);
-    }
-}
-
-quickcheck! {
-    fn merge_join_by_qc(i1: Vec<usize>, i2: Vec<usize>) -> () {
-        test_specializations(&i1.into_iter().merge_join_by(i2.into_iter(), std::cmp::Ord::cmp));
-    }
-}
-
-quickcheck! {
-    fn map_into(v: Vec<u8>) -> () {
-        test_specializations(&v.into_iter().map_into::<u32>());
-    }
-}
-
-quickcheck! {
-    fn map_ok(v: Vec<Result<u8, char>>) -> () {
-        test_specializations(&v.into_iter().map_ok(|u| u.checked_add(1)));
-    }
-}
-
-quickcheck! {
-    fn process_results(v: Vec<Result<u8, u8>>) -> () {
-        helper(v.iter().copied());
-        helper(v.iter().copied().filter(Result::is_ok));
-
-        fn helper(it: impl Iterator<Item = Result<u8, u8>> + Clone) {
-            macro_rules! check_results_specialized {
-                ($src:expr, |$it:pat| $closure:expr) => {
-                    assert_eq!(
-                        itertools::process_results($src.clone(), |$it| $closure),
-                        itertools::process_results($src.clone(), |i| {
-                            let $it = Unspecialized(i);
-                            $closure
-                        }),
-                    )
-                }
-            }
-
-            check_results_specialized!(it, |i| i.count());
-            check_results_specialized!(it, |i| i.last());
-            check_results_specialized!(it, |i| i.collect::<Vec<_>>());
-            check_results_specialized!(it, |i| {
-                let mut parameters_from_fold = vec![];
-                let fold_result = i.fold(vec![], |mut acc, v| {
-                    parameters_from_fold.push((acc.clone(), v));
-                    acc.push(v);
-                    acc
-                });
-                (parameters_from_fold, fold_result)
-            });
-            check_results_specialized!(it, |mut i| {
-                let mut parameters_from_all = vec![];
-                let first = i.next();
-                let all_result = i.all(|x| {
-                    parameters_from_all.push(x);
-                    Some(x)==first
-                });
-                (parameters_from_all, all_result)
-            });
-            let size = it.clone().count();
-            for n in 0..size + 2 {
-                check_results_specialized!(it, |mut i| i.nth(n));
-            }
-        }
-    }
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/test_std.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/test_std.rs
deleted file mode 100644
index 77207d87..0000000
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/test_std.rs
+++ /dev/null
@@ -1,1184 +0,0 @@
-use quickcheck as qc;
-use rand::{distributions::{Distribution, Standard}, Rng, SeedableRng, rngs::StdRng};
-use rand::{seq::SliceRandom, thread_rng};
-use std::{cmp::min, fmt::Debug, marker::PhantomData};
-use itertools as it;
-use crate::it::Itertools;
-use crate::it::ExactlyOneError;
-use crate::it::multizip;
-use crate::it::multipeek;
-use crate::it::peek_nth;
-use crate::it::free::rciter;
-use crate::it::free::put_back_n;
-use crate::it::FoldWhile;
-use crate::it::cloned;
-use crate::it::iproduct;
-use crate::it::izip;
-
-#[test]
-fn product3() {
-    let prod = iproduct!(0..3, 0..2, 0..2);
-    assert_eq!(prod.size_hint(), (12, Some(12)));
-    let v = prod.collect_vec();
-    for i in 0..3 {
-        for j in 0..2 {
-            for k in 0..2 {
-                assert!((i, j, k) == v[(i * 2 * 2 + j * 2 + k) as usize]);
-            }
-        }
-    }
-    for (_, _, _, _) in iproduct!(0..3, 0..2, 0..2, 0..3) {
-        /* test compiles */
-    }
-}
-
-#[test]
-fn interleave_shortest() {
-    let v0: Vec<i32> = vec![0, 2, 4];
-    let v1: Vec<i32> = vec![1, 3, 5, 7];
-    let it = v0.into_iter().interleave_shortest(v1.into_iter());
-    assert_eq!(it.size_hint(), (6, Some(6)));
-    assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5]);
-
-    let v0: Vec<i32> = vec![0, 2, 4, 6, 8];
-    let v1: Vec<i32> = vec![1, 3, 5];
-    let it = v0.into_iter().interleave_shortest(v1.into_iter());
-    assert_eq!(it.size_hint(), (7, Some(7)));
-    assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5, 6]);
-
-    let i0 = ::std::iter::repeat(0);
-    let v1: Vec<_> = vec![1, 3, 5];
-    let it = i0.interleave_shortest(v1.into_iter());
-    assert_eq!(it.size_hint(), (7, Some(7)));
-
-    let v0: Vec<_> = vec![0, 2, 4];
-    let i1 = ::std::iter::repeat(1);
-    let it = v0.into_iter().interleave_shortest(i1);
-    assert_eq!(it.size_hint(), (6, Some(6)));
-}
-
-#[test]
-fn duplicates_by() {
-    let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"];
-    let ys = ["aa", "bbbb", "cccc"];
-    it::assert_equal(ys.iter(), xs.iter().duplicates_by(|x| x[..2].to_string()));
-    it::assert_equal(ys.iter(), xs.iter().rev().duplicates_by(|x| x[..2].to_string()).rev());
-    let ys_rev = ["ccc", "aa", "bbbbb"];
-    it::assert_equal(ys_rev.iter(), xs.iter().duplicates_by(|x| x[..2].to_string()).rev());
-}
-
-#[test]
-fn duplicates() {
-    let xs = [0, 1, 2, 3, 2, 1, 3];
-    let ys = [2, 1, 3];
-    it::assert_equal(ys.iter(), xs.iter().duplicates());
-    it::assert_equal(ys.iter(), xs.iter().rev().duplicates().rev());
-    let ys_rev = [3, 2, 1];
-    it::assert_equal(ys_rev.iter(), xs.iter().duplicates().rev());
-
-    let xs = [0, 1, 0, 1];
-    let ys = [0, 1];
-    it::assert_equal(ys.iter(), xs.iter().duplicates());
-    it::assert_equal(ys.iter(), xs.iter().rev().duplicates().rev());
-    let ys_rev = [1, 0];
-    it::assert_equal(ys_rev.iter(), xs.iter().duplicates().rev());
-
-    let xs = vec![0, 1, 2, 1, 2];
-    let ys = vec![1, 2];
-    assert_eq!(ys, xs.iter().duplicates().cloned().collect_vec());
-    assert_eq!(ys, xs.iter().rev().duplicates().rev().cloned().collect_vec());
-    let ys_rev = vec![2, 1];
-    assert_eq!(ys_rev, xs.iter().duplicates().rev().cloned().collect_vec());
-}
-
-#[test]
-fn unique_by() {
-    let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"];
-    let ys = ["aaa", "bbbbb", "ccc"];
-    it::assert_equal(ys.iter(), xs.iter().unique_by(|x| x[..2].to_string()));
-    it::assert_equal(ys.iter(), xs.iter().rev().unique_by(|x| x[..2].to_string()).rev());
-    let ys_rev = ["cccc", "aaaaa", "bbbb"];
-    it::assert_equal(ys_rev.iter(), xs.iter().unique_by(|x| x[..2].to_string()).rev());
-}
-
-#[test]
-fn unique() {
-    let xs = [0, 1, 2, 3, 2, 1, 3];
-    let ys = [0, 1, 2, 3];
-    it::assert_equal(ys.iter(), xs.iter().unique());
-    it::assert_equal(ys.iter(), xs.iter().rev().unique().rev());
-    let ys_rev = [3, 1, 2, 0];
-    it::assert_equal(ys_rev.iter(), xs.iter().unique().rev());
-
-    let xs = [0, 1];
-    let ys = [0, 1];
-    it::assert_equal(ys.iter(), xs.iter().unique());
-    it::assert_equal(ys.iter(), xs.iter().rev().unique().rev());
-    let ys_rev = [1, 0];
-    it::assert_equal(ys_rev.iter(), xs.iter().unique().rev());
-}
-
-#[test]
-fn intersperse() {
-    let xs = ["a", "", "b", "c"];
-    let v: Vec<&str> = xs.iter().cloned().intersperse(", ").collect();
-    let text: String = v.concat();
-    assert_eq!(text, "a, , b, c".to_string());
-
-    let ys = [0, 1, 2, 3];
-    let mut it = ys[..0].iter().copied().intersperse(1);
-    assert!(it.next() == None);
-}
-
-#[test]
-fn dedup() {
-    let xs = [0, 1, 1, 1, 2, 1, 3, 3];
-    let ys = [0, 1, 2, 1, 3];
-    it::assert_equal(ys.iter(), xs.iter().dedup());
-    let xs = [0, 0, 0, 0, 0];
-    let ys = [0];
-    it::assert_equal(ys.iter(), xs.iter().dedup());
-
-    let xs = [0, 1, 1, 1, 2, 1, 3, 3];
-    let ys = [0, 1, 2, 1, 3];
-    let mut xs_d = Vec::new();
-    xs.iter().dedup().fold((), |(), &elt| xs_d.push(elt));
-    assert_eq!(&xs_d, &ys);
-}
-
-#[test]
-fn coalesce() {
-    let data = vec![-1., -2., -3., 3., 1., 0., -1.];
-    let it = data.iter().cloned().coalesce(|x, y|
-        if (x >= 0.) == (y >= 0.) {
-            Ok(x + y)
-        } else {
-            Err((x, y))
-        }
-    );
-    itertools::assert_equal(it.clone(), vec![-6., 4., -1.]);
-    assert_eq!(
-        it.fold(vec![], |mut v, n| {
-            v.push(n);
-            v
-        }),
-        vec![-6., 4., -1.]
-    );
-}
-
-#[test]
-fn dedup_by() {
-    let xs = [(0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3)];
-    let ys = [(0, 0), (0, 1), (0, 2), (3, 1), (0, 3)];
-    it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.1==y.1));
-    let xs = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)];
-    let ys = [(0, 1)];
-    it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.0==y.0));
-
-    let xs = [(0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3)];
-    let ys = [(0, 0), (0, 1), (0, 2), (3, 1), (0, 3)];
-    let mut xs_d = Vec::new();
-    xs.iter().dedup_by(|x, y| x.1==y.1).fold((), |(), &elt| xs_d.push(elt));
-    assert_eq!(&xs_d, &ys);
-}
-
-#[test]
-fn dedup_with_count() {
-    let xs: [i32; 8] = [0, 1, 1, 1, 2, 1, 3, 3];
-    let ys: [(usize, &i32); 5] = [(1, &0), (3, &1), (1, &2), (1, &1), (2, &3)];
-
-    it::assert_equal(ys.iter().cloned(), xs.iter().dedup_with_count());
-
-    let xs: [i32; 5] = [0, 0, 0, 0, 0];
-    let ys: [(usize, &i32); 1] = [(5, &0)];
-
-    it::assert_equal(ys.iter().cloned(), xs.iter().dedup_with_count());
-}
-
-
-#[test]
-fn dedup_by_with_count() {
-    let xs = [(0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3)];
-    let ys = [(1, &(0, 0)), (3, &(0, 1)), (1, &(0, 2)), (1, &(3, 1)), (2, &(0, 3))];
-
-    it::assert_equal(ys.iter().cloned(), xs.iter().dedup_by_with_count(|x, y| x.1==y.1));
-
-    let xs = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)];
-    let ys = [( 5, &(0, 1))];
-
-    it::assert_equal(ys.iter().cloned(), xs.iter().dedup_by_with_count(|x, y| x.0==y.0));
-}
-
-#[test]
-fn all_equal() {
-    assert!("".chars().all_equal());
-    assert!("A".chars().all_equal());
-    assert!(!"AABBCCC".chars().all_equal());
-    assert!("AAAAAAA".chars().all_equal());
-    for (_key, mut sub) in &"AABBCCC".chars().group_by(|&x| x) {
-        assert!(sub.all_equal());
-    }
-}
-
-#[test]
-fn all_equal_value() {
-    assert_eq!("".chars().all_equal_value(), Err(None));
-    assert_eq!("A".chars().all_equal_value(), Ok('A'));
-    assert_eq!("AABBCCC".chars().all_equal_value(), Err(Some(('A', 'B'))));
-    assert_eq!("AAAAAAA".chars().all_equal_value(), Ok('A'));
-    {
-        let mut it = [1,2,3].iter().copied();
-        let result = it.all_equal_value();
-        assert_eq!(result, Err(Some((1, 2))));
-        let remaining = it.next();
-        assert_eq!(remaining, Some(3));
-        assert!(it.next().is_none());
-    }
-}
-
-#[test]
-fn all_unique() {
-    assert!("ABCDEFGH".chars().all_unique());
-    assert!(!"ABCDEFGA".chars().all_unique());
-    assert!(::std::iter::empty::<usize>().all_unique());
-}
-
-#[test]
-fn test_put_back_n() {
-    let xs = [0, 1, 1, 1, 2, 1, 3, 3];
-    let mut pb = put_back_n(xs.iter().cloned());
-    pb.next();
-    pb.next();
-    pb.put_back(1);
-    pb.put_back(0);
-    it::assert_equal(pb, xs.iter().cloned());
-}
-
-#[test]
-fn tee() {
-    let xs  = [0, 1, 2, 3];
-    let (mut t1, mut t2) = xs.iter().cloned().tee();
-    assert_eq!(t1.next(), Some(0));
-    assert_eq!(t2.next(), Some(0));
-    assert_eq!(t1.next(), Some(1));
-    assert_eq!(t1.next(), Some(2));
-    assert_eq!(t1.next(), Some(3));
-    assert_eq!(t1.next(), None);
-    assert_eq!(t2.next(), Some(1));
-    assert_eq!(t2.next(), Some(2));
-    assert_eq!(t1.next(), None);
-    assert_eq!(t2.next(), Some(3));
-    assert_eq!(t2.next(), None);
-    assert_eq!(t1.next(), None);
-    assert_eq!(t2.next(), None);
-
-    let (t1, t2) = xs.iter().cloned().tee();
-    it::assert_equal(t1, xs.iter().cloned());
-    it::assert_equal(t2, xs.iter().cloned());
-
-    let (t1, t2) = xs.iter().cloned().tee();
-    it::assert_equal(t1.zip(t2), xs.iter().cloned().zip(xs.iter().cloned()));
-}
-
-
-#[test]
-fn test_rciter() {
-    let xs = [0, 1, 1, 1, 2, 1, 3, 5, 6];
-
-    let mut r1 = rciter(xs.iter().cloned());
-    let mut r2 = r1.clone();
-    assert_eq!(r1.next(), Some(0));
-    assert_eq!(r2.next(), Some(1));
-    let mut z = r1.zip(r2);
-    assert_eq!(z.next(), Some((1, 1)));
-    assert_eq!(z.next(), Some((2, 1)));
-    assert_eq!(z.next(), Some((3, 5)));
-    assert_eq!(z.next(), None);
-
-    // test intoiterator
-    let r1 = rciter(0..5);
-    let mut z = izip!(&r1, r1);
-    assert_eq!(z.next(), Some((0, 1)));
-}
-
-#[allow(deprecated)]
-#[test]
-fn trait_pointers() {
-    struct ByRef<'r, I: ?Sized>(&'r mut I) ;
-
-    impl<'r, X, I: ?Sized> Iterator for ByRef<'r, I> where
-        I: 'r + Iterator<Item=X>
-    {
-        type Item = X;
-        fn next(&mut self) -> Option<Self::Item>
-        {
-            self.0.next()
-        }
-    }
-
-    let mut it = Box::new(0..10) as Box<dyn Iterator<Item=i32>>;
-    assert_eq!(it.next(), Some(0));
-
-    {
-        /* make sure foreach works on non-Sized */
-        let jt: &mut dyn Iterator<Item = i32> = &mut *it;
-        assert_eq!(jt.next(), Some(1));
-
-        {
-            let mut r = ByRef(jt);
-            assert_eq!(r.next(), Some(2));
-        }
-
-        assert_eq!(jt.find_position(|x| *x == 4), Some((1, 4)));
-        jt.foreach(|_| ());
-    }
-}
-
-#[test]
-fn merge_by() {
-    let odd : Vec<(u32, &str)> = vec![(1, "hello"), (3, "world"), (5, "!")];
-    let even = vec![(2, "foo"), (4, "bar"), (6, "baz")];
-    let expected = vec![(1, "hello"), (2, "foo"), (3, "world"), (4, "bar"), (5, "!"), (6, "baz")];
-    let results = odd.iter().merge_by(even.iter(), |a, b| a.0 <= b.0);
-    it::assert_equal(results, expected.iter());
-}
-
-#[test]
-fn merge_by_btree() {
-    use std::collections::BTreeMap;
-    let mut bt1 = BTreeMap::new();
-    bt1.insert("hello", 1);
-    bt1.insert("world", 3);
-    let mut bt2 = BTreeMap::new();
-    bt2.insert("foo", 2);
-    bt2.insert("bar", 4);
-    let results = bt1.into_iter().merge_by(bt2.into_iter(), |a, b| a.0 <= b.0 );
-    let expected = vec![("bar", 4), ("foo", 2), ("hello", 1), ("world", 3)];
-    it::assert_equal(results, expected.into_iter());
-}
-
-#[allow(deprecated)]
-#[test]
-fn kmerge() {
-    let its = (0..4).map(|s| (s..10).step(4));
-
-    it::assert_equal(its.kmerge(), 0..10);
-}
-
-#[allow(deprecated)]
-#[test]
-fn kmerge_2() {
-    let its = vec![3, 2, 1, 0].into_iter().map(|s| (s..10).step(4));
-
-    it::assert_equal(its.kmerge(), 0..10);
-}
-
-#[test]
-fn kmerge_empty() {
-    let its = (0..4).map(|_| 0..0);
-    assert_eq!(its.kmerge().next(), None);
-}
-
-#[test]
-fn kmerge_size_hint() {
-    let its = (0..5).map(|_| (0..10));
-    assert_eq!(its.kmerge().size_hint(), (50, Some(50)));
-}
-
-#[test]
-fn kmerge_empty_size_hint() {
-    let its = (0..5).map(|_| (0..0));
-    assert_eq!(its.kmerge().size_hint(), (0, Some(0)));
-}
-
-#[test]
-fn join() {
-    let many = [1, 2, 3];
-    let one  = [1];
-    let none: Vec<i32> = vec![];
-
-    assert_eq!(many.iter().join(", "), "1, 2, 3");
-    assert_eq!( one.iter().join(", "), "1");
-    assert_eq!(none.iter().join(", "), "");
-}
-
-#[test]
-fn sorted_unstable_by() {
-    let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| {
-        a.cmp(&b)
-    });
-    it::assert_equal(sc, vec![1, 2, 3, 4]);
-
-    let v = (0..5).sorted_unstable_by(|&a, &b| a.cmp(&b).reverse());
-    it::assert_equal(v, vec![4, 3, 2, 1, 0]);
-}
-
-#[test]
-fn sorted_unstable_by_key() {
-    let sc = [3, 4, 1, 2].iter().cloned().sorted_unstable_by_key(|&x| x);
-    it::assert_equal(sc, vec![1, 2, 3, 4]);
-
-    let v = (0..5).sorted_unstable_by_key(|&x| -x);
-    it::assert_equal(v, vec![4, 3, 2, 1, 0]);
-}
-
-#[test]
-fn sorted_by() {
-    let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| {
-        a.cmp(&b)
-    });
-    it::assert_equal(sc, vec![1, 2, 3, 4]);
-
-    let v = (0..5).sorted_by(|&a, &b| a.cmp(&b).reverse());
-    it::assert_equal(v, vec![4, 3, 2, 1, 0]);
-}
-
-qc::quickcheck! {
-    fn k_smallest_range(n: u64, m: u16, k: u16) -> () {
-        // u16 is used to constrain k and m to 0..2¹⁶,
-        //  otherwise the test could use too much memory.
-        let (k, m) = (k as u64, m as u64);
-
-        // Generate a random permutation of n..n+m
-        let i = {
-            let mut v: Vec<u64> = (n..n.saturating_add(m)).collect();
-            v.shuffle(&mut thread_rng());
-            v.into_iter()
-        };
-
-        // Check that taking the k smallest elements yields n..n+min(k, m)
-        it::assert_equal(
-            i.k_smallest(k as usize),
-            n..n.saturating_add(min(k, m))
-        );
-    }
-}
-
-#[derive(Clone, Debug)]
-struct RandIter<T: 'static + Clone + Send, R: 'static + Clone + Rng + SeedableRng + Send = StdRng> {
-    idx: usize,
-    len: usize,
-    rng: R,
-    _t: PhantomData<T>
-}
-
-impl<T: Clone + Send, R: Clone + Rng + SeedableRng + Send> Iterator for RandIter<T, R>
-where Standard: Distribution<T> {
-    type Item = T;
-    fn next(&mut self) -> Option<T> {
-        if self.idx == self.len {
-            None
-        } else {
-            self.idx += 1;
-            Some(self.rng.gen())
-        }
-    }
-}
-
-impl<T: Clone + Send, R: Clone + Rng + SeedableRng + Send> qc::Arbitrary for RandIter<T, R> {
-    fn arbitrary<G: qc::Gen>(g: &mut G) -> Self {
-        RandIter {
-            idx: 0,
-            len: g.size(),
-            rng: R::seed_from_u64(g.next_u64()),
-            _t : PhantomData{},
-        }
-    }
-}
-
-// Check that taking the k smallest is the same as
-//  sorting then taking the k first elements
-fn k_smallest_sort<I>(i: I, k: u16)
-where
-    I: Iterator + Clone,
-    I::Item: Ord + Debug,
-{
-    let j = i.clone();
-    let k = k as usize;
-    it::assert_equal(
-        i.k_smallest(k),
-        j.sorted().take(k)
-    )
-}
-
-macro_rules! generic_test {
-    ($f:ident, $($t:ty),+) => {
-        $(paste::item! {
-            qc::quickcheck! {
-                fn [< $f _ $t >](i: RandIter<$t>, k: u16) -> () {
-                    $f(i, k)
-                }
-            }
-        })+
-    };
-}
-
-generic_test!(k_smallest_sort, u8, u16, u32, u64, i8, i16, i32, i64);
-
-#[test]
-fn sorted_by_key() {
-    let sc = [3, 4, 1, 2].iter().cloned().sorted_by_key(|&x| x);
-    it::assert_equal(sc, vec![1, 2, 3, 4]);
-
-    let v = (0..5).sorted_by_key(|&x| -x);
-    it::assert_equal(v, vec![4, 3, 2, 1, 0]);
-}
-
-#[test]
-fn sorted_by_cached_key() {
-    // Track calls to key function
-    let mut ncalls = 0;
-
-    let sorted = [3, 4, 1, 2].iter().cloned().sorted_by_cached_key(|&x| {
-        ncalls += 1;
-        x.to_string()
-    });
-    it::assert_equal(sorted, vec![1, 2, 3, 4]);
-    // Check key function called once per element
-    assert_eq!(ncalls, 4);
-
-    let mut ncalls = 0;
-
-    let sorted = (0..5).sorted_by_cached_key(|&x| {
-        ncalls += 1;
-        -x
-    });
-    it::assert_equal(sorted, vec![4, 3, 2, 1, 0]);
-    // Check key function called once per element
-    assert_eq!(ncalls, 5);
-}
-
-#[test]
-fn test_multipeek() {
-    let nums = vec![1u8,2,3,4,5];
-
-    let mp = multipeek(nums.iter().copied());
-    assert_eq!(nums, mp.collect::<Vec<_>>());
-
-    let mut mp = multipeek(nums.iter().copied());
-    assert_eq!(mp.peek(), Some(&1));
-    assert_eq!(mp.next(), Some(1));
-    assert_eq!(mp.peek(), Some(&2));
-    assert_eq!(mp.peek(), Some(&3));
-    assert_eq!(mp.next(), Some(2));
-    assert_eq!(mp.peek(), Some(&3));
-    assert_eq!(mp.peek(), Some(&4));
-    assert_eq!(mp.peek(), Some(&5));
-    assert_eq!(mp.peek(), None);
-    assert_eq!(mp.next(), Some(3));
-    assert_eq!(mp.next(), Some(4));
-    assert_eq!(mp.peek(), Some(&5));
-    assert_eq!(mp.peek(), None);
-    assert_eq!(mp.next(), Some(5));
-    assert_eq!(mp.next(), None);
-    assert_eq!(mp.peek(), None);
-}
-
-#[test]
-fn test_multipeek_reset() {
-    let data = [1, 2, 3, 4];
-
-    let mut mp = multipeek(cloned(&data));
-    assert_eq!(mp.peek(), Some(&1));
-    assert_eq!(mp.next(), Some(1));
-    assert_eq!(mp.peek(), Some(&2));
-    assert_eq!(mp.peek(), Some(&3));
-    mp.reset_peek();
-    assert_eq!(mp.peek(), Some(&2));
-    assert_eq!(mp.next(), Some(2));
-}
-
-#[test]
-fn test_multipeek_peeking_next() {
-    use crate::it::PeekingNext;
-    let nums = vec![1u8,2,3,4,5,6,7];
-
-    let mut mp = multipeek(nums.iter().copied());
-    assert_eq!(mp.peeking_next(|&x| x != 0), Some(1));
-    assert_eq!(mp.next(), Some(2));
-    assert_eq!(mp.peek(), Some(&3));
-    assert_eq!(mp.peek(), Some(&4));
-    assert_eq!(mp.peeking_next(|&x| x == 3), Some(3));
-    assert_eq!(mp.peek(), Some(&4));
-    assert_eq!(mp.peeking_next(|&x| x != 4), None);
-    assert_eq!(mp.peeking_next(|&x| x == 4), Some(4));
-    assert_eq!(mp.peek(), Some(&5));
-    assert_eq!(mp.peek(), Some(&6));
-    assert_eq!(mp.peeking_next(|&x| x != 5), None);
-    assert_eq!(mp.peek(), Some(&7));
-    assert_eq!(mp.peeking_next(|&x| x == 5), Some(5));
-    assert_eq!(mp.peeking_next(|&x| x == 6), Some(6));
-    assert_eq!(mp.peek(), Some(&7));
-    assert_eq!(mp.peek(), None);
-    assert_eq!(mp.next(), Some(7));
-    assert_eq!(mp.peek(), None);
-}
-
-#[test]
-fn test_peek_nth() {
-    let nums = vec![1u8,2,3,4,5];
-
-    let iter = peek_nth(nums.iter().copied());
-    assert_eq!(nums, iter.collect::<Vec<_>>());
-
-    let mut iter = peek_nth(nums.iter().copied());
-
-    assert_eq!(iter.peek_nth(0), Some(&1));
-    assert_eq!(iter.peek_nth(0), Some(&1));
-    assert_eq!(iter.next(), Some(1));
-
-    assert_eq!(iter.peek_nth(0), Some(&2));
-    assert_eq!(iter.peek_nth(1), Some(&3));
-    assert_eq!(iter.next(), Some(2));
-
-    assert_eq!(iter.peek_nth(0), Some(&3));
-    assert_eq!(iter.peek_nth(1), Some(&4));
-    assert_eq!(iter.peek_nth(2), Some(&5));
-    assert_eq!(iter.peek_nth(3), None);
-
-    assert_eq!(iter.next(), Some(3));
-    assert_eq!(iter.next(), Some(4));
-
-    assert_eq!(iter.peek_nth(0), Some(&5));
-    assert_eq!(iter.peek_nth(1), None);
-    assert_eq!(iter.next(), Some(5));
-    assert_eq!(iter.next(), None);
-
-    assert_eq!(iter.peek_nth(0), None);
-    assert_eq!(iter.peek_nth(1), None);
-}
-
-#[test]
-fn test_peek_nth_peeking_next() {
-    use it::PeekingNext;
-    let nums = vec![1u8,2,3,4,5,6,7];
-    let mut iter = peek_nth(nums.iter().copied());
-
-    assert_eq!(iter.peeking_next(|&x| x != 0), Some(1));
-    assert_eq!(iter.next(), Some(2));
-
-    assert_eq!(iter.peek_nth(0), Some(&3));
-    assert_eq!(iter.peek_nth(1), Some(&4));
-    assert_eq!(iter.peeking_next(|&x| x == 3), Some(3));
-    assert_eq!(iter.peek(), Some(&4));
-
-    assert_eq!(iter.peeking_next(|&x| x != 4), None);
-    assert_eq!(iter.peeking_next(|&x| x == 4), Some(4));
-    assert_eq!(iter.peek_nth(0), Some(&5));
-    assert_eq!(iter.peek_nth(1), Some(&6));
-
-    assert_eq!(iter.peeking_next(|&x| x != 5), None);
-    assert_eq!(iter.peek(), Some(&5));
-
-    assert_eq!(iter.peeking_next(|&x| x == 5), Some(5));
-    assert_eq!(iter.peeking_next(|&x| x == 6), Some(6));
-    assert_eq!(iter.peek_nth(0), Some(&7));
-    assert_eq!(iter.peek_nth(1), None);
-    assert_eq!(iter.next(), Some(7));
-    assert_eq!(iter.peek(), None);
-}
-
-#[test]
-fn pad_using() {
-    it::assert_equal((0..0).pad_using(1, |_| 1), 1..2);
-
-    let v: Vec<usize> = vec![0, 1, 2];
-    let r = v.into_iter().pad_using(5, |n| n);
-    it::assert_equal(r, vec![0, 1, 2, 3, 4]);
-
-    let v: Vec<usize> = vec![0, 1, 2];
-    let r = v.into_iter().pad_using(1, |_| panic!());
-    it::assert_equal(r, vec![0, 1, 2]);
-}
-
-#[test]
-fn group_by() {
-    for (ch1, sub) in &"AABBCCC".chars().group_by(|&x| x) {
-        for ch2 in sub {
-            assert_eq!(ch1, ch2);
-        }
-    }
-
-    for (ch1, sub) in &"AAABBBCCCCDDDD".chars().group_by(|&x| x) {
-        for ch2 in sub {
-            assert_eq!(ch1, ch2);
-            if ch1 == 'C' {
-                break;
-            }
-        }
-    }
-
-    let toupper = |ch: &char| ch.to_uppercase().next().unwrap();
-
-    // try all possible orderings
-    for indices in permutohedron::Heap::new(&mut [0, 1, 2, 3]) {
-        let groups = "AaaBbbccCcDDDD".chars().group_by(&toupper);
-        let mut subs = groups.into_iter().collect_vec();
-
-        for &idx in &indices[..] {
-            let (key, text) = match idx {
-                 0 => ('A', "Aaa".chars()),
-                 1 => ('B', "Bbb".chars()),
-                 2 => ('C', "ccCc".chars()),
-                 3 => ('D', "DDDD".chars()),
-                 _ => unreachable!(),
-            };
-            assert_eq!(key, subs[idx].0);
-            it::assert_equal(&mut subs[idx].1, text);
-        }
-    }
-
-    let groups = "AAABBBCCCCDDDD".chars().group_by(|&x| x);
-    let mut subs = groups.into_iter().map(|(_, g)| g).collect_vec();
-
-    let sd = subs.pop().unwrap();
-    let sc = subs.pop().unwrap();
-    let sb = subs.pop().unwrap();
-    let sa = subs.pop().unwrap();
-    for (a, b, c, d) in multizip((sa, sb, sc, sd)) {
-        assert_eq!(a, 'A');
-        assert_eq!(b, 'B');
-        assert_eq!(c, 'C');
-        assert_eq!(d, 'D');
-    }
-
-    // check that the key closure is called exactly n times
-    {
-        let mut ntimes = 0;
-        let text = "AABCCC";
-        for (_, sub) in &text.chars().group_by(|&x| { ntimes += 1; x}) {
-            for _ in sub {
-            }
-        }
-        assert_eq!(ntimes, text.len());
-    }
-
-    {
-        let mut ntimes = 0;
-        let text = "AABCCC";
-        for _ in &text.chars().group_by(|&x| { ntimes += 1; x}) {
-        }
-        assert_eq!(ntimes, text.len());
-    }
-
-    {
-        let text = "ABCCCDEEFGHIJJKK";
-        let gr = text.chars().group_by(|&x| x);
-        it::assert_equal(gr.into_iter().flat_map(|(_, sub)| sub), text.chars());
-    }
-}
-
-#[test]
-fn group_by_lazy_2() {
-    let data = vec![0, 1];
-    let groups = data.iter().group_by(|k| *k);
-    let gs = groups.into_iter().collect_vec();
-    it::assert_equal(data.iter(), gs.into_iter().flat_map(|(_k, g)| g));
-
-    let data = vec![0, 1, 1, 0, 0];
-    let groups = data.iter().group_by(|k| *k);
-    let mut gs = groups.into_iter().collect_vec();
-    gs[1..].reverse();
-    it::assert_equal(&[0, 0, 0, 1, 1], gs.into_iter().flat_map(|(_, g)| g));
-
-    let grouper = data.iter().group_by(|k| *k);
-    let mut groups = Vec::new();
-    for (k, group) in &grouper {
-        if *k == 1 {
-            groups.push(group);
-        }
-    }
-    it::assert_equal(&mut groups[0], &[1, 1]);
-
-    let data = vec![0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
-    let grouper = data.iter().group_by(|k| *k);
-    let mut groups = Vec::new();
-    for (i, (_, group)) in grouper.into_iter().enumerate() {
-        if i < 2 {
-            groups.push(group);
-        } else if i < 4 {
-            for _ in group {
-            }
-        } else {
-            groups.push(group);
-        }
-    }
-    it::assert_equal(&mut groups[0], &[0, 0, 0]);
-    it::assert_equal(&mut groups[1], &[1, 1]);
-    it::assert_equal(&mut groups[2], &[3, 3]);
-
-    // use groups as chunks
-    let data = vec![0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
-    let mut i = 0;
-    let grouper = data.iter().group_by(move |_| { let k = i / 3; i += 1; k });
-    for (i, group) in &grouper {
-        match i {
-            0 => it::assert_equal(group, &[0, 0, 0]),
-            1 => it::assert_equal(group, &[1, 1, 0]),
-            2 => it::assert_equal(group, &[0, 2, 2]),
-            3 => it::assert_equal(group, &[3, 3]),
-            _ => unreachable!(),
-        }
-    }
-}
-
-#[test]
-fn group_by_lazy_3() {
-    // test consuming each group on the lap after it was produced
-    let data = vec![0, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2];
-    let grouper = data.iter().group_by(|elt| *elt);
-    let mut last = None;
-    for (key, group) in &grouper {
-        if let Some(gr) = last.take() {
-            for elt in gr {
-                assert!(elt != key && i32::abs(elt - key) == 1);
-            }
-        }
-        last = Some(group);
-    }
-}
-
-#[test]
-fn chunks() {
-    let data = vec![0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
-    let grouper = data.iter().chunks(3);
-    for (i, chunk) in grouper.into_iter().enumerate() {
-        match i {
-            0 => it::assert_equal(chunk, &[0, 0, 0]),
-            1 => it::assert_equal(chunk, &[1, 1, 0]),
-            2 => it::assert_equal(chunk, &[0, 2, 2]),
-            3 => it::assert_equal(chunk, &[3, 3]),
-            _ => unreachable!(),
-        }
-    }
-}
-
-#[test]
-fn concat_empty() {
-    let data: Vec<Vec<()>> = Vec::new();
-    assert_eq!(data.into_iter().concat(), Vec::new())
-}
-
-#[test]
-fn concat_non_empty() {
-    let data = vec![vec![1,2,3], vec![4,5,6], vec![7,8,9]];
-    assert_eq!(data.into_iter().concat(), vec![1,2,3,4,5,6,7,8,9])
-}
-
-#[test]
-fn combinations() {
-    assert!((1..3).combinations(5).next().is_none());
-
-    let it = (1..3).combinations(2);
-    it::assert_equal(it, vec![
-        vec![1, 2],
-        ]);
-
-    let it = (1..5).combinations(2);
-    it::assert_equal(it, vec![
-        vec![1, 2],
-        vec![1, 3],
-        vec![1, 4],
-        vec![2, 3],
-        vec![2, 4],
-        vec![3, 4],
-        ]);
-
-    it::assert_equal((0..0).tuple_combinations::<(_, _)>(), <Vec<_>>::new());
-    it::assert_equal((0..1).tuple_combinations::<(_, _)>(), <Vec<_>>::new());
-    it::assert_equal((0..2).tuple_combinations::<(_, _)>(), vec![(0, 1)]);
-
-    it::assert_equal((0..0).combinations(2), <Vec<Vec<_>>>::new());
-    it::assert_equal((0..1).combinations(1), vec![vec![0]]);
-    it::assert_equal((0..2).combinations(1), vec![vec![0], vec![1]]);
-    it::assert_equal((0..2).combinations(2), vec![vec![0, 1]]);
-}
-
-#[test]
-fn combinations_of_too_short() {
-    for i in 1..10 {
-        assert!((0..0).combinations(i).next().is_none());
-        assert!((0..i - 1).combinations(i).next().is_none());
-    }
-}
-
-
-#[test]
-fn combinations_zero() {
-    it::assert_equal((1..3).combinations(0), vec![vec![]]);
-    it::assert_equal((0..0).combinations(0), vec![vec![]]);
-}
-
-#[test]
-fn permutations_zero() {
-    it::assert_equal((1..3).permutations(0), vec![vec![]]);
-    it::assert_equal((0..0).permutations(0), vec![vec![]]);
-}
-
-#[test]
-fn combinations_with_replacement() {
-    // Pool smaller than n
-    it::assert_equal((0..1).combinations_with_replacement(2), vec![vec![0, 0]]);
-    // Pool larger than n
-    it::assert_equal(
-        (0..3).combinations_with_replacement(2),
-        vec![
-            vec![0, 0],
-            vec![0, 1],
-            vec![0, 2],
-            vec![1, 1],
-            vec![1, 2],
-            vec![2, 2],
-        ],
-    );
-    // Zero size
-    it::assert_equal(
-        (0..3).combinations_with_replacement(0),
-        vec![vec![]],
-    );
-    // Zero size on empty pool
-    it::assert_equal(
-        (0..0).combinations_with_replacement(0),
-        vec![vec![]],
-    );
-    // Empty pool
-    it::assert_equal(
-        (0..0).combinations_with_replacement(2),
-        <Vec<Vec<_>>>::new(),
-    );
-}
-
-#[test]
-fn powerset() {
-    it::assert_equal((0..0).powerset(), vec![vec![]]);
-    it::assert_equal((0..1).powerset(), vec![vec![], vec![0]]);
-    it::assert_equal((0..2).powerset(), vec![vec![], vec![0], vec![1], vec![0, 1]]);
-    it::assert_equal((0..3).powerset(), vec![
-        vec![],
-        vec![0], vec![1], vec![2],
-        vec![0, 1], vec![0, 2], vec![1, 2],
-        vec![0, 1, 2]
-    ]);
-
-    assert_eq!((0..4).powerset().count(), 1 << 4);
-    assert_eq!((0..8).powerset().count(), 1 << 8);
-    assert_eq!((0..16).powerset().count(), 1 << 16);
-}
-
-#[test]
-fn diff_mismatch() {
-    let a = vec![1, 2, 3, 4];
-    let b = vec![1.0, 5.0, 3.0, 4.0];
-    let b_map = b.into_iter().map(|f| f as i32);
-    let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b);
-
-    assert!(match diff {
-        Some(it::Diff::FirstMismatch(1, _, from_diff)) =>
-            from_diff.collect::<Vec<_>>() == vec![5, 3, 4],
-        _ => false,
-    });
-}
-
-#[test]
-fn diff_longer() {
-    let a = vec![1, 2, 3, 4];
-    let b = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
-    let b_map = b.into_iter().map(|f| f as i32);
-    let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b);
-
-    assert!(match diff {
-        Some(it::Diff::Longer(_, remaining)) =>
-            remaining.collect::<Vec<_>>() == vec![5, 6],
-        _ => false,
-    });
-}
-
-#[test]
-fn diff_shorter() {
-    let a = vec![1, 2, 3, 4];
-    let b = vec![1.0, 2.0];
-    let b_map = b.into_iter().map(|f| f as i32);
-    let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b);
-
-    assert!(match diff {
-        Some(it::Diff::Shorter(len, _)) => len == 2,
-        _ => false,
-    });
-}
-
-#[test]
-fn extrema_set() {
-    use std::cmp::Ordering;
-
-    // A peculiar type: Equality compares both tuple items, but ordering only the
-    // first item. Used to distinguish equal elements.
-    #[derive(Clone, Debug, PartialEq, Eq)]
-    struct Val(u32, u32);
-
-    impl PartialOrd<Val> for Val {
-        fn partial_cmp(&self, other: &Val) -> Option<Ordering> {
-            self.0.partial_cmp(&other.0)
-        }
-    }
-
-    impl Ord for Val {
-        fn cmp(&self, other: &Val) -> Ordering {
-            self.0.cmp(&other.0)
-        }
-    }
-
-    assert_eq!(None::<u32>.iter().min_set(), Vec::<&u32>::new());
-    assert_eq!(None::<u32>.iter().max_set(), Vec::<&u32>::new());
-
-    assert_eq!(Some(1u32).iter().min_set(), vec![&1]);
-    assert_eq!(Some(1u32).iter().max_set(), vec![&1]);
-
-    let data = vec![Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)];
-
-    let min_set = data.iter().min_set();
-    assert_eq!(min_set, vec![&Val(0, 1), &Val(0, 2)]);
-
-    let min_set_by_key = data.iter().min_set_by_key(|v| v.1);
-    assert_eq!(min_set_by_key, vec![&Val(2, 0), &Val(1, 0)]);
-
-    let min_set_by = data.iter().min_set_by(|x, y| x.1.cmp(&y.1));
-    assert_eq!(min_set_by, vec![&Val(2, 0), &Val(1, 0)]);
-
-    let max_set = data.iter().max_set();
-    assert_eq!(max_set, vec![&Val(2, 0), &Val(2, 1)]);
-
-    let max_set_by_key = data.iter().max_set_by_key(|v| v.1);
-    assert_eq!(max_set_by_key, vec![&Val(0, 2)]);
-
-    let max_set_by = data.iter().max_set_by(|x, y| x.1.cmp(&y.1));
-    assert_eq!(max_set_by, vec![&Val(0, 2)]);
-}
-
-#[test]
-fn minmax() {
-    use std::cmp::Ordering;
-    use crate::it::MinMaxResult;
-
-    // A peculiar type: Equality compares both tuple items, but ordering only the
-    // first item.  This is so we can check the stability property easily.
-    #[derive(Clone, Debug, PartialEq, Eq)]
-    struct Val(u32, u32);
-
-    impl PartialOrd<Val> for Val {
-        fn partial_cmp(&self, other: &Val) -> Option<Ordering> {
-            self.0.partial_cmp(&other.0)
-        }
-    }
-
-    impl Ord for Val {
-        fn cmp(&self, other: &Val) -> Ordering {
-            self.0.cmp(&other.0)
-        }
-    }
-
-    assert_eq!(None::<Option<u32>>.iter().minmax(), MinMaxResult::NoElements);
-
-    assert_eq!(Some(1u32).iter().minmax(), MinMaxResult::OneElement(&1));
-
-    let data = vec![Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)];
-
-    let minmax = data.iter().minmax();
-    assert_eq!(minmax, MinMaxResult::MinMax(&Val(0, 1), &Val(2, 1)));
-
-    let (min, max) = data.iter().minmax_by_key(|v| v.1).into_option().unwrap();
-    assert_eq!(min, &Val(2, 0));
-    assert_eq!(max, &Val(0, 2));
-
-    let (min, max) = data.iter().minmax_by(|x, y| x.1.cmp(&y.1)).into_option().unwrap();
-    assert_eq!(min, &Val(2, 0));
-    assert_eq!(max, &Val(0, 2));
-}
-
-#[test]
-fn format() {
-    let data = [0, 1, 2, 3];
-    let ans1 = "0, 1, 2, 3";
-    let ans2 = "0--1--2--3";
-
-    let t1 = format!("{}", data.iter().format(", "));
-    assert_eq!(t1, ans1);
-    let t2 = format!("{:?}", data.iter().format("--"));
-    assert_eq!(t2, ans2);
-
-    let dataf = [1.1, 5.71828, -22.];
-    let t3 = format!("{:.2e}", dataf.iter().format(", "));
-    assert_eq!(t3, "1.10e0, 5.72e0, -2.20e1");
-}
-
-#[test]
-fn while_some() {
-    let ns = (1..10).map(|x| if x % 5 != 0 { Some(x) } else { None })
-                    .while_some();
-    it::assert_equal(ns, vec![1, 2, 3, 4]);
-}
-
-#[allow(deprecated)]
-#[test]
-fn fold_while() {
-    let mut iterations = 0;
-    let vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
-    let sum = vec.into_iter().fold_while(0, |acc, item| {
-        iterations += 1;
-        let new_sum = acc + item;
-        if new_sum <= 20 {
-            FoldWhile::Continue(new_sum)
-        } else {
-            FoldWhile::Done(acc)
-        }
-    }).into_inner();
-    assert_eq!(iterations, 6);
-    assert_eq!(sum, 15);
-}
-
-#[test]
-fn tree_fold1() {
-    let x = [
-        "",
-        "0",
-        "0 1 x",
-        "0 1 x 2 x",
-        "0 1 x 2 3 x x",
-        "0 1 x 2 3 x x 4 x",
-        "0 1 x 2 3 x x 4 5 x x",
-        "0 1 x 2 3 x x 4 5 x 6 x x",
-        "0 1 x 2 3 x x 4 5 x 6 7 x x x",
-        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 x",
-        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x x",
-        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 x x",
-        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x x",
-        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 x x",
-        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x x x",
-        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x 14 x x x",
-        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x 14 15 x x x x",
-    ];
-    for (i, &s) in x.iter().enumerate() {
-        let expected = if s.is_empty() { None } else { Some(s.to_string()) };
-        let num_strings = (0..i).map(|x| x.to_string());
-        let actual = num_strings.tree_fold1(|a, b| format!("{} {} x", a, b));
-        assert_eq!(actual, expected);
-    }
-}
-
-#[test]
-fn exactly_one_question_mark_syntax_works() {
-    exactly_one_question_mark_return().unwrap_err();
-}
-
-fn exactly_one_question_mark_return() -> Result<(), ExactlyOneError<std::slice::Iter<'static, ()>>> {
-    [].iter().exactly_one()?;
-    Ok(())
-}
-
-#[test]
-fn multiunzip() {
-    let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = [(0, 1, 2), (3, 4, 5), (6, 7, 8)].iter().cloned().multiunzip();
-    assert_eq!((a, b, c), (vec![0, 3, 6], vec![1, 4, 7], vec![2, 5, 8]));
-    let (): () = [(), (), ()].iter().cloned().multiunzip();
-    let t: (Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>) = [(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)].iter().cloned().multiunzip();
-    assert_eq!(t, (vec![0], vec![1], vec![2], vec![3], vec![4], vec![5], vec![6], vec![7], vec![8], vec![9], vec![10], vec![11]));
-}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.cargo-checksum.json b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.cargo-checksum.json
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.cargo-checksum.json
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.cargo-checksum.json
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.cargo_vcs_info.json
new file mode 100644
index 0000000..a6fc921
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "a015a6831525ee1637df747d3f530a627d9741bf"
+  },
+  "path_in_vcs": ""
+}
\ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.codecov.yml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.codecov.yml
new file mode 100644
index 0000000..d06394a
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.codecov.yml
@@ -0,0 +1,7 @@
+coverage:
+  status:
+    project:
+      default:
+        target: auto
+        # Allow a tiny drop of overall project coverage in PR to reduce spurious failures.
+        threshold: 0.25%
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.github/dependabot.yml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/dependabot.yml
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.github/dependabot.yml
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/dependabot.yml
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/workflows/ci.yml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/workflows/ci.yml
new file mode 100644
index 0000000..8e2a9d6
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/workflows/ci.yml
@@ -0,0 +1,101 @@
+name: CI
+
+on:
+  pull_request:
+    paths-ignore:
+      - "**.md"
+  merge_group:
+    paths-ignore:
+      - "**.md"
+
+jobs:
+  check:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        features:
+          [
+            "",
+            "--no-default-features",
+            "--no-default-features --features use_alloc",
+            "--all-targets --all-features",
+          ]
+    steps:
+      - uses: actions/checkout@v4
+      - uses: dtolnay/rust-toolchain@stable
+        with:
+          components: clippy
+      - run: RUSTFLAGS="--deny warnings" cargo clippy ${{ matrix.features }}
+
+  doc:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - uses: dtolnay/rust-toolchain@stable
+      - run: RUSTDOCFLAGS="-Dwarnings" cargo doc --all-features
+
+  msrv:
+    runs-on: ubuntu-latest
+    env:
+      CARGO_NET_GIT_FETCH_WITH_CLI: true
+    steps:
+      - uses: actions/checkout@v4
+      - uses: taiki-e/install-action@cargo-no-dev-deps
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          # Here, it does not trigger a PR from dependabot.
+          toolchain: 1.63.0
+      - run: cargo no-dev-deps check
+
+  test:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - uses: dtolnay/rust-toolchain@stable
+      - run: cargo test --all-features
+
+  miri:
+    runs-on: ubuntu-latest
+    env:
+      CARGO_TERM_COLOR: always
+    steps:
+      - uses: actions/checkout@v4
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: nightly
+          components: miri
+      - uses: taiki-e/install-action@nextest
+      - run: |
+          cargo miri nextest run --all-features
+          cargo miri test --doc
+
+  check-format:
+    name: check format
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: stable
+          components: rustfmt
+      - run: cargo fmt --check
+
+  semver-checks:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - uses: dtolnay/rust-toolchain@stable
+      - uses: obi1kenobi/cargo-semver-checks-action@v2.4
+        with:
+          rust-toolchain: manual
+          feature-group: all-features
+
+  # Used to signal to branch protections that all other jobs have succeeded.
+  all-jobs-succeed:
+    name: All checks succeeded
+    if: success()
+    runs-on: ubuntu-latest
+    needs: [check, msrv, test, check-format, doc]
+    steps:
+      - name: Mark the job as successful
+        run: exit 0
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/workflows/coverage.yml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/workflows/coverage.yml
new file mode 100644
index 0000000..5c084565
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.github/workflows/coverage.yml
@@ -0,0 +1,34 @@
+on:
+  push:
+    branches: [master]
+    paths-ignore: 
+      - "**.md" 
+  pull_request:
+    paths-ignore: 
+      - "**.md" 
+
+name: Code Coverage
+
+jobs:
+  coverage:
+    name: coverage
+    runs-on: ubuntu-latest
+    steps:
+      - name: checkout source
+        uses: actions/checkout@v4
+
+      - name: Install nightly toolchain
+        uses: dtolnay/rust-toolchain@nightly
+        with:
+          components: llvm-tools-preview
+
+      - name: Install cargo-llvm-cov
+        uses: taiki-e/install-action@cargo-llvm-cov
+
+      - name: Run llvm-cov
+        run: cargo llvm-cov --all-features --doctests --workspace --lcov --output-path lcov.info
+
+      - name: Upload coverage to Codecov
+        uses: codecov/codecov-action@v4
+        with:
+          files: lcov.info
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.gitignore b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.gitignore
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/.gitignore
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/.gitignore
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/CHANGELOG.md b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/CHANGELOG.md
similarity index 76%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/CHANGELOG.md
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/CHANGELOG.md
index 8d7404e..6b08f68 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/CHANGELOG.md
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/CHANGELOG.md
@@ -1,5 +1,166 @@
 # Changelog
 
+## 0.14.0
+
+### Breaking
+- Increased MSRV to 1.63.0 (#960)
+- Removed generic parameter from `cons_tuples` (#988)
+
+### Added
+- Added `array_combinations` (#991)
+- Added `k_smallest_relaxed` and variants (#925)
+- Added `next_array` and `collect_array` (#560)
+- Implemented `DoubleEndedIterator` for `FilterOk` (#948)
+- Implemented `DoubleEndedIterator` for `FilterMapOk` (#950)
+
+### Changed
+- Allow `Q: ?Sized` in `Itertools::contains` (#971)
+- Improved hygiene of `chain!` (#943)
+- Improved `into_group_map_by` documentation (#1000)
+- Improved `tree_reduce` documentation (#955)
+- Improved discoverability of `merge_join_by` (#966)
+- Improved discoverability of `take_while_inclusive` (#972)
+- Improved documentation of `find_or_last` and `find_or_first` (#984)
+- Prevented exponentially large type sizes in `tuple_combinations` (#945)
+- Added `track_caller` attr for `asser_equal` (#976)
+
+### Notable Internal Changes
+- Fixed clippy lints (#956, #987, #1008)
+- Addressed warnings within doctests (#964)
+- CI: Run most tests with miri (#961)
+- CI: Speed up "cargo-semver-checks" action (#938)
+- Changed an instance of `default_features` in `Cargo.toml` to `default-features` (#985)
+
+## 0.13.0
+
+### Breaking
+- Removed implementation of `DoubleEndedIterator` for `ConsTuples` (#853)
+- Made `MultiProduct` fused and fixed on an empty iterator (#835, #834)
+- Changed `iproduct!` to return tuples for maxi one iterator too (#870)
+- Changed `PutBack::put_back` to return the old value (#880)
+- Removed deprecated `repeat_call, Itertools::{foreach, step, map_results, fold_results}` (#878)
+- Removed `TakeWhileInclusive::new` (#912)
+
+### Added
+- Added `Itertools::{smallest_by, smallest_by_key, largest, largest_by, largest_by_key}` (#654, #885)
+- Added `Itertools::tail` (#899)
+- Implemented `DoubleEndedIterator` for `ProcessResults` (#910)
+- Implemented `Debug` for `FormatWith` (#931)
+- Added `Itertools::get` (#891)
+
+### Changed
+- Deprecated `Itertools::group_by` (renamed `chunk_by`) (#866, #879)
+- Deprecated `unfold` (use `std::iter::from_fn` instead) (#871)
+- Optimized `GroupingMapBy` (#873, #876)
+- Relaxed `Fn` bounds to `FnMut` in `diff_with, Itertools::into_group_map_by` (#886)
+- Relaxed `Debug/Clone` bounds for `MapInto` (#889)
+- Documented the `use_alloc` feature (#887)
+- Optimized `Itertools::set_from` (#888)
+- Removed badges in `README.md` (#890)
+- Added "no-std" categories in `Cargo.toml` (#894)
+- Fixed `Itertools::k_smallest` on short unfused iterators (#900)
+- Deprecated `Itertools::tree_fold1` (renamed `tree_reduce`) (#895)
+- Deprecated `GroupingMap::fold_first` (renamed `reduce`) (#902)
+- Fixed `Itertools::k_smallest(0)` to consume the iterator, optimized `Itertools::k_smallest(1)` (#909)
+- Specialized `Combinations::nth` (#914)
+- Specialized `MergeBy::fold` (#920)
+- Specialized `CombinationsWithReplacement::nth` (#923)
+- Specialized `FlattenOk::{fold, rfold}` (#927)
+- Specialized `Powerset::nth` (#924)
+- Documentation fixes (#882, #936)
+- Fixed `assert_equal` for iterators longer than `i32::MAX` (#932)
+- Updated the `must_use` message of non-lazy `KMergeBy` and `TupleCombinations` (#939)
+
+### Notable Internal Changes
+- Tested iterator laziness (#792)
+- Created `CONTRIBUTING.md` (#767)
+
+## 0.12.1
+
+### Added
+- Documented iteration order guarantee for `Itertools::[tuple_]combinations` (#822)
+- Documented possible panic in `iterate` (#842)
+- Implemented `Clone` and `Debug` for `Diff` (#845)
+- Implemented `Debug` for `WithPosition` (#859)
+- Implemented `Eq` for `MinMaxResult` (#838)
+- Implemented `From<EitherOrBoth<A, B>>` for `Option<Either<A, B>>` (#843)
+- Implemented `PeekingNext` for `RepeatN` (#855)
+
+### Changed
+- Made `CoalesceBy` lazy (#801)
+- Optimized `Filter[Map]Ok::next`, `Itertools::partition`, `Unique[By]::next[_back]` (#818)
+- Optimized `Itertools::find_position` (#837)
+- Optimized `Positions::next[_back]` (#816)
+- Optimized `ZipLongest::fold` (#854)
+- Relaxed `Debug` bounds for `GroupingMapBy` (#860)
+- Specialized `ExactlyOneError::fold` (#826)
+- Specialized `Interleave[Shortest]::fold` (#849)
+- Specialized `MultiPeek::fold` (#820)
+- Specialized `PadUsing::[r]fold` (#825)
+- Specialized `PeekNth::fold` (#824)
+- Specialized `Positions::[r]fold` (#813)
+- Specialized `PutBackN::fold` (#823)
+- Specialized `RepeatN::[r]fold` (#821)
+- Specialized `TakeWhileInclusive::fold` (#851)
+- Specialized `ZipLongest::rfold` (#848)
+
+### Notable Internal Changes
+- Added test coverage in CI (#847, #856)
+- Added semver check in CI (#784)
+- Enforced `clippy` in CI (#740)
+- Enforced `rustdoc` in CI (#840)
+- Improved specialization tests (#807)
+- More specialization benchmarks (#806)
+
+## 0.12.0
+
+### Breaking
+- Made `take_while_inclusive` consume iterator by value (#709)
+- Added `Clone` bound to `Unique` (#777)
+
+### Added
+- Added `Itertools::try_len` (#723)
+- Added free function `sort_unstable` (#796)
+- Added `GroupMap::fold_with` (#778, #785)
+- Added `PeekNth::{peek_mut, peek_nth_mut}` (#716)
+- Added `PeekNth::{next_if, next_if_eq}` (#734)
+- Added conversion into `(Option<A>,Option<B>)` to `EitherOrBoth` (#713)
+- Added conversion from `Either<A, B>` to `EitherOrBoth<A, B>` (#715)
+- Implemented `ExactSizeIterator` for `Tuples` (#761)
+- Implemented `ExactSizeIterator` for `(Circular)TupleWindows` (#752)
+- Made `EitherOrBoth<T>` a shorthand for `EitherOrBoth<T, T>` (#719)
+
+### Changed
+- Added missing `#[must_use]` annotations on iterator adaptors (#794)
+- Made `Combinations` lazy (#795)
+- Made `Intersperse(With)` lazy (#797)
+- Made `Permutations` lazy (#793)
+- Made `Product` lazy (#800)
+- Made `TupleWindows` lazy (#602)
+- Specialized `Combinations::{count, size_hint}` (#729)
+- Specialized `CombinationsWithReplacement::{count, size_hint}` (#737)
+- Specialized `Powerset::fold` (#765)
+- Specialized `Powerset::count` (#735)
+- Specialized `TupleCombinations::{count, size_hint}` (#763)
+- Specialized `TupleCombinations::fold` (#775)
+- Specialized `WhileSome::fold` (#780)
+- Specialized `WithPosition::fold` (#772)
+- Specialized `ZipLongest::fold` (#774)
+- Changed `{min, max}_set*` operations require `alloc` feature, instead of `std` (#760)
+- Improved documentation of `tree_fold1` (#787)
+- Improved documentation of `permutations` (#724)
+- Fixed typo in documentation of `multiunzip` (#770)
+
+### Notable Internal Changes
+- Improved specialization tests (#799, #786, #782)
+- Simplified implementation of `Permutations` (#739, #748, #790)
+- Combined `Merge`/`MergeBy`/`MergeJoinBy` implementations (#736)
+- Simplified `Permutations::size_hint` (#739)
+- Fix wrapping arithmetic in benchmarks (#770)
+- Enforced `rustfmt` in CI (#751)
+- Disallowed compile warnings in CI (#720)
+- Used `cargo hack` to check MSRV (#754)
+
 ## 0.11.0
 
 ### Breaking
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/CONTRIBUTING.md b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/CONTRIBUTING.md
new file mode 100644
index 0000000..1dbf6f5
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/CONTRIBUTING.md
@@ -0,0 +1,189 @@
+# Contributing to itertools
+
+We use stable Rust only.
+Please check the minimum version of Rust we use in `Cargo.toml`.
+
+_If you are proposing a major change to CI or a new iterator adaptor for this crate,
+then **please first file an issue** describing your proposal._
+[Usual concerns about new methods](https://github.com/rust-itertools/itertools/issues/413#issuecomment-657670781).
+
+To pass CI tests successfully, your code must be free of "compiler warnings" and "clippy warnings" and be "rustfmt" formatted.
+
+Note that small PRs are easier to review and therefore are more easily merged.
+
+## Write a new method/adaptor for `Itertools` trait
+In general, the code logic should be tested with [quickcheck](https://crates.io/crates/quickcheck) tests in `tests/quick.rs`
+which allow us to test properties about the code with randomly generated inputs.
+
+### Behind `use_std`/`use_alloc` feature?
+If it needs the "std" (such as using hashes) then it should be behind the `use_std` feature,
+or if it requires heap allocation (such as using vectors) then it should be behind the `use_alloc` feature.
+Otherwise it should be able to run in `no_std` context.
+
+This mostly applies to your new module, each import from it, and to your new `Itertools` method.
+
+### Pick the right receiver
+`self`, `&mut self` or `&self`? From [#710](https://github.com/rust-itertools/itertools/pull/710):
+
+- Take by value when:
+    - It transfers ownership to another iterator type, such as `filter`, `map`...
+    - It consumes the iterator completely, such as `count`, `last`, `max`...
+- Mutably borrow when it consumes only part of the iterator, such as `find`, `all`, `try_collect`...
+- Immutably borrow when there is no change, such as `size_hint`.
+
+### Laziness
+Iterators are [lazy](https://doc.rust-lang.org/std/iter/index.html#laziness):
+
+- structs of iterator adaptors should have `#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]` ;
+- structs of iterators should have `#[must_use = "iterators are lazy and do nothing unless consumed"]`.
+
+Those behaviors are **tested** in `tests/laziness.rs`.
+
+## Specialize `Iterator` methods
+It might be more performant to specialize some methods.
+However, each specialization should be thoroughly tested.
+
+Correctly specializing methods can be difficult, and _we do not require that you do it on your initial PR_.
+
+Most of the time, we want specializations of:
+
+- [`size_hint`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.size_hint):
+  It mostly allows allocation optimizations.
+  When always exact, it also enables to implement `ExactSizeIterator`.
+  See our private module `src/size_hint.rs` for helpers.
+- [`fold`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold)
+  might make iteration faster than calling `next` repeatedly.
+- [`count`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.count),
+  [`last`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.last),
+  [`nth`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.nth)
+  as we might be able to avoid iterating on every item with `next`.
+
+Additionally,
+
+- `for_each`, `reduce`, `max/min[_by[_key]]` and `partition` all rely on `fold` so you should specialize it instead.
+- `all`, `any`, `find`, `find_map`, `cmp`, `partial_cmp`, `eq`, `ne`, `lt`, `le`, `gt`, `ge` and `position` all rely (by default) on `try_fold`
+  which we can not specialize on stable rust, so you might want to wait it stabilizes
+  or specialize each of them.
+- `DoubleEndedIterator::{nth_back, rfold, rfind}`: similar reasoning.
+
+An adaptor might use the inner iterator specializations for its own specializations.
+
+They are **tested** in `tests/specializations.rs` and **benchmarked** in `benches/specializations.rs`
+(build those benchmarks is slow so you might want to temporarily remove the ones you do not want to measure).
+
+## Additional implementations
+### The [`Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html) implementation
+All our iterators should implement `Debug`.
+
+When one of the field is not debuggable (such as _functions_), you must not derive `Debug`.
+Instead, manually implement it and _ignore this field_ in our helper macro `debug_fmt_fields`.
+
+<details>
+<summary>4 examples (click to expand)</summary>
+
+```rust
+use std::fmt;
+
+/* ===== Simple derive. ===== */
+#[derive(Debug)]
+struct Name1<I> {
+    iter: I,
+}
+
+/* ===== With an unclonable field. ===== */
+struct Name2<I, F> {
+    iter: I,
+    func: F,
+}
+
+// No `F: Debug` bound and the field `func` is ignored.
+impl<I: fmt::Debug, F> fmt::Debug for Name2<I, F> {
+    // it defines the `fmt` function from a struct name and the fields you want to debug.
+    debug_fmt_fields!(Name2, iter);
+}
+
+/* ===== With an unclonable field, but another bound to add. ===== */
+struct Name3<I: Iterator, F> {
+    iter: I,
+    item: Option<I::Item>,
+    func: F,
+}
+
+// Same about `F` and `func`, similar about `I` but we must add the `I::Item: Debug` bound.
+impl<I: Iterator + fmt::Debug, F> fmt::Debug for Name3<I, F>
+where
+    I::Item: fmt::Debug,
+{
+    debug_fmt_fields!(Name3, iter, item);
+}
+
+/* ===== With an unclonable field for which we can provide some information. ===== */
+struct Name4<I, F> {
+    iter: I,
+    func: Option<F>,
+}
+
+// If ignore a field is not good enough, implement Debug fully manually.
+impl<I: fmt::Debug, F> fmt::Debug for Name4<I, F> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let func = if self.func.is_some() { "Some(_)" } else { "None" };
+        f.debug_struct("Name4")
+            .field("iter", &self.iter)
+            .field("func", &func)
+            .finish()
+    }
+}
+```
+</details>
+
+### When/How to implement [`Clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html)
+All our iterators should implement `Clone` when possible.
+
+Note that a mutable reference is never clonable so `struct Name<'a, I: 'a> { iter: &'a mut I }` can not implement `Clone`.
+
+Derive `Clone` on a generic struct adds the bound `Clone` on each generic parameter.
+It might be an issue in which case you should manually implement it with our helper macro `clone_fields` (it defines the `clone` function calling `clone` on each field) and be careful about the bounds.
+
+### When to implement [`std::iter::FusedIterator`](https://doc.rust-lang.org/std/iter/trait.FusedIterator.html)
+This trait should be implemented _by all iterators that always return `None` after returning `None` once_, because it allows to optimize `Iterator::fuse()`.
+
+The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, eventually refined to ensure it behaves in a fused way.
+
+### When to implement [`ExactSizeIterator`](https://doc.rust-lang.org/std/iter/trait.ExactSizeIterator.html)
+_When we are always able to return an exact non-overflowing length._
+
+Therefore, we do not implement it on adaptors that makes the iterator longer as the resulting length could overflow.
+
+One should not override `ExactSizeIterator::len` method but rely on an exact `Iterator::size_hint` implementation, meaning it returns `(length, Some(length))` (unless you could make `len` more performant than the default).
+
+The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, probably refined to ensure the size hint is exact.
+
+### When to implement [`DoubleEndedIterator`](https://doc.rust-lang.org/std/iter/trait.DoubleEndedIterator.html)
+When the iterator structure allows to handle _iterating on both fronts simultaneously_.
+The iteration might stop in the middle when both fronts meet.
+
+The conditions on which it should be implemented are usually the ones from the `Iterator` implementation, probably refined to ensure we can iterate on both fronts simultaneously.
+
+### When to implement [`itertools::PeekingNext`](https://docs.rs/itertools/latest/itertools/trait.PeekingNext.html)
+TODO
+
+This is currently **tested** in `tests/test_std.rs`.
+
+## About lending iterators
+TODO
+
+
+## Other notes
+No guideline about using `#[inline]` yet.
+
+### `.fold` / `.for_each` / `.try_fold` / `.try_for_each`
+In the Rust standard library, it's quite common for `fold` to be implemented in terms of `try_fold`. But it's not something we do yet because we can not specialize `try_fold` methods yet (it uses the unstable `Try`).
+
+From [#781](https://github.com/rust-itertools/itertools/pull/781), the general rule to follow is something like this:
+
+- If you need to completely consume an iterator:
+  - Use `fold` if you need an _owned_ access to an accumulator.
+  - Use `for_each` otherwise.
+- If you need to partly consume an iterator, the same applies with `try_` versions:
+  - Use `try_fold` if you need an _owned_ access to an accumulator.
+  - Use `try_for_each` otherwise.
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.lock b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.lock
similarity index 62%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.lock
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.lock
index 76936c9e..fbf369b 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.lock
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.lock
@@ -3,6 +3,15 @@
 version = 3
 
 [[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
 name = "anes"
 version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -21,9 +30,9 @@
 
 [[package]]
 name = "autocfg"
-version = "1.1.0"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
 
 [[package]]
 name = "bitflags"
@@ -33,9 +42,9 @@
 
 [[package]]
 name = "bumpalo"
-version = "3.11.0"
+version = "3.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
 
 [[package]]
 name = "cast"
@@ -51,9 +60,9 @@
 
 [[package]]
 name = "ciborium"
-version = "0.2.0"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f"
+checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
 dependencies = [
  "ciborium-io",
  "ciborium-ll",
@@ -62,15 +71,15 @@
 
 [[package]]
 name = "ciborium-io"
-version = "0.2.0"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369"
+checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
 
 [[package]]
 name = "ciborium-ll"
-version = "0.2.0"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b"
+checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
 dependencies = [
  "ciborium-io",
  "half",
@@ -78,9 +87,9 @@
 
 [[package]]
 name = "clap"
-version = "3.2.22"
+version = "3.2.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750"
+checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
 dependencies = [
  "bitflags",
  "clap_lex",
@@ -109,7 +118,7 @@
  "ciborium",
  "clap",
  "criterion-plot",
- "itertools 0.10.4",
+ "itertools 0.10.5",
  "lazy_static",
  "num-traits",
  "oorandom",
@@ -130,59 +139,45 @@
 checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
 dependencies = [
  "cast",
- "itertools 0.10.4",
-]
-
-[[package]]
-name = "crossbeam-channel"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
-dependencies = [
- "cfg-if",
- "crossbeam-utils",
+ "itertools 0.10.5",
 ]
 
 [[package]]
 name = "crossbeam-deque"
-version = "0.8.2"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
 dependencies = [
- "cfg-if",
  "crossbeam-epoch",
  "crossbeam-utils",
 ]
 
 [[package]]
 name = "crossbeam-epoch"
-version = "0.9.10"
+version = "0.9.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
 dependencies = [
- "autocfg",
- "cfg-if",
  "crossbeam-utils",
- "memoffset",
- "once_cell",
- "scopeguard",
 ]
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.11"
+version = "0.8.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
-dependencies = [
- "cfg-if",
- "once_cell",
-]
+checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
+
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
 
 [[package]]
 name = "either"
-version = "1.8.0"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
 
 [[package]]
 name = "getrandom"
@@ -197,9 +192,13 @@
 
 [[package]]
 name = "half"
-version = "1.8.2"
+version = "2.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
+checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+]
 
 [[package]]
 name = "hashbrown"
@@ -218,9 +217,9 @@
 
 [[package]]
 name = "indexmap"
-version = "1.9.1"
+version = "1.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
 dependencies = [
  "autocfg",
  "hashbrown",
@@ -228,16 +227,16 @@
 
 [[package]]
 name = "itertools"
-version = "0.10.4"
+version = "0.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8bf247779e67a9082a4790b45e71ac7cfd1321331a5c856a74a9faebdab78d0"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
 dependencies = [
  "either",
 ]
 
 [[package]]
 name = "itertools"
-version = "0.11.0"
+version = "0.14.0"
 dependencies = [
  "criterion",
  "either",
@@ -249,15 +248,15 @@
 
 [[package]]
 name = "itoa"
-version = "1.0.3"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
 
 [[package]]
 name = "js-sys"
-version = "0.3.60"
+version = "0.3.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -270,52 +269,36 @@
 
 [[package]]
 name = "libc"
-version = "0.2.133"
+version = "0.2.154"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
+checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
 
 [[package]]
 name = "log"
-version = "0.4.17"
+version = "0.4.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
-dependencies = [
- "cfg-if",
-]
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
 
 [[package]]
-name = "memoffset"
-version = "0.6.5"
+name = "memchr"
+version = "2.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
-dependencies = [
- "autocfg",
-]
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
 
 [[package]]
 name = "num-traits"
-version = "0.2.15"
+version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
 dependencies = [
  "autocfg",
 ]
 
 [[package]]
-name = "num_cpus"
-version = "1.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
-dependencies = [
- "hermit-abi",
- "libc",
-]
-
-[[package]]
 name = "once_cell"
-version = "1.14.0"
+version = "1.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
 [[package]]
 name = "oorandom"
@@ -325,15 +308,15 @@
 
 [[package]]
 name = "os_str_bytes"
-version = "6.3.0"
+version = "6.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
+checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
 
 [[package]]
 name = "paste"
-version = "1.0.9"
+version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
 
 [[package]]
 name = "permutohedron"
@@ -343,9 +326,9 @@
 
 [[package]]
 name = "plotters"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
+checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
 dependencies = [
  "num-traits",
  "plotters-backend",
@@ -356,30 +339,30 @@
 
 [[package]]
 name = "plotters-backend"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
+checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
 
 [[package]]
 name = "plotters-svg"
-version = "0.3.3"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
+checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
 dependencies = [
  "plotters-backend",
 ]
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.16"
+version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.43"
+version = "1.0.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
+checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
 dependencies = [
  "unicode-ident",
 ]
@@ -396,9 +379,9 @@
 
 [[package]]
 name = "quote"
-version = "1.0.21"
+version = "1.0.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
 dependencies = [
  "proc-macro2",
 ]
@@ -446,48 +429,58 @@
 
 [[package]]
 name = "rayon"
-version = "1.5.3"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
 dependencies = [
- "autocfg",
- "crossbeam-deque",
  "either",
  "rayon-core",
 ]
 
 [[package]]
 name = "rayon-core"
-version = "1.9.3"
+version = "1.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
 dependencies = [
- "crossbeam-channel",
  "crossbeam-deque",
  "crossbeam-utils",
- "num_cpus",
 ]
 
 [[package]]
 name = "regex"
-version = "1.6.0"
+version = "1.10.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
 dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+dependencies = [
+ "aho-corasick",
+ "memchr",
  "regex-syntax",
 ]
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.27"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
 
 [[package]]
 name = "ryu"
-version = "1.0.11"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
 
 [[package]]
 name = "same-file"
@@ -499,25 +492,19 @@
 ]
 
 [[package]]
-name = "scopeguard"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
-
-[[package]]
 name = "serde"
-version = "1.0.144"
+version = "1.0.202"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
+checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.144"
+version = "1.0.202"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
+checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -526,9 +513,9 @@
 
 [[package]]
 name = "serde_json"
-version = "1.0.85"
+version = "1.0.117"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
+checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
 dependencies = [
  "itoa",
  "ryu",
@@ -537,9 +524,9 @@
 
 [[package]]
 name = "syn"
-version = "1.0.100"
+version = "2.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e"
+checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -548,9 +535,9 @@
 
 [[package]]
 name = "textwrap"
-version = "0.15.1"
+version = "0.16.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
+checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
 
 [[package]]
 name = "tinytemplate"
@@ -564,18 +551,17 @@
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.4"
+version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
 [[package]]
 name = "walkdir"
-version = "2.3.2"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
 dependencies = [
  "same-file",
- "winapi",
  "winapi-util",
 ]
 
@@ -587,9 +573,9 @@
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.83"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
 dependencies = [
  "cfg-if",
  "wasm-bindgen-macro",
@@ -597,9 +583,9 @@
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.83"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
 dependencies = [
  "bumpalo",
  "log",
@@ -612,9 +598,9 @@
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.83"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -622,9 +608,9 @@
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.83"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -635,15 +621,15 @@
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.83"
+version = "0.2.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
 
 [[package]]
 name = "web-sys"
-version = "0.3.60"
+version = "0.3.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
+checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -667,11 +653,11 @@
 
 [[package]]
 name = "winapi-util"
-version = "0.1.5"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
 dependencies = [
- "winapi",
+ "windows-sys",
 ]
 
 [[package]]
@@ -679,3 +665,76 @@
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.toml
new file mode 100644
index 0000000..96f959771
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.toml
@@ -0,0 +1,180 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+rust-version = "1.63.0"
+name = "itertools"
+version = "0.14.0"
+authors = ["bluss"]
+build = false
+autolib = false
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
+description = "Extra iterator adaptors, iterator methods, free functions, and macros."
+documentation = "https://docs.rs/itertools/"
+readme = "README.md"
+keywords = [
+    "iterator",
+    "data-structure",
+    "zip",
+    "product",
+]
+categories = [
+    "algorithms",
+    "rust-patterns",
+    "no-std",
+    "no-std::no-alloc",
+]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/rust-itertools/itertools"
+
+[features]
+default = ["use_std"]
+use_alloc = []
+use_std = [
+    "use_alloc",
+    "either/use_std",
+]
+
+[lib]
+name = "itertools"
+path = "src/lib.rs"
+test = false
+bench = false
+
+[[example]]
+name = "iris"
+path = "examples/iris.rs"
+
+[[test]]
+name = "adaptors_no_collect"
+path = "tests/adaptors_no_collect.rs"
+
+[[test]]
+name = "flatten_ok"
+path = "tests/flatten_ok.rs"
+
+[[test]]
+name = "laziness"
+path = "tests/laziness.rs"
+
+[[test]]
+name = "macros_hygiene"
+path = "tests/macros_hygiene.rs"
+
+[[test]]
+name = "merge_join"
+path = "tests/merge_join.rs"
+
+[[test]]
+name = "peeking_take_while"
+path = "tests/peeking_take_while.rs"
+
+[[test]]
+name = "quick"
+path = "tests/quick.rs"
+
+[[test]]
+name = "specializations"
+path = "tests/specializations.rs"
+
+[[test]]
+name = "test_core"
+path = "tests/test_core.rs"
+
+[[test]]
+name = "test_std"
+path = "tests/test_std.rs"
+
+[[test]]
+name = "tuples"
+path = "tests/tuples.rs"
+
+[[test]]
+name = "zip"
+path = "tests/zip.rs"
+
+[[bench]]
+name = "bench1"
+path = "benches/bench1.rs"
+harness = false
+
+[[bench]]
+name = "combinations"
+path = "benches/combinations.rs"
+harness = false
+
+[[bench]]
+name = "combinations_with_replacement"
+path = "benches/combinations_with_replacement.rs"
+harness = false
+
+[[bench]]
+name = "fold_specialization"
+path = "benches/fold_specialization.rs"
+harness = false
+
+[[bench]]
+name = "k_smallest"
+path = "benches/k_smallest.rs"
+harness = false
+
+[[bench]]
+name = "powerset"
+path = "benches/powerset.rs"
+harness = false
+
+[[bench]]
+name = "specializations"
+path = "benches/specializations.rs"
+harness = false
+
+[[bench]]
+name = "tree_reduce"
+path = "benches/tree_reduce.rs"
+harness = false
+
+[[bench]]
+name = "tuple_combinations"
+path = "benches/tuple_combinations.rs"
+harness = false
+
+[[bench]]
+name = "tuples"
+path = "benches/tuples.rs"
+harness = false
+
+[dependencies.either]
+version = "1.0"
+default-features = false
+
+[dev-dependencies.criterion]
+version = "0.4.0"
+features = ["html_reports"]
+
+[dev-dependencies.paste]
+version = "1.0.0"
+
+[dev-dependencies.permutohedron]
+version = "0.2"
+
+[dev-dependencies.quickcheck]
+version = "0.9"
+default-features = false
+
+[dev-dependencies.rand]
+version = "0.7"
+
+[profile.bench]
+debug = 2
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.toml.orig b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.toml.orig
similarity index 67%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.toml.orig
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.toml.orig
index a6dfefb..1d8a68f 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/Cargo.toml.orig
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "itertools"
-version = "0.11.0"
+version = "0.14.0"
 
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/rust-itertools/itertools"
@@ -10,13 +10,13 @@
 
 description = "Extra iterator adaptors, iterator methods, free functions, and macros."
 
-keywords = ["iterator", "data-structure", "zip", "product", "group-by"]
-categories = ["algorithms", "rust-patterns"]
-exclude = ["/bors.toml"]
+keywords = ["iterator", "data-structure", "zip", "product"]
+categories = ["algorithms", "rust-patterns", "no-std", "no-std::no-alloc"]
 
 edition = "2018"
 
-rust-version = "1.36.0"
+# When bumping, please resolve all `#[allow(clippy::*)]` that are newly resolvable.
+rust-version = "1.63.0"
 
 [lib]
 bench = false
@@ -27,10 +27,10 @@
 
 [dev-dependencies]
 rand = "0.7"
-criterion = "0.4.0"
+criterion = { version = "0.4.0", features = ["html_reports"] }
 paste = "1.0.0"  # Used in test_std to instantiate generic tests
 permutohedron = "0.2"
-quickcheck = { version = "0.9", default_features = false }
+quickcheck = { version = "0.9", default-features = false }
 
 [features]
 default = ["use_std"]
@@ -57,7 +57,7 @@
 harness = false
 
 [[bench]]
-name = "tree_fold1"
+name = "tree_reduce"
 harness = false
 
 [[bench]]
@@ -71,3 +71,11 @@
 [[bench]]
 name = "powerset"
 harness = false
+
+[[bench]]
+name = "specializations"
+harness = false
+
+[[bench]]
+name = "k_smallest"
+harness = false
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/LICENSE-APACHE b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/LICENSE-APACHE
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/LICENSE-APACHE
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/LICENSE-APACHE
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/LICENSE-MIT b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/LICENSE-MIT
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/LICENSE-MIT
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/LICENSE-MIT
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/README.md b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/README.md
new file mode 100644
index 0000000..46acc3f
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/README.md
@@ -0,0 +1,33 @@
+# Itertools
+
+Extra iterator adaptors, functions and macros.
+
+Please read the [API documentation here](https://docs.rs/itertools/).
+
+How to use with Cargo:
+
+```toml
+[dependencies]
+itertools = "0.14.0"
+```
+
+How to use in your crate:
+
+```rust
+use itertools::Itertools;
+```
+
+## How to contribute
+If you're not sure what to work on, try checking the [help wanted](https://github.com/rust-itertools/itertools/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label.
+
+See our [CONTRIBUTING.md](https://github.com/rust-itertools/itertools/blob/master/CONTRIBUTING.md) for a detailed guide.
+
+## License
+
+Dual-licensed to be compatible with the Rust project.
+
+Licensed under the Apache License, Version 2.0
+https://www.apache.org/licenses/LICENSE-2.0 or the MIT license
+https://opensource.org/licenses/MIT, at your
+option. This file may not be copied, modified, or distributed
+except according to those terms.
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/bench1.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/bench1.rs
similarity index 72%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/bench1.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/bench1.rs
index 71278d1..a55fa7c9 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/bench1.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/bench1.rs
@@ -1,22 +1,20 @@
-use criterion::{black_box, criterion_group, criterion_main, Criterion};
-use itertools::Itertools;
+use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion};
 use itertools::free::cloned;
 use itertools::iproduct;
+use itertools::Itertools;
 
-use std::iter::repeat;
 use std::cmp;
+use std::iter::repeat;
 use std::ops::{Add, Range};
 
-mod extra;
-
-use crate::extra::ZipSlices;
-
 fn slice_iter(c: &mut Criterion) {
     let xs: Vec<_> = repeat(1i32).take(20).collect();
 
     c.bench_function("slice iter", move |b| {
-        b.iter(|| for elt in xs.iter() {
-            black_box(elt);
+        b.iter(|| {
+            for elt in xs.iter() {
+                black_box(elt);
+            }
         })
     });
 }
@@ -25,8 +23,10 @@
     let xs: Vec<_> = repeat(1i32).take(20).collect();
 
     c.bench_function("slice iter rev", move |b| {
-        b.iter(|| for elt in xs.iter().rev() {
-            black_box(elt);
+        b.iter(|| {
+            for elt in xs.iter().rev() {
+                black_box(elt);
+            }
         })
     });
 }
@@ -116,72 +116,6 @@
     });
 }
 
-fn zipslices(c: &mut Criterion) {
-    let xs = vec![0; 1024];
-    let ys = vec![0; 768];
-    let xs = black_box(xs);
-    let ys = black_box(ys);
-
-    c.bench_function("zipslices", move |b| {
-        b.iter(|| {
-            for (&x, &y) in ZipSlices::new(&xs, &ys) {
-                black_box(x);
-                black_box(y);
-            }
-        })
-    });
-}
-
-fn zipslices_mut(c: &mut Criterion) {
-    let xs = vec![0; 1024];
-    let ys = vec![0; 768];
-    let xs = black_box(xs);
-    let mut ys = black_box(ys);
-
-    c.bench_function("zipslices mut", move |b| {
-        b.iter(|| {
-            for (&x, &mut y) in ZipSlices::from_slices(&xs[..], &mut ys[..]) {
-                black_box(x);
-                black_box(y);
-            }
-        })
-    });
-}
-
-fn zipdot_i32_zipslices(c: &mut Criterion) {
-    let xs = vec![2; 1024];
-    let ys = vec![2; 768];
-    let xs = black_box(xs);
-    let ys = black_box(ys);
-
-    c.bench_function("zipdot i32 zipslices", move |b| {
-        b.iter(|| {
-            let mut s = 0i32;
-            for (&x, &y) in ZipSlices::new(&xs, &ys) {
-                s += x * y;
-            }
-            s
-        })
-    });
-}
-
-fn zipdot_f32_zipslices(c: &mut Criterion) {
-    let xs = vec![2f32; 1024];
-    let ys = vec![2f32; 768];
-    let xs = black_box(xs);
-    let ys = black_box(ys);
-
-    c.bench_function("zipdot f32 zipslices", move |b| {
-        b.iter(|| {
-            let mut s = 0.;
-            for (&x, &y) in ZipSlices::new(&xs, &ys) {
-                s += x * y;
-            }
-            s
-        })
-    });
-}
-
 fn zip_checked_counted_loop(c: &mut Criterion) {
     let xs = vec![0; 1024];
     let ys = vec![0; 768];
@@ -307,10 +241,10 @@
             let len = cmp::min(xs.len(), ys.len());
             for i in 0..len {
                 unsafe {
-                let x = *xs.get_unchecked(i);
-                let y = *ys.get_unchecked(i);
-                black_box(x);
-                black_box(y);
+                    let x = *xs.get_unchecked(i);
+                    let y = *ys.get_unchecked(i);
+                    black_box(x);
+                    black_box(y);
                 }
             }
         })
@@ -329,9 +263,9 @@
             let mut s = 0i32;
             for i in 0..len {
                 unsafe {
-                let x = *xs.get_unchecked(i);
-                let y = *ys.get_unchecked(i);
-                s += x * y;
+                    let x = *xs.get_unchecked(i);
+                    let y = *ys.get_unchecked(i);
+                    s += x * y;
                 }
             }
             s
@@ -351,9 +285,9 @@
             let mut s = 0f32;
             for i in 0..len {
                 unsafe {
-                let x = *xs.get_unchecked(i);
-                let y = *ys.get_unchecked(i);
-                s += x * y;
+                    let x = *xs.get_unchecked(i);
+                    let y = *ys.get_unchecked(i);
+                    s += x * y;
                 }
             }
             s
@@ -374,19 +308,19 @@
             let len = cmp::min(xs.len(), cmp::min(ys.len(), zs.len()));
             for i in 0..len {
                 unsafe {
-                let x = *xs.get_unchecked(i);
-                let y = *ys.get_unchecked(i);
-                let z = *zs.get_unchecked(i);
-                black_box(x);
-                black_box(y);
-                black_box(z);
+                    let x = *xs.get_unchecked(i);
+                    let y = *ys.get_unchecked(i);
+                    let z = *zs.get_unchecked(i);
+                    black_box(x);
+                    black_box(y);
+                    black_box(z);
                 }
             }
         })
     });
 }
 
-fn group_by_lazy_1(c: &mut Criterion) {
+fn chunk_by_lazy_1(c: &mut Criterion) {
     let mut data = vec![0; 1024];
     for (index, elt) in data.iter_mut().enumerate() {
         *elt = index / 10;
@@ -394,10 +328,10 @@
 
     let data = black_box(data);
 
-    c.bench_function("group by lazy 1", move |b| {
+    c.bench_function("chunk by lazy 1", move |b| {
         b.iter(|| {
-            for (_key, group) in &data.iter().group_by(|elt| **elt) {
-                for elt in group {
+            for (_key, chunk) in &data.iter().chunk_by(|elt| **elt) {
+                for elt in chunk {
                     black_box(elt);
                 }
             }
@@ -405,7 +339,7 @@
     });
 }
 
-fn group_by_lazy_2(c: &mut Criterion) {
+fn chunk_by_lazy_2(c: &mut Criterion) {
     let mut data = vec![0; 1024];
     for (index, elt) in data.iter_mut().enumerate() {
         *elt = index / 2;
@@ -413,10 +347,10 @@
 
     let data = black_box(data);
 
-    c.bench_function("group by lazy 2", move |b| {
+    c.bench_function("chunk by lazy 2", move |b| {
         b.iter(|| {
-            for (_key, group) in &data.iter().group_by(|elt| **elt) {
-                for elt in group {
+            for (_key, chunk) in &data.iter().chunk_by(|elt| **elt) {
+                for elt in chunk {
                     black_box(elt);
                 }
             }
@@ -432,8 +366,8 @@
 
     c.bench_function("slice chunks", move |b| {
         b.iter(|| {
-            for group in data.chunks(sz) {
-                for elt in group {
+            for chunk in data.chunks(sz) {
+                for elt in chunk {
                     black_box(elt);
                 }
             }
@@ -449,8 +383,8 @@
 
     c.bench_function("chunks lazy 1", move |b| {
         b.iter(|| {
-            for group in &data.iter().chunks(sz) {
-                for elt in group {
+            for chunk in &data.iter().chunks(sz) {
+                for elt in chunk {
                     black_box(elt);
                 }
             }
@@ -464,17 +398,15 @@
     let alpha = black_box(&data[1..]);
     let beta = black_box(&data[..l - 1]);
 
-    c.bench_function("equal", move |b| {
-        b.iter(|| {
-            itertools::equal(alpha, beta)
-        })
-    });
+    c.bench_function("equal", move |b| b.iter(|| itertools::equal(alpha, beta)));
 }
 
 fn merge_default(c: &mut Criterion) {
     let mut data1 = vec![0; 1024];
     let mut data2 = vec![0; 800];
     let mut x = 0;
+
+    #[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)]
     for (_, elt) in data1.iter_mut().enumerate() {
         *elt = x;
         x += 1;
@@ -492,17 +424,15 @@
     let data1 = black_box(data1);
     let data2 = black_box(data2);
 
-    c.bench_function("merge default", move |b| {
-        b.iter(|| {
-            data1.iter().merge(&data2).count()
-        })
-    });
+    c.bench_function("merge default", move |b| b.iter(|| data1.iter().merge(&data2).count()));
 }
 
 fn merge_by_cmp(c: &mut Criterion) {
     let mut data1 = vec![0; 1024];
     let mut data2 = vec![0; 800];
     let mut x = 0;
+
+    #[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)]
     for (_, elt) in data1.iter_mut().enumerate() {
         *elt = x;
         x += 1;
@@ -521,9 +451,7 @@
     let data2 = black_box(data2);
 
     c.bench_function("merge by cmp", move |b| {
-        b.iter(|| {
-            data1.iter().merge_by(&data2, PartialOrd::le).count()
-        })
+        b.iter(|| data1.iter().merge_by(&data2, PartialOrd::le).count())
     });
 }
 
@@ -531,6 +459,8 @@
     let mut data1 = vec![0; 1024];
     let mut data2 = vec![0; 800];
     let mut x = 0;
+
+    #[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)]
     for (_, elt) in data1.iter_mut().enumerate() {
         *elt = x;
         x += 1;
@@ -549,9 +479,7 @@
     let data2 = black_box(data2);
 
     c.bench_function("merge by lt", move |b| {
-        b.iter(|| {
-            data1.iter().merge_by(&data2, |a, b| a <= b).count()
-        })
+        b.iter(|| data1.iter().merge_by(&data2, |a, b| a <= b).count())
     });
 }
 
@@ -559,6 +487,8 @@
     let mut data1 = vec![0; 1024];
     let mut data2 = vec![0; 800];
     let mut x = 0;
+
+    #[allow(clippy::explicit_counter_loop, clippy::unused_enumerate_index)]
     for (_, elt) in data1.iter_mut().enumerate() {
         *elt = x;
         x += 1;
@@ -577,11 +507,7 @@
     let data2 = black_box(data2);
     let its = &[data1.iter(), data2.iter()];
 
-    c.bench_function("kmerge default", move |b| {
-        b.iter(|| {
-            its.iter().cloned().kmerge().count()
-        })
-    });
+    c.bench_function("kmerge default", move |b| b.iter(|| its.iter().cloned().kmerge().count()));
 }
 
 fn kmerge_tenway(c: &mut Criterion) {
@@ -589,7 +515,7 @@
 
     let mut state = 1729u16;
     fn rng(state: &mut u16) -> u16 {
-        let new = state.wrapping_mul(31421) + 6927;
+        let new = state.wrapping_mul(31421).wrapping_add(6927);
         *state = new;
         new
     }
@@ -600,27 +526,25 @@
 
     let mut chunks = Vec::new();
     let mut rest = &mut data[..];
-    while rest.len() > 0 {
+    while !rest.is_empty() {
         let chunk_len = 1 + rng(&mut state) % 512;
         let chunk_len = cmp::min(rest.len(), chunk_len as usize);
-        let (fst, tail) = {rest}.split_at_mut(chunk_len);
+        let (fst, tail) = { rest }.split_at_mut(chunk_len);
         fst.sort();
         chunks.push(fst.iter().cloned());
         rest = tail;
     }
 
-    // println!("Chunk lengths: {}", chunks.iter().format_with(", ", |elt, f| f(&elt.len())));
+    // println!("Chunk lengths: {}", chunks.iter().format_with(", ", |elt, f|
+    // f(&elt.len())));
 
-    c.bench_function("kmerge tenway", move |b| {
-        b.iter(|| {
-            chunks.iter().cloned().kmerge().count()
-        })
-    });
+    c.bench_function("kmerge tenway", move |b| b.iter(|| chunks.iter().cloned().kmerge().count()));
 }
 
 fn fast_integer_sum<I>(iter: I) -> I::Item
-    where I: IntoIterator,
-          I::Item: Default + Add<Output=I::Item>
+where
+    I: IntoIterator,
+    I::Item: Default + Add<Output = I::Item>,
 {
     iter.into_iter().fold(<_>::default(), |x, y| x + y)
 }
@@ -629,9 +553,7 @@
     let v = vec![0; 1024];
 
     c.bench_function("step vec 2", move |b| {
-        b.iter(|| {
-            fast_integer_sum(cloned(v.iter().step_by(2)))
-        })
+        b.iter(|| fast_integer_sum(cloned(v.iter().step_by(2))))
     });
 }
 
@@ -639,29 +561,32 @@
     let v = vec![0; 1024];
 
     c.bench_function("step vec 10", move |b| {
-        b.iter(|| {
-            fast_integer_sum(cloned(v.iter().step_by(10)))
-        })
+        b.iter(|| fast_integer_sum(cloned(v.iter().step_by(10))))
     });
 }
 
 fn step_range_2(c: &mut Criterion) {
     let v = black_box(0..1024);
 
-    c.bench_function("step range 2", move |b| {
-        b.iter(|| {
-            fast_integer_sum(v.clone().step_by(2))
-        })
-    });
+    c.bench_function("step range 2", move |b| b.iter(|| fast_integer_sum(v.clone().step_by(2))));
 }
 
 fn step_range_10(c: &mut Criterion) {
     let v = black_box(0..1024);
 
-    c.bench_function("step range 10", move |b| {
-        b.iter(|| {
-            fast_integer_sum(v.clone().step_by(10))
-        })
+    c.bench_function("step range 10", move |b| b.iter(|| fast_integer_sum(v.clone().step_by(10))));
+}
+
+fn vec_iter_mut_partition(c: &mut Criterion) {
+    let data = std::iter::repeat(-1024i32..1024).take(256).flatten().collect_vec();
+    c.bench_function("vec iter mut partition", move |b| {
+        b.iter_batched(
+            || data.clone(),
+            |mut data| {
+                black_box(itertools::partition(black_box(&mut data), |n| *n >= 0));
+            },
+            BatchSize::LargeInput,
+        )
     });
 }
 
@@ -681,22 +606,6 @@
     });
 }
 
-fn cartesian_product_fold(c: &mut Criterion) {
-    let xs = vec![0; 16];
-
-    c.bench_function("cartesian product fold", move |b| {
-        b.iter(|| {
-            let mut sum = 0;
-            iproduct!(&xs, &xs, &xs).fold((), |(), (&x, &y, &z)| {
-                sum += x;
-                sum += y;
-                sum += z;
-            });
-            sum
-        })
-    });
-}
-
 fn multi_cartesian_product_iterator(c: &mut Criterion) {
     let xs = [vec![0; 16], vec![0; 16], vec![0; 16]];
 
@@ -713,22 +622,6 @@
     });
 }
 
-fn multi_cartesian_product_fold(c: &mut Criterion) {
-    let xs = [vec![0; 16], vec![0; 16], vec![0; 16]];
-
-    c.bench_function("multi cartesian product fold", move |b| {
-        b.iter(|| {
-            let mut sum = 0;
-            xs.iter().multi_cartesian_product().fold((), |(), x| {
-                sum += x[0];
-                sum += x[1];
-                sum += x[2];
-            });
-            sum
-        })
-    });
-}
-
 fn cartesian_product_nested_for(c: &mut Criterion) {
     let xs = vec![0; 16];
 
@@ -753,9 +646,7 @@
     let mut xs = vec![0; 5_000_000];
     xs.extend(vec![1; 5_000_000]);
 
-    c.bench_function("all equal", move |b| {
-        b.iter(|| xs.iter().all_equal())
-    });
+    c.bench_function("all equal", move |b| b.iter(|| xs.iter().all_equal()));
 }
 
 fn all_equal_for(c: &mut Criterion) {
@@ -778,9 +669,7 @@
     let mut xs = vec![0; 5_000_000];
     xs.extend(vec![1; 5_000_000]);
 
-    c.bench_function("all equal default", move |b| {
-        b.iter(|| xs.iter().dedup().nth(1).is_none())
-    });
+    c.bench_function("all equal default", move |b| b.iter(|| xs.iter().dedup().nth(1).is_none()));
 }
 
 const PERM_COUNT: usize = 6;
@@ -797,21 +686,13 @@
     }
 
     c.bench_function("permutations iter", move |b| {
-        b.iter(|| {
-            for _ in NewIterator(0..PERM_COUNT).permutations(PERM_COUNT) {
-
-            }
-        })
+        b.iter(|| for _ in NewIterator(0..PERM_COUNT).permutations(PERM_COUNT) {})
     });
 }
 
 fn permutations_range(c: &mut Criterion) {
     c.bench_function("permutations range", move |b| {
-        b.iter(|| {
-            for _ in (0..PERM_COUNT).permutations(PERM_COUNT) {
-
-            }
-        })
+        b.iter(|| for _ in (0..PERM_COUNT).permutations(PERM_COUNT) {})
     });
 }
 
@@ -819,11 +700,7 @@
     let v = (0..PERM_COUNT).collect_vec();
 
     c.bench_function("permutations slice", move |b| {
-        b.iter(|| {
-            for _ in v.as_slice().iter().permutations(PERM_COUNT) {
-
-            }
-        })
+        b.iter(|| for _ in v.as_slice().iter().permutations(PERM_COUNT) {})
     });
 }
 
@@ -836,10 +713,6 @@
     zipdot_f32_default_zip,
     zip_default_zip3,
     zip_slices_ziptuple,
-    zipslices,
-    zipslices_mut,
-    zipdot_i32_zipslices,
-    zipdot_f32_zipslices,
     zip_checked_counted_loop,
     zipdot_i32_checked_counted_loop,
     zipdot_f32_checked_counted_loop,
@@ -848,8 +721,8 @@
     zipdot_i32_unchecked_counted_loop,
     zipdot_f32_unchecked_counted_loop,
     zip_unchecked_counted_loop3,
-    group_by_lazy_1,
-    group_by_lazy_2,
+    chunk_by_lazy_1,
+    chunk_by_lazy_2,
     slice_chunks,
     chunks_lazy_1,
     equal,
@@ -862,10 +735,9 @@
     step_vec_10,
     step_range_2,
     step_range_10,
+    vec_iter_mut_partition,
     cartesian_product_iterator,
-    cartesian_product_fold,
     multi_cartesian_product_iterator,
-    multi_cartesian_product_fold,
     cartesian_product_nested_for,
     all_equal,
     all_equal_for,
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/combinations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/combinations.rs
similarity index 95%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/combinations.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/combinations.rs
index e7433a4..42a45211 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/combinations.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/combinations.rs
@@ -111,15 +111,7 @@
 }
 
 criterion_group!(
-    benches,
-    comb_for1,
-    comb_for2,
-    comb_for3,
-    comb_for4,
-    comb_c1,
-    comb_c2,
-    comb_c3,
-    comb_c4,
+    benches, comb_for1, comb_for2, comb_for3, comb_for4, comb_c1, comb_c2, comb_c3, comb_c4,
     comb_c14,
 );
 criterion_main!(benches);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/combinations_with_replacement.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/combinations_with_replacement.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/combinations_with_replacement.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/combinations_with_replacement.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/fold_specialization.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/fold_specialization.rs
similarity index 82%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/fold_specialization.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/fold_specialization.rs
index 5de4671..b44f347 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/fold_specialization.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/fold_specialization.rs
@@ -1,10 +1,13 @@
+#![allow(unstable_name_collisions)]
+
 use criterion::{criterion_group, criterion_main, Criterion};
 use itertools::Itertools;
 
 struct Unspecialized<I>(I);
 
 impl<I> Iterator for Unspecialized<I>
-where I: Iterator
+where
+    I: Iterator,
 {
     type Item = I::Item;
 
@@ -25,8 +28,7 @@
     pub mod intersperse {
         use super::*;
 
-        pub fn external(c: &mut Criterion)
-        {
+        pub fn external(c: &mut Criterion) {
             let arr = [1; 1024];
 
             c.bench_function("external", move |b| {
@@ -40,23 +42,23 @@
             });
         }
 
-        pub fn internal_specialized(c: &mut Criterion)
-        {
+        pub fn internal_specialized(c: &mut Criterion) {
             let arr = [1; 1024];
 
             c.bench_function("internal specialized", move |b| {
                 b.iter(|| {
+                    #[allow(clippy::unnecessary_fold)]
                     arr.iter().intersperse(&0).fold(0, |acc, x| acc + x)
                 })
             });
         }
 
-        pub fn internal_unspecialized(c: &mut Criterion)
-        {
+        pub fn internal_unspecialized(c: &mut Criterion) {
             let arr = [1; 1024];
 
             c.bench_function("internal unspecialized", move |b| {
                 b.iter(|| {
+                    #[allow(clippy::unnecessary_fold)]
                     Unspecialized(arr.iter().intersperse(&0)).fold(0, |acc, x| acc + x)
                 })
             });
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/k_smallest.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/k_smallest.rs
new file mode 100644
index 0000000..509ed7f
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/k_smallest.rs
@@ -0,0 +1,61 @@
+use criterion::{black_box, criterion_group, criterion_main, Bencher, BenchmarkId, Criterion};
+use itertools::Itertools;
+use rand::{rngs::StdRng, seq::SliceRandom, SeedableRng};
+
+fn strict(b: &mut Bencher, (k, vals): &(usize, &Vec<usize>)) {
+    b.iter(|| black_box(vals.iter()).k_smallest(*k))
+}
+
+fn relaxed(b: &mut Bencher, (k, vals): &(usize, &Vec<usize>)) {
+    b.iter(|| black_box(vals.iter()).k_smallest_relaxed(*k))
+}
+
+fn ascending(n: usize) -> Vec<usize> {
+    (0..n).collect()
+}
+
+fn random(n: usize) -> Vec<usize> {
+    let mut vals = (0..n).collect_vec();
+    vals.shuffle(&mut StdRng::seed_from_u64(42));
+    vals
+}
+
+fn descending(n: usize) -> Vec<usize> {
+    (0..n).rev().collect()
+}
+
+fn k_smallest(c: &mut Criterion, order: &str, vals: fn(usize) -> Vec<usize>) {
+    let mut g = c.benchmark_group(format!("k-smallest/{order}"));
+
+    for log_n in 20..23 {
+        let n = 1 << log_n;
+
+        let vals = vals(n);
+
+        for log_k in 7..10 {
+            let k = 1 << log_k;
+
+            let params = format!("{log_n}/{log_k}");
+            let input = (k, &vals);
+            g.bench_with_input(BenchmarkId::new("strict", &params), &input, strict);
+            g.bench_with_input(BenchmarkId::new("relaxed", &params), &input, relaxed);
+        }
+    }
+
+    g.finish()
+}
+
+fn k_smallest_asc(c: &mut Criterion) {
+    k_smallest(c, "asc", ascending);
+}
+
+fn k_smallest_rand(c: &mut Criterion) {
+    k_smallest(c, "rand", random);
+}
+
+fn k_smallest_desc(c: &mut Criterion) {
+    k_smallest(c, "desc", descending);
+}
+
+criterion_group!(benches, k_smallest_asc, k_smallest_rand, k_smallest_desc);
+criterion_main!(benches);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/powerset.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/powerset.rs
new file mode 100644
index 0000000..018333d3
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/powerset.rs
@@ -0,0 +1,97 @@
+use criterion::{black_box, criterion_group, criterion_main, Criterion};
+use itertools::Itertools;
+
+// Keep aggregate generated elements the same, regardless of powerset length.
+const TOTAL_ELEMENTS: usize = 1 << 12;
+const fn calc_iters(n: usize) -> usize {
+    TOTAL_ELEMENTS / (1 << n)
+}
+
+fn powerset_n(c: &mut Criterion, n: usize) {
+    let id = format!("powerset {}", n);
+    c.bench_function(id.as_str(), move |b| {
+        b.iter(|| {
+            for _ in 0..calc_iters(n) {
+                for elt in (0..n).powerset() {
+                    black_box(elt);
+                }
+            }
+        })
+    });
+}
+
+fn powerset_n_fold(c: &mut Criterion, n: usize) {
+    let id = format!("powerset {} fold", n);
+    c.bench_function(id.as_str(), move |b| {
+        b.iter(|| {
+            for _ in 0..calc_iters(n) {
+                (0..n).powerset().fold(0, |s, elt| s + black_box(elt).len());
+            }
+        })
+    });
+}
+
+fn powerset_0(c: &mut Criterion) {
+    powerset_n(c, 0);
+}
+
+fn powerset_1(c: &mut Criterion) {
+    powerset_n(c, 1);
+}
+
+fn powerset_2(c: &mut Criterion) {
+    powerset_n(c, 2);
+}
+
+fn powerset_4(c: &mut Criterion) {
+    powerset_n(c, 4);
+}
+
+fn powerset_8(c: &mut Criterion) {
+    powerset_n(c, 8);
+}
+
+fn powerset_12(c: &mut Criterion) {
+    powerset_n(c, 12);
+}
+
+fn powerset_0_fold(c: &mut Criterion) {
+    powerset_n_fold(c, 0);
+}
+
+fn powerset_1_fold(c: &mut Criterion) {
+    powerset_n_fold(c, 1);
+}
+
+fn powerset_2_fold(c: &mut Criterion) {
+    powerset_n_fold(c, 2);
+}
+
+fn powerset_4_fold(c: &mut Criterion) {
+    powerset_n_fold(c, 4);
+}
+
+fn powerset_8_fold(c: &mut Criterion) {
+    powerset_n_fold(c, 8);
+}
+
+fn powerset_12_fold(c: &mut Criterion) {
+    powerset_n_fold(c, 12);
+}
+
+criterion_group!(
+    benches,
+    powerset_0,
+    powerset_1,
+    powerset_2,
+    powerset_4,
+    powerset_8,
+    powerset_12,
+    powerset_0_fold,
+    powerset_1_fold,
+    powerset_2_fold,
+    powerset_4_fold,
+    powerset_8_fold,
+    powerset_12_fold,
+);
+criterion_main!(benches);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/specializations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/specializations.rs
new file mode 100644
index 0000000..aca6a55
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/specializations.rs
@@ -0,0 +1,670 @@
+#![allow(unstable_name_collisions)]
+
+use criterion::black_box;
+use criterion::BenchmarkId;
+use itertools::Itertools;
+
+const NTH_INPUTS: &[usize] = &[0, 1, 2, 4, 8];
+
+/// Create multiple functions each defining a benchmark group about iterator
+/// methods.
+///
+/// Each created group has functions with the following ids:
+///
+/// - `next`, `size_hint`, `count`, `last`, `nth`, `collect`, `fold`
+/// - and when marked as `DoubleEndedIterator`: `next_back`, `nth_back`, `rfold`
+/// - and when marked as `ExactSizeIterator`: `len`
+///
+/// Note that this macro can be called only once.
+macro_rules! bench_specializations {
+    (
+        $(
+            $name:ident {
+                $($extra:ident)*
+                {$(
+                    $init:stmt;
+                )*}
+                $iterator:expr
+            }
+        )*
+    ) => {
+        $(
+            #[allow(unused_must_use)]
+            fn $name(c: &mut ::criterion::Criterion) {
+                let mut bench_group = c.benchmark_group(stringify!($name));
+                $(
+                    $init
+                )*
+                let bench_first_its = {
+                    let mut bench_idx = 0;
+                    [0; 1000].map(|_| {
+                        let mut it = $iterator;
+                        if bench_idx != 0 {
+                            it.nth(bench_idx - 1);
+                        }
+                        bench_idx += 1;
+                        it
+                    })
+                };
+                bench_specializations!(@Iterator bench_group bench_first_its: $iterator);
+                $(
+                    bench_specializations!(@$extra bench_group bench_first_its: $iterator);
+                )*
+                bench_group.finish();
+            }
+        )*
+
+        ::criterion::criterion_group!(benches, $($name, )*);
+        ::criterion::criterion_main!(benches);
+    };
+
+    (@Iterator $group:ident $first_its:ident: $iterator:expr) => {
+        $group.bench_function("next", |bencher| bencher.iter(|| {
+            let mut it = $iterator;
+            while let Some(x) = it.next() {
+                black_box(x);
+            }
+        }));
+        $group.bench_function("size_hint", |bencher| bencher.iter(|| {
+            $first_its.iter().for_each(|it| {
+                black_box(it.size_hint());
+            })
+        }));
+        $group.bench_function("count", |bencher| bencher.iter(|| {
+            $iterator.count()
+        }));
+        $group.bench_function("last", |bencher| bencher.iter(|| {
+            $iterator.last()
+        }));
+        for n in NTH_INPUTS {
+            $group.bench_with_input(BenchmarkId::new("nth", n), n, |bencher, n| bencher.iter(|| {
+                for start in 0_usize..10 {
+                    let mut it = $iterator;
+                    if let Some(s) = start.checked_sub(1) {
+                        black_box(it.nth(s));
+                    }
+                    while let Some(x) = it.nth(*n) {
+                        black_box(x);
+                    }
+                }
+            }));
+        }
+        $group.bench_function("collect", |bencher| bencher.iter(|| {
+            $iterator.collect::<Vec<_>>()
+        }));
+        $group.bench_function("fold", |bencher| bencher.iter(|| {
+            $iterator.fold((), |(), x| {
+                black_box(x);
+            })
+        }));
+    };
+
+    (@DoubleEndedIterator $group:ident $_first_its:ident: $iterator:expr) => {
+        $group.bench_function("next_back", |bencher| bencher.iter(|| {
+            let mut it = $iterator;
+            while let Some(x) = it.next_back() {
+                black_box(x);
+            }
+        }));
+        for n in NTH_INPUTS {
+            $group.bench_with_input(BenchmarkId::new("nth_back", n), n, |bencher, n| bencher.iter(|| {
+                for start in 0_usize..10 {
+                    let mut it = $iterator;
+                    if let Some(s) = start.checked_sub(1) {
+                        black_box(it.nth_back(s));
+                    }
+                    while let Some(x) = it.nth_back(*n) {
+                        black_box(x);
+                    }
+                }
+            }));
+        }
+        $group.bench_function("rfold", |bencher| bencher.iter(|| {
+            $iterator.rfold((), |(), x| {
+                black_box(x);
+            })
+        }));
+    };
+
+    (@ExactSizeIterator $group:ident $first_its:ident: $_iterator:expr) => {
+        $group.bench_function("len", |bencher| bencher.iter(|| {
+            $first_its.iter().for_each(|it| {
+                black_box(it.len());
+            })
+        }));
+    };
+}
+
+// Usage examples:
+// - For `ZipLongest::fold` only: cargo bench --bench specializations
+//   zip_longest/fold
+// - For `.combinations(k).nth(8)`: cargo bench --bench specializations
+//   combinations./nth/8
+bench_specializations! {
+    interleave {
+        {
+            let v1 = black_box(vec![0; 1024]);
+            let v2 = black_box(vec![0; 768]);
+        }
+        v1.iter().interleave(&v2)
+    }
+    interleave_shortest {
+        {
+            let v1 = black_box(vec![0; 1024]);
+            let v2 = black_box(vec![0; 768]);
+        }
+        v1.iter().interleave_shortest(&v2)
+    }
+    batching {
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().batching(Iterator::next)
+    }
+    tuple_windows1 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuple_windows::<(_,)>()
+    }
+    tuple_windows2 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuple_windows::<(_, _)>()
+    }
+    tuple_windows3 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuple_windows::<(_, _, _)>()
+    }
+    tuple_windows4 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuple_windows::<(_, _, _, _)>()
+    }
+    circular_tuple_windows1 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().circular_tuple_windows::<(_,)>()
+    }
+    circular_tuple_windows2 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().circular_tuple_windows::<(_, _)>()
+    }
+    circular_tuple_windows3 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().circular_tuple_windows::<(_, _, _)>()
+    }
+    circular_tuple_windows4 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().circular_tuple_windows::<(_, _, _, _)>()
+    }
+    tuples1 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuples::<(_,)>()
+    }
+    tuples2 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuples::<(_, _)>()
+    }
+    tuples3 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuples::<(_, _, _)>()
+    }
+    tuples4 {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuples::<(_, _, _, _)>()
+    }
+    tuple_buffer {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 11]);
+            // Short but the buffer can't have 12 or more elements.
+        }
+        {
+            let mut it = v.iter().tuples::<(_, _, _, _, _, _, _, _, _, _, _, _)>();
+            it.next(); // No element but it fills the buffer.
+            it.into_buffer()
+        }
+    }
+    cartesian_product {
+        {
+            let v = black_box(vec![0; 16]);
+        }
+        itertools::iproduct!(&v, &v, &v)
+    }
+    multi_cartesian_product {
+        {
+            let vs = black_box([0; 3].map(|_| vec![0; 16]));
+        }
+        vs.iter().multi_cartesian_product()
+    }
+    coalesce {
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) })
+    }
+    dedup {
+        {
+            let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec());
+        }
+        v.iter().dedup()
+    }
+    dedup_by {
+        {
+            let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec());
+        }
+        v.iter().dedup_by(PartialOrd::ge)
+    }
+    dedup_with_count {
+        {
+            let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec());
+        }
+        v.iter().dedup_with_count()
+    }
+    dedup_by_with_count {
+        {
+            let v = black_box((0..32).flat_map(|x| [x; 32]).collect_vec());
+        }
+        v.iter().dedup_by_with_count(PartialOrd::ge)
+    }
+    duplicates {
+        DoubleEndedIterator
+        {
+            let v = black_box((0..32).cycle().take(1024).collect_vec());
+        }
+        v.iter().duplicates()
+    }
+    duplicates_by {
+        DoubleEndedIterator
+        {
+            let v = black_box((0..1024).collect_vec());
+        }
+        v.iter().duplicates_by(|x| *x % 10)
+    }
+    unique {
+        DoubleEndedIterator
+        {
+            let v = black_box((0..32).cycle().take(1024).collect_vec());
+        }
+        v.iter().unique()
+    }
+    unique_by {
+        DoubleEndedIterator
+        {
+            let v = black_box((0..1024).collect_vec());
+        }
+        v.iter().unique_by(|x| *x % 50)
+    }
+    take_while_inclusive {
+        {
+            let v = black_box((0..1024).collect_vec());
+        }
+        v.iter().take_while_inclusive(|x| **x < 1000)
+    }
+    pad_using {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {
+            let v = black_box((0..1024).collect_vec());
+        }
+        v.iter().copied().pad_using(2048, |i| 5 * i)
+    }
+    positions {
+        DoubleEndedIterator
+        {
+            let v = black_box((0..1024).collect_vec());
+        }
+        v.iter().positions(|x| x % 5 == 0)
+    }
+    update {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {
+            let v = black_box((0_i32..1024).collect_vec());
+        }
+        v.iter().copied().update(|x| *x *= 7)
+    }
+    tuple_combinations1 {
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().tuple_combinations::<(_,)>()
+    }
+    tuple_combinations2 {
+        {
+            let v = black_box(vec![0; 64]);
+        }
+        v.iter().tuple_combinations::<(_, _)>()
+    }
+    tuple_combinations3 {
+        {
+            let v = black_box(vec![0; 64]);
+        }
+        v.iter().tuple_combinations::<(_, _, _)>()
+    }
+    tuple_combinations4 {
+        {
+            let v = black_box(vec![0; 64]);
+        }
+        v.iter().tuple_combinations::<(_, _, _, _)>()
+    }
+    intersperse {
+        {
+            let v = black_box(vec![0; 1024]);
+            let n = black_box(0);
+        }
+        v.iter().intersperse(&n)
+    }
+    intersperse_with {
+        {
+            let v = black_box(vec![0; 1024]);
+            let n = black_box(0);
+        }
+        v.iter().intersperse_with(|| &n)
+    }
+    combinations1 {
+        {
+            let v = black_box(vec![0; 1792]);
+        }
+        v.iter().combinations(1)
+    }
+    combinations2 {
+        {
+            let v = black_box(vec![0; 60]);
+        }
+        v.iter().combinations(2)
+    }
+    combinations3 {
+        {
+            let v = black_box(vec![0; 23]);
+        }
+        v.iter().combinations(3)
+    }
+    combinations4 {
+        {
+            let v = black_box(vec![0; 16]);
+        }
+        v.iter().combinations(4)
+    }
+    combinations_with_replacement1 {
+        {
+            let v = black_box(vec![0; 4096]);
+        }
+        v.iter().combinations_with_replacement(1)
+    }
+    combinations_with_replacement2 {
+        {
+            let v = black_box(vec![0; 90]);
+        }
+        v.iter().combinations_with_replacement(2)
+    }
+    combinations_with_replacement3 {
+        {
+            let v = black_box(vec![0; 28]);
+        }
+        v.iter().combinations_with_replacement(3)
+    }
+    combinations_with_replacement4 {
+        {
+            let v = black_box(vec![0; 16]);
+        }
+        v.iter().combinations_with_replacement(4)
+    }
+    permutations1 {
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().permutations(1)
+    }
+    permutations2 {
+        {
+            let v = black_box(vec![0; 36]);
+        }
+        v.iter().permutations(2)
+    }
+    permutations3 {
+        {
+            let v = black_box(vec![0; 12]);
+        }
+        v.iter().permutations(3)
+    }
+    permutations4 {
+        {
+            let v = black_box(vec![0; 8]);
+        }
+        v.iter().permutations(4)
+    }
+    powerset {
+        {
+            let v = black_box(vec![0; 10]);
+        }
+        v.iter().powerset()
+    }
+    while_some {
+        {}
+        (0..)
+            .map(black_box)
+            .map(|i| char::from_digit(i, 16))
+            .while_some()
+    }
+    with_position {
+        ExactSizeIterator
+        {
+            let v = black_box((0..10240).collect_vec());
+        }
+        v.iter().with_position()
+    }
+    zip_longest {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {
+            let xs = black_box(vec![0; 1024]);
+            let ys = black_box(vec![0; 768]);
+        }
+        xs.iter().zip_longest(ys.iter())
+    }
+    zip_eq {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        v.iter().zip_eq(v.iter().rev())
+    }
+    multizip {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {
+            let v1 = black_box(vec![0; 1024]);
+            let v2 = black_box(vec![0; 768]);
+            let v3 = black_box(vec![0; 2048]);
+        }
+        itertools::multizip((&v1, &v2, &v3))
+    }
+    izip {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {
+            let v1 = black_box(vec![0; 1024]);
+            let v2 = black_box(vec![0; 768]);
+            let v3 = black_box(vec![0; 2048]);
+        }
+        itertools::izip!(&v1, &v2, &v3)
+    }
+    put_back {
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        itertools::put_back(&v).with_value(black_box(&0))
+    }
+    put_back_n {
+        {
+            let v1 = black_box(vec![0; 1024]);
+            let v2 = black_box(vec![0; 16]);
+        }
+        {
+            let mut it = itertools::put_back_n(&v1);
+            for n in &v2 {
+                it.put_back(n);
+            }
+            it
+        }
+    }
+    exactly_one_error {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+        }
+        // Use `at_most_one` would be similar.
+        v.iter().exactly_one().unwrap_err()
+    }
+    multipeek {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+            let n = black_box(16);
+        }
+        {
+            let mut it = v.iter().multipeek();
+            for _ in 0..n {
+                it.peek();
+            }
+            it
+        }
+    }
+    peek_nth {
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0; 1024]);
+            let n = black_box(16);
+        }
+        {
+            let mut it = itertools::peek_nth(&v);
+            it.peek_nth(n);
+            it
+        }
+    }
+    repeat_n {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {}
+        itertools::repeat_n(black_box(0), black_box(1024))
+    }
+    merge {
+        {
+            let v1 = black_box((0..1024).collect_vec());
+            let v2 = black_box((0..768).collect_vec());
+        }
+        v1.iter().merge(&v2)
+    }
+    merge_by {
+        {
+            let v1 = black_box((0..1024).collect_vec());
+            let v2 = black_box((0..768).collect_vec());
+        }
+        v1.iter().merge_by(&v2, PartialOrd::ge)
+    }
+    merge_join_by_ordering {
+        {
+            let v1 = black_box((0..1024).collect_vec());
+            let v2 = black_box((0..768).collect_vec());
+        }
+        v1.iter().merge_join_by(&v2, Ord::cmp)
+    }
+    merge_join_by_bool {
+        {
+            let v1 = black_box((0..1024).collect_vec());
+            let v2 = black_box((0..768).collect_vec());
+        }
+        v1.iter().merge_join_by(&v2, PartialOrd::ge)
+    }
+    kmerge {
+        {
+            let vs = black_box(vec![vec![0; 1024], vec![0; 256], vec![0; 768]]);
+        }
+        vs.iter().kmerge()
+    }
+    kmerge_by {
+        {
+            let vs = black_box(vec![vec![0; 1024], vec![0; 256], vec![0; 768]]);
+        }
+        vs.iter().kmerge_by(PartialOrd::ge)
+    }
+    map_into {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {
+            let v = black_box(vec![0_u8; 1024]);
+        }
+        v.iter().copied().map_into::<u32>()
+    }
+    map_ok {
+        DoubleEndedIterator
+        ExactSizeIterator
+        {
+            let v = black_box((0_u32..1024)
+                .map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) })
+                .collect_vec());
+        }
+        v.iter().copied().map_ok(|x| x + 1)
+    }
+    filter_ok {
+        DoubleEndedIterator
+        {
+            let v = black_box((0_u32..1024)
+                .map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) })
+                .collect_vec());
+        }
+        v.iter().copied().filter_ok(|x| x % 3 == 0)
+    }
+    filter_map_ok {
+        DoubleEndedIterator
+        {
+            let v = black_box((0_u32..1024)
+                .map(|x| if x % 2 == 1 { Err(x) } else { Ok(x) })
+                .collect_vec());
+        }
+        v.iter().copied().filter_map_ok(|x| if x % 3 == 0 { Some(x + 1) } else { None })
+    }
+    flatten_ok {
+        DoubleEndedIterator
+        {
+            let d = black_box(vec![0; 8]);
+            let v = black_box((0..512)
+                .map(|x| if x % 2 == 0 { Ok(&d) } else { Err(x) })
+                .collect_vec());
+        }
+        v.iter().copied().flatten_ok()
+    }
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tree_reduce.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tree_reduce.rs
new file mode 100644
index 0000000..722a78cd
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tree_reduce.rs
@@ -0,0 +1,147 @@
+#![allow(deprecated)]
+
+use criterion::{criterion_group, criterion_main, Criterion};
+use itertools::{cloned, Itertools};
+
+trait IterEx: Iterator {
+    // Another efficient implementation against which to compare,
+    // but needs `std` so is less desirable.
+    fn tree_reduce_vec<F>(self, mut f: F) -> Option<Self::Item>
+    where
+        F: FnMut(Self::Item, Self::Item) -> Self::Item,
+        Self: Sized,
+    {
+        let hint = self.size_hint().0;
+        let cap = std::mem::size_of::<usize>() * 8 - hint.leading_zeros() as usize;
+        let mut stack = Vec::with_capacity(cap);
+        self.enumerate().for_each(|(mut i, mut x)| {
+            while (i & 1) != 0 {
+                x = f(stack.pop().unwrap(), x);
+                i >>= 1;
+            }
+            stack.push(x);
+        });
+        stack.into_iter().fold1(f)
+    }
+}
+impl<T: Iterator> IterEx for T {}
+
+macro_rules! def_benchs {
+    ($N:expr,
+     $FUN:ident,
+     $BENCH_NAME:ident,
+     ) => {
+        mod $BENCH_NAME {
+            use super::*;
+
+            pub fn sum(c: &mut Criterion) {
+                let v: Vec<u32> = (0..$N).collect();
+
+                c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " sum"), move |b| {
+                    b.iter(|| cloned(&v).$FUN(|x, y| x + y))
+                });
+            }
+
+            pub fn complex_iter(c: &mut Criterion) {
+                let u = (3..).take($N / 2);
+                let v = (5..).take($N / 2);
+                let it = u.chain(v);
+
+                c.bench_function(
+                    &(stringify!($BENCH_NAME).replace('_', " ") + " complex iter"),
+                    move |b| b.iter(|| it.clone().map(|x| x as f32).$FUN(f32::atan2)),
+                );
+            }
+
+            pub fn string_format(c: &mut Criterion) {
+                // This goes quadratic with linear `fold1`, so use a smaller
+                // size to not waste too much time in travis.  The allocations
+                // in here are so expensive anyway that it'll still take
+                // way longer per iteration than the other two benchmarks.
+                let v: Vec<u32> = (0..($N / 4)).collect();
+
+                c.bench_function(
+                    &(stringify!($BENCH_NAME).replace('_', " ") + " string format"),
+                    move |b| {
+                        b.iter(|| {
+                            cloned(&v).map(|x| x.to_string()).$FUN(|x, y| format!("{} + {}", x, y))
+                        })
+                    },
+                );
+            }
+        }
+
+        criterion_group!(
+            $BENCH_NAME,
+            $BENCH_NAME::sum,
+            $BENCH_NAME::complex_iter,
+            $BENCH_NAME::string_format,
+        );
+    };
+}
+
+def_benchs! {
+    10_000,
+    fold1,
+    fold1_10k,
+}
+
+def_benchs! {
+    10_000,
+    tree_reduce,
+    tree_reduce_stack_10k,
+}
+
+def_benchs! {
+    10_000,
+    tree_reduce_vec,
+    tree_reduce_vec_10k,
+}
+
+def_benchs! {
+    100,
+    fold1,
+    fold1_100,
+}
+
+def_benchs! {
+    100,
+    tree_reduce,
+    tree_reduce_stack_100,
+}
+
+def_benchs! {
+    100,
+    tree_reduce_vec,
+    tree_reduce_vec_100,
+}
+
+def_benchs! {
+    8,
+    fold1,
+    fold1_08,
+}
+
+def_benchs! {
+    8,
+    tree_reduce,
+    tree_reduce_stack_08,
+}
+
+def_benchs! {
+    8,
+    tree_reduce_vec,
+    tree_reduce_vec_08,
+}
+
+criterion_main!(
+    fold1_10k,
+    tree_reduce_stack_10k,
+    tree_reduce_vec_10k,
+    fold1_100,
+    tree_reduce_stack_100,
+    tree_reduce_vec_100,
+    fold1_08,
+    tree_reduce_stack_08,
+    tree_reduce_vec_08,
+);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tuple_combinations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tuple_combinations.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tuple_combinations.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tuple_combinations.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tuples.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tuples.rs
similarity index 92%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tuples.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tuples.rs
index ea50aaae..2eca347 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/benches/tuples.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/benches/tuples.rs
@@ -33,7 +33,7 @@
     s4(s[0], s[1], s[2], s[3])
 }
 
-fn sum_t1(s: &(&u32, )) -> u32 {
+fn sum_t1(s: &(&u32,)) -> u32 {
     s1(*s.0)
 }
 
@@ -60,9 +60,9 @@
      $WINDOWS:ident;
      $FOR_CHUNKS:ident,
      $FOR_WINDOWS:ident
-     ) => (
+     ) => {
         fn $FOR_CHUNKS(c: &mut Criterion) {
-            let v: Vec<u32> = (0.. $N * 1_000).collect();
+            let v: Vec<u32> = (0..$N * 1_000).collect();
             let mut s = 0;
             c.bench_function(&stringify!($FOR_CHUNKS).replace('_', " "), move |b| {
                 b.iter(|| {
@@ -90,7 +90,7 @@
         }
 
         fn $TUPLES(c: &mut Criterion) {
-            let v: Vec<u32> = (0.. $N * 1_000).collect();
+            let v: Vec<u32> = (0..$N * 1_000).collect();
             let mut s = 0;
             c.bench_function(&stringify!($TUPLES).replace('_', " "), move |b| {
                 b.iter(|| {
@@ -103,7 +103,7 @@
         }
 
         fn $CHUNKS(c: &mut Criterion) {
-            let v: Vec<u32> = (0.. $N * 1_000).collect();
+            let v: Vec<u32> = (0..$N * 1_000).collect();
             let mut s = 0;
             c.bench_function(&stringify!($CHUNKS).replace('_', " "), move |b| {
                 b.iter(|| {
@@ -150,10 +150,10 @@
             $TUPLE_WINDOWS,
             $WINDOWS,
         );
-    )
+    };
 }
 
-def_benchs!{
+def_benchs! {
     1;
     benches_1,
     sum_t1,
@@ -166,7 +166,7 @@
     for_windows_1
 }
 
-def_benchs!{
+def_benchs! {
     2;
     benches_2,
     sum_t2,
@@ -179,7 +179,7 @@
     for_windows_2
 }
 
-def_benchs!{
+def_benchs! {
     3;
     benches_3,
     sum_t3,
@@ -192,7 +192,7 @@
     for_windows_3
 }
 
-def_benchs!{
+def_benchs! {
     4;
     benches_4,
     sum_t4,
@@ -205,9 +205,4 @@
     for_windows_4
 }
 
-criterion_main!(
-    benches_1,
-    benches_2,
-    benches_3,
-    benches_4,
-);
+criterion_main!(benches_1, benches_2, benches_3, benches_4,);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/examples/iris.data b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/examples/iris.data
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/examples/iris.data
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/examples/iris.data
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/examples/iris.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/examples/iris.rs
similarity index 81%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/examples/iris.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/examples/iris.rs
index 987d9e9..e7783436 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/examples/iris.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/examples/iris.rs
@@ -3,14 +3,13 @@
 /// and does some simple manipulations.
 ///
 /// Iterators and itertools functionality are used throughout.
-
 use itertools::Itertools;
 use std::collections::HashMap;
 use std::iter::repeat;
 use std::num::ParseFloatError;
 use std::str::FromStr;
 
-static DATA: &'static str = include_str!("iris.data");
+static DATA: &str = include_str!("iris.data");
 
 #[derive(Clone, Debug)]
 struct Iris {
@@ -18,6 +17,7 @@
     data: [f32; 4],
 }
 
+#[allow(dead_code)] // fields are currently ignored
 #[derive(Clone, Debug)]
 enum ParseError {
     Numeric(ParseFloatError),
@@ -26,7 +26,7 @@
 
 impl From<ParseFloatError> for ParseError {
     fn from(err: ParseFloatError) -> Self {
-        ParseError::Numeric(err)
+        Self::Numeric(err)
     }
 }
 
@@ -35,8 +35,8 @@
     type Err = ParseError;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
-        let mut iris = Iris { name: "".into(), data: [0.; 4] };
-        let mut parts = s.split(",").map(str::trim);
+        let mut iris = Self { name: "".into(), data: [0.; 4] };
+        let mut parts = s.split(',').map(str::trim);
 
         // using Iterator::by_ref()
         for (index, part) in parts.by_ref().take(4).enumerate() {
@@ -45,7 +45,7 @@
         if let Some(name) = parts.next() {
             iris.name = name.into();
         } else {
-            return Err(ParseError::Other("Missing name"))
+            return Err(ParseError::Other("Missing name"));
         }
         Ok(iris)
     }
@@ -53,12 +53,10 @@
 
 fn main() {
     // using Itertools::fold_results to create the result of parsing
-    let irises = DATA.lines()
-                     .map(str::parse)
-                     .fold_ok(Vec::new(), |mut v, iris: Iris| {
-                         v.push(iris);
-                         v
-                     });
+    let irises = DATA.lines().map(str::parse).fold_ok(Vec::new(), |mut v, iris: Iris| {
+        v.push(iris);
+        v
+    });
     let mut irises = match irises {
         Err(e) => {
             println!("Error parsing: {:?}", e);
@@ -74,19 +72,16 @@
     let mut plot_symbols = "+ox".chars().cycle();
     let mut symbolmap = HashMap::new();
 
-    // using Itertools::group_by
-    for (species, species_group) in &irises.iter().group_by(|iris| &iris.name) {
+    // using Itertools::chunk_by
+    for (species, species_chunk) in &irises.iter().chunk_by(|iris| &iris.name) {
         // assign a plot symbol
-        symbolmap.entry(species).or_insert_with(|| {
-            plot_symbols.next().unwrap()
-        });
+        symbolmap.entry(species).or_insert_with(|| plot_symbols.next().unwrap());
         println!("{} (symbol={})", species, symbolmap[species]);
 
-        for iris in species_group {
+        for iris in species_chunk {
             // using Itertools::format for lazy formatting
             println!("{:>3.1}", iris.data.iter().format(", "));
         }
-
     }
 
     // Look at all combinations of the four columns
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/coalesce.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/coalesce.rs
new file mode 100644
index 0000000..5c0ddd45
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/coalesce.rs
@@ -0,0 +1,274 @@
+use std::fmt;
+use std::iter::FusedIterator;
+
+use crate::size_hint;
+
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct CoalesceBy<I, F, C>
+where
+    I: Iterator,
+    C: CountItem<I::Item>,
+{
+    iter: I,
+    /// `last` is `None` while no item have been taken out of `iter` (at
+    /// definition). Then `last` will be `Some(Some(item))` until `iter` is
+    /// exhausted, in which case `last` will be `Some(None)`.
+    last: Option<Option<C::CItem>>,
+    f: F,
+}
+
+impl<I, F, C> Clone for CoalesceBy<I, F, C>
+where
+    I: Clone + Iterator,
+    F: Clone,
+    C: CountItem<I::Item>,
+    C::CItem: Clone,
+{
+    clone_fields!(last, iter, f);
+}
+
+impl<I, F, C> fmt::Debug for CoalesceBy<I, F, C>
+where
+    I: Iterator + fmt::Debug,
+    C: CountItem<I::Item>,
+    C::CItem: fmt::Debug,
+{
+    debug_fmt_fields!(CoalesceBy, iter, last);
+}
+
+pub trait CoalescePredicate<Item, T> {
+    fn coalesce_pair(&mut self, t: T, item: Item) -> Result<T, (T, T)>;
+}
+
+impl<I, F, C> Iterator for CoalesceBy<I, F, C>
+where
+    I: Iterator,
+    F: CoalescePredicate<I::Item, C::CItem>,
+    C: CountItem<I::Item>,
+{
+    type Item = C::CItem;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let Self { iter, last, f } = self;
+        // this fuses the iterator
+        let init = match last {
+            Some(elt) => elt.take(),
+            None => {
+                *last = Some(None);
+                iter.next().map(C::new)
+            }
+        }?;
+
+        Some(
+            iter.try_fold(init, |accum, next| match f.coalesce_pair(accum, next) {
+                Ok(joined) => Ok(joined),
+                Err((last_, next_)) => {
+                    *last = Some(Some(next_));
+                    Err(last_)
+                }
+            })
+            .unwrap_or_else(|x| x),
+        )
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let (low, hi) = size_hint::add_scalar(
+            self.iter.size_hint(),
+            matches!(self.last, Some(Some(_))) as usize,
+        );
+        ((low > 0) as usize, hi)
+    }
+
+    fn fold<Acc, FnAcc>(self, acc: Acc, mut fn_acc: FnAcc) -> Acc
+    where
+        FnAcc: FnMut(Acc, Self::Item) -> Acc,
+    {
+        let Self { mut iter, last, mut f } = self;
+        if let Some(last) = last.unwrap_or_else(|| iter.next().map(C::new)) {
+            let (last, acc) =
+                iter.fold((last, acc), |(last, acc), elt| match f.coalesce_pair(last, elt) {
+                    Ok(joined) => (joined, acc),
+                    Err((last_, next_)) => (next_, fn_acc(acc, last_)),
+                });
+            fn_acc(acc, last)
+        } else {
+            acc
+        }
+    }
+}
+
+impl<I, F, C> FusedIterator for CoalesceBy<I, F, C>
+where
+    I: Iterator,
+    F: CoalescePredicate<I::Item, C::CItem>,
+    C: CountItem<I::Item>,
+{
+}
+
+pub struct NoCount;
+
+pub struct WithCount;
+
+pub trait CountItem<T> {
+    type CItem;
+    fn new(t: T) -> Self::CItem;
+}
+
+impl<T> CountItem<T> for NoCount {
+    type CItem = T;
+    #[inline(always)]
+    fn new(t: T) -> T {
+        t
+    }
+}
+
+impl<T> CountItem<T> for WithCount {
+    type CItem = (usize, T);
+    #[inline(always)]
+    fn new(t: T) -> (usize, T) {
+        (1, t)
+    }
+}
+
+/// An iterator adaptor that may join together adjacent elements.
+///
+/// See [`.coalesce()`](crate::Itertools::coalesce) for more information.
+pub type Coalesce<I, F> = CoalesceBy<I, F, NoCount>;
+
+impl<F, Item, T> CoalescePredicate<Item, T> for F
+where
+    F: FnMut(T, Item) -> Result<T, (T, T)>,
+{
+    fn coalesce_pair(&mut self, t: T, item: Item) -> Result<T, (T, T)> {
+        self(t, item)
+    }
+}
+
+/// Create a new `Coalesce`.
+pub fn coalesce<I, F>(iter: I, f: F) -> Coalesce<I, F>
+where
+    I: Iterator,
+{
+    Coalesce { last: None, iter, f }
+}
+
+/// An iterator adaptor that removes repeated duplicates, determining equality
+/// using a comparison function.
+///
+/// See [`.dedup_by()`](crate::Itertools::dedup_by) or
+/// [`.dedup()`](crate::Itertools::dedup) for more information.
+pub type DedupBy<I, Pred> = CoalesceBy<I, DedupPred2CoalescePred<Pred>, NoCount>;
+
+#[derive(Clone)]
+pub struct DedupPred2CoalescePred<DP>(DP);
+
+impl<DP> fmt::Debug for DedupPred2CoalescePred<DP> {
+    debug_fmt_fields!(DedupPred2CoalescePred,);
+}
+
+pub trait DedupPredicate<T> {
+    // TODO replace by Fn(&T, &T)->bool once Rust supports it
+    fn dedup_pair(&mut self, a: &T, b: &T) -> bool;
+}
+
+impl<DP, T> CoalescePredicate<T, T> for DedupPred2CoalescePred<DP>
+where
+    DP: DedupPredicate<T>,
+{
+    fn coalesce_pair(&mut self, t: T, item: T) -> Result<T, (T, T)> {
+        if self.0.dedup_pair(&t, &item) {
+            Ok(t)
+        } else {
+            Err((t, item))
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct DedupEq;
+
+impl<T: PartialEq> DedupPredicate<T> for DedupEq {
+    fn dedup_pair(&mut self, a: &T, b: &T) -> bool {
+        a == b
+    }
+}
+
+impl<T, F: FnMut(&T, &T) -> bool> DedupPredicate<T> for F {
+    fn dedup_pair(&mut self, a: &T, b: &T) -> bool {
+        self(a, b)
+    }
+}
+
+/// Create a new `DedupBy`.
+pub fn dedup_by<I, Pred>(iter: I, dedup_pred: Pred) -> DedupBy<I, Pred>
+where
+    I: Iterator,
+{
+    DedupBy { last: None, iter, f: DedupPred2CoalescePred(dedup_pred) }
+}
+
+/// An iterator adaptor that removes repeated duplicates.
+///
+/// See [`.dedup()`](crate::Itertools::dedup) for more information.
+pub type Dedup<I> = DedupBy<I, DedupEq>;
+
+/// Create a new `Dedup`.
+pub fn dedup<I>(iter: I) -> Dedup<I>
+where
+    I: Iterator,
+{
+    dedup_by(iter, DedupEq)
+}
+
+/// An iterator adaptor that removes repeated duplicates, while keeping a count
+/// of how many repeated elements were present. This will determine equality
+/// using a comparison function.
+///
+/// See [`.dedup_by_with_count()`](crate::Itertools::dedup_by_with_count) or
+/// [`.dedup_with_count()`](crate::Itertools::dedup_with_count) for more
+/// information.
+pub type DedupByWithCount<I, Pred> =
+    CoalesceBy<I, DedupPredWithCount2CoalescePred<Pred>, WithCount>;
+
+#[derive(Clone, Debug)]
+pub struct DedupPredWithCount2CoalescePred<DP>(DP);
+
+impl<DP, T> CoalescePredicate<T, (usize, T)> for DedupPredWithCount2CoalescePred<DP>
+where
+    DP: DedupPredicate<T>,
+{
+    fn coalesce_pair(
+        &mut self,
+        (c, t): (usize, T),
+        item: T,
+    ) -> Result<(usize, T), ((usize, T), (usize, T))> {
+        if self.0.dedup_pair(&t, &item) {
+            Ok((c + 1, t))
+        } else {
+            Err(((c, t), (1, item)))
+        }
+    }
+}
+
+/// An iterator adaptor that removes repeated duplicates, while keeping a count
+/// of how many repeated elements were present.
+///
+/// See [`.dedup_with_count()`](crate::Itertools::dedup_with_count) for more
+/// information.
+pub type DedupWithCount<I> = DedupByWithCount<I, DedupEq>;
+
+/// Create a new `DedupByWithCount`.
+pub fn dedup_by_with_count<I, Pred>(iter: I, dedup_pred: Pred) -> DedupByWithCount<I, Pred>
+where
+    I: Iterator,
+{
+    DedupByWithCount { last: None, iter, f: DedupPredWithCount2CoalescePred(dedup_pred) }
+}
+
+/// Create a new `DedupWithCount`.
+pub fn dedup_with_count<I>(iter: I) -> DedupWithCount<I>
+where
+    I: Iterator,
+{
+    dedup_by_with_count(iter, DedupEq)
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/map.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/map.rs
similarity index 87%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/map.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/map.rs
index cf5e5a0..2ca30d6 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/map.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/map.rs
@@ -4,8 +4,8 @@
 #[derive(Clone, Debug)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct MapSpecialCase<I, F> {
-    iter: I,
-    f: F,
+    pub(crate) iter: I,
+    pub(crate) f: F,
 }
 
 pub trait MapSpecialCaseFn<T> {
@@ -67,10 +67,6 @@
 /// See [`.map_ok()`](crate::Itertools::map_ok) for more information.
 pub type MapOk<I, F> = MapSpecialCase<I, MapSpecialCaseFnOk<F>>;
 
-/// See [`MapOk`].
-#[deprecated(note = "Use MapOk instead", since = "0.10.0")]
-pub type MapResults<I, F> = MapOk<I, F>;
-
 impl<F, T, U, E> MapSpecialCaseFn<Result<T, E>> for MapSpecialCaseFnOk<F>
 where
     F: FnMut(T) -> U,
@@ -94,10 +90,7 @@
     I: Iterator<Item = Result<T, E>>,
     F: FnMut(T) -> U,
 {
-    MapSpecialCase {
-        iter,
-        f: MapSpecialCaseFnOk(f),
-    }
+    MapSpecialCase { iter, f: MapSpecialCaseFnOk(f) }
 }
 
 /// An iterator adapter to apply `Into` conversion to each element.
@@ -112,13 +105,20 @@
     }
 }
 
-#[derive(Clone, Debug)]
 pub struct MapSpecialCaseFnInto<U>(PhantomData<U>);
 
+impl<U> std::fmt::Debug for MapSpecialCaseFnInto<U> {
+    debug_fmt_fields!(MapSpecialCaseFnInto, 0);
+}
+
+impl<U> Clone for MapSpecialCaseFnInto<U> {
+    #[inline]
+    fn clone(&self) -> Self {
+        Self(PhantomData)
+    }
+}
+
 /// Create a new [`MapInto`] iterator.
 pub fn map_into<I, R>(iter: I) -> MapInto<I, R> {
-    MapSpecialCase {
-        iter,
-        f: MapSpecialCaseFnInto(PhantomData),
-    }
+    MapSpecialCase { iter, f: MapSpecialCaseFnInto(PhantomData) }
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/mod.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/mod.rs
new file mode 100644
index 0000000..5df2db1
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/mod.rs
@@ -0,0 +1,1209 @@
+//! Licensed under the Apache License, Version 2.0
+//! <https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+//! <https://opensource.org/licenses/MIT>, at your
+//! option. This file may not be copied, modified, or distributed
+//! except according to those terms.
+
+mod coalesce;
+pub(crate) mod map;
+mod multi_product;
+pub use self::coalesce::*;
+pub use self::map::{map_into, map_ok, MapInto, MapOk};
+#[cfg(feature = "use_alloc")]
+pub use self::multi_product::*;
+
+use crate::size_hint::{self, SizeHint};
+use std::fmt;
+use std::iter::{Enumerate, FromIterator, Fuse, FusedIterator};
+use std::marker::PhantomData;
+
+/// An iterator adaptor that alternates elements from two iterators until both
+/// run out.
+///
+/// This iterator is *fused*.
+///
+/// See [`.interleave()`](crate::Itertools::interleave) for more information.
+#[derive(Clone, Debug)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct Interleave<I, J> {
+    i: Fuse<I>,
+    j: Fuse<J>,
+    next_coming_from_j: bool,
+}
+
+/// Create an iterator that interleaves elements in `i` and `j`.
+///
+/// [`IntoIterator`] enabled version of
+/// [`Itertools::interleave`](crate::Itertools::interleave).
+pub fn interleave<I, J>(
+    i: I,
+    j: J,
+) -> Interleave<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
+where
+    I: IntoIterator,
+    J: IntoIterator<Item = I::Item>,
+{
+    Interleave { i: i.into_iter().fuse(), j: j.into_iter().fuse(), next_coming_from_j: false }
+}
+
+impl<I, J> Iterator for Interleave<I, J>
+where
+    I: Iterator,
+    J: Iterator<Item = I::Item>,
+{
+    type Item = I::Item;
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        self.next_coming_from_j = !self.next_coming_from_j;
+        if self.next_coming_from_j {
+            match self.i.next() {
+                None => self.j.next(),
+                r => r,
+            }
+        } else {
+            match self.j.next() {
+                None => self.i.next(),
+                r => r,
+            }
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        size_hint::add(self.i.size_hint(), self.j.size_hint())
+    }
+
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let Self { mut i, mut j, next_coming_from_j } = self;
+        if next_coming_from_j {
+            match j.next() {
+                Some(y) => init = f(init, y),
+                None => return i.fold(init, f),
+            }
+        }
+        let res = i.try_fold(init, |mut acc, x| {
+            acc = f(acc, x);
+            match j.next() {
+                Some(y) => Ok(f(acc, y)),
+                None => Err(acc),
+            }
+        });
+        match res {
+            Ok(acc) => j.fold(acc, f),
+            Err(acc) => i.fold(acc, f),
+        }
+    }
+}
+
+impl<I, J> FusedIterator for Interleave<I, J>
+where
+    I: Iterator,
+    J: Iterator<Item = I::Item>,
+{
+}
+
+/// An iterator adaptor that alternates elements from the two iterators until
+/// one of them runs out.
+///
+/// This iterator is *fused*.
+///
+/// See [`.interleave_shortest()`](crate::Itertools::interleave_shortest)
+/// for more information.
+#[derive(Clone, Debug)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct InterleaveShortest<I, J>
+where
+    I: Iterator,
+    J: Iterator<Item = I::Item>,
+{
+    i: I,
+    j: J,
+    next_coming_from_j: bool,
+}
+
+/// Create a new `InterleaveShortest` iterator.
+pub fn interleave_shortest<I, J>(i: I, j: J) -> InterleaveShortest<I, J>
+where
+    I: Iterator,
+    J: Iterator<Item = I::Item>,
+{
+    InterleaveShortest { i, j, next_coming_from_j: false }
+}
+
+impl<I, J> Iterator for InterleaveShortest<I, J>
+where
+    I: Iterator,
+    J: Iterator<Item = I::Item>,
+{
+    type Item = I::Item;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        let e = if self.next_coming_from_j { self.j.next() } else { self.i.next() };
+        if e.is_some() {
+            self.next_coming_from_j = !self.next_coming_from_j;
+        }
+        e
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let (curr_hint, next_hint) = {
+            let i_hint = self.i.size_hint();
+            let j_hint = self.j.size_hint();
+            if self.next_coming_from_j {
+                (j_hint, i_hint)
+            } else {
+                (i_hint, j_hint)
+            }
+        };
+        let (curr_lower, curr_upper) = curr_hint;
+        let (next_lower, next_upper) = next_hint;
+        let (combined_lower, combined_upper) =
+            size_hint::mul_scalar(size_hint::min(curr_hint, next_hint), 2);
+        let lower = if curr_lower > next_lower { combined_lower + 1 } else { combined_lower };
+        let upper = {
+            let extra_elem = match (curr_upper, next_upper) {
+                (_, None) => false,
+                (None, Some(_)) => true,
+                (Some(curr_max), Some(next_max)) => curr_max > next_max,
+            };
+            if extra_elem {
+                combined_upper.and_then(|x| x.checked_add(1))
+            } else {
+                combined_upper
+            }
+        };
+        (lower, upper)
+    }
+
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let Self { mut i, mut j, next_coming_from_j } = self;
+        if next_coming_from_j {
+            match j.next() {
+                Some(y) => init = f(init, y),
+                None => return init,
+            }
+        }
+        let res = i.try_fold(init, |mut acc, x| {
+            acc = f(acc, x);
+            match j.next() {
+                Some(y) => Ok(f(acc, y)),
+                None => Err(acc),
+            }
+        });
+        match res {
+            Ok(val) => val,
+            Err(val) => val,
+        }
+    }
+}
+
+impl<I, J> FusedIterator for InterleaveShortest<I, J>
+where
+    I: FusedIterator,
+    J: FusedIterator<Item = I::Item>,
+{
+}
+
+#[derive(Clone, Debug)]
+/// An iterator adaptor that allows putting back a single
+/// item to the front of the iterator.
+///
+/// Iterator element type is `I::Item`.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct PutBack<I>
+where
+    I: Iterator,
+{
+    top: Option<I::Item>,
+    iter: I,
+}
+
+/// Create an iterator where you can put back a single item
+pub fn put_back<I>(iterable: I) -> PutBack<I::IntoIter>
+where
+    I: IntoIterator,
+{
+    PutBack { top: None, iter: iterable.into_iter() }
+}
+
+impl<I> PutBack<I>
+where
+    I: Iterator,
+{
+    /// put back value `value` (builder method)
+    pub fn with_value(mut self, value: I::Item) -> Self {
+        self.put_back(value);
+        self
+    }
+
+    /// Split the `PutBack` into its parts.
+    #[inline]
+    pub fn into_parts(self) -> (Option<I::Item>, I) {
+        let Self { top, iter } = self;
+        (top, iter)
+    }
+
+    /// Put back a single value to the front of the iterator.
+    ///
+    /// If a value is already in the put back slot, it is returned.
+    #[inline]
+    pub fn put_back(&mut self, x: I::Item) -> Option<I::Item> {
+        self.top.replace(x)
+    }
+}
+
+impl<I> Iterator for PutBack<I>
+where
+    I: Iterator,
+{
+    type Item = I::Item;
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        match self.top {
+            None => self.iter.next(),
+            ref mut some => some.take(),
+        }
+    }
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        // Not ExactSizeIterator because size may be larger than usize
+        size_hint::add_scalar(self.iter.size_hint(), self.top.is_some() as usize)
+    }
+
+    fn count(self) -> usize {
+        self.iter.count() + (self.top.is_some() as usize)
+    }
+
+    fn last(self) -> Option<Self::Item> {
+        self.iter.last().or(self.top)
+    }
+
+    fn nth(&mut self, n: usize) -> Option<Self::Item> {
+        match self.top {
+            None => self.iter.nth(n),
+            ref mut some => {
+                if n == 0 {
+                    some.take()
+                } else {
+                    *some = None;
+                    self.iter.nth(n - 1)
+                }
+            }
+        }
+    }
+
+    fn all<G>(&mut self, mut f: G) -> bool
+    where
+        G: FnMut(Self::Item) -> bool,
+    {
+        if let Some(elt) = self.top.take() {
+            if !f(elt) {
+                return false;
+            }
+        }
+        self.iter.all(f)
+    }
+
+    fn fold<Acc, G>(mut self, init: Acc, mut f: G) -> Acc
+    where
+        G: FnMut(Acc, Self::Item) -> Acc,
+    {
+        let mut accum = init;
+        if let Some(elt) = self.top.take() {
+            accum = f(accum, elt);
+        }
+        self.iter.fold(accum, f)
+    }
+}
+
+#[derive(Debug, Clone)]
+/// An iterator adaptor that iterates over the cartesian product of
+/// the element sets of two iterators `I` and `J`.
+///
+/// Iterator element type is `(I::Item, J::Item)`.
+///
+/// See [`.cartesian_product()`](crate::Itertools::cartesian_product) for more
+/// information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct Product<I, J>
+where
+    I: Iterator,
+{
+    a: I,
+    /// `a_cur` is `None` while no item have been taken out of `a` (at
+    /// definition). Then `a_cur` will be `Some(Some(item))` until `a` is
+    /// exhausted, in which case `a_cur` will be `Some(None)`.
+    a_cur: Option<Option<I::Item>>,
+    b: J,
+    b_orig: J,
+}
+
+/// Create a new cartesian product iterator
+///
+/// Iterator element type is `(I::Item, J::Item)`.
+pub fn cartesian_product<I, J>(i: I, j: J) -> Product<I, J>
+where
+    I: Iterator,
+    J: Clone + Iterator,
+    I::Item: Clone,
+{
+    Product { a_cur: None, a: i, b: j.clone(), b_orig: j }
+}
+
+impl<I, J> Iterator for Product<I, J>
+where
+    I: Iterator,
+    J: Clone + Iterator,
+    I::Item: Clone,
+{
+    type Item = (I::Item, J::Item);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let Self { a, a_cur, b, b_orig } = self;
+        let elt_b = match b.next() {
+            None => {
+                *b = b_orig.clone();
+                match b.next() {
+                    None => return None,
+                    Some(x) => {
+                        *a_cur = Some(a.next());
+                        x
+                    }
+                }
+            }
+            Some(x) => x,
+        };
+        a_cur.get_or_insert_with(|| a.next()).as_ref().map(|a| (a.clone(), elt_b))
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        // Not ExactSizeIterator because size may be larger than usize
+        // Compute a * b_orig + b for both lower and upper bound
+        let mut sh = size_hint::mul(self.a.size_hint(), self.b_orig.size_hint());
+        if matches!(self.a_cur, Some(Some(_))) {
+            sh = size_hint::add(sh, self.b.size_hint());
+        }
+        sh
+    }
+
+    fn fold<Acc, G>(self, mut accum: Acc, mut f: G) -> Acc
+    where
+        G: FnMut(Acc, Self::Item) -> Acc,
+    {
+        // use a split loop to handle the loose a_cur as well as avoiding to
+        // clone b_orig at the end.
+        let Self { mut a, a_cur, mut b, b_orig } = self;
+        if let Some(mut elt_a) = a_cur.unwrap_or_else(|| a.next()) {
+            loop {
+                accum = b.fold(accum, |acc, elt| f(acc, (elt_a.clone(), elt)));
+
+                // we can only continue iterating a if we had a first element;
+                if let Some(next_elt_a) = a.next() {
+                    b = b_orig.clone();
+                    elt_a = next_elt_a;
+                } else {
+                    break;
+                }
+            }
+        }
+        accum
+    }
+}
+
+impl<I, J> FusedIterator for Product<I, J>
+where
+    I: FusedIterator,
+    J: Clone + FusedIterator,
+    I::Item: Clone,
+{
+}
+
+/// A “meta iterator adaptor”. Its closure receives a reference to the iterator
+/// and may pick off as many elements as it likes, to produce the next iterator
+/// element.
+///
+/// Iterator element type is `X` if the return type of `F` is `Option<X>`.
+///
+/// See [`.batching()`](crate::Itertools::batching) for more information.
+#[derive(Clone)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct Batching<I, F> {
+    f: F,
+    iter: I,
+}
+
+impl<I, F> fmt::Debug for Batching<I, F>
+where
+    I: fmt::Debug,
+{
+    debug_fmt_fields!(Batching, iter);
+}
+
+/// Create a new Batching iterator.
+pub fn batching<I, F>(iter: I, f: F) -> Batching<I, F> {
+    Batching { f, iter }
+}
+
+impl<B, F, I> Iterator for Batching<I, F>
+where
+    I: Iterator,
+    F: FnMut(&mut I) -> Option<B>,
+{
+    type Item = B;
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        (self.f)(&mut self.iter)
+    }
+}
+
+/// An iterator adaptor that borrows from a `Clone`-able iterator
+/// to only pick off elements while the predicate returns `true`.
+///
+/// See [`.take_while_ref()`](crate::Itertools::take_while_ref) for more
+/// information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct TakeWhileRef<'a, I: 'a, F> {
+    iter: &'a mut I,
+    f: F,
+}
+
+impl<I, F> fmt::Debug for TakeWhileRef<'_, I, F>
+where
+    I: Iterator + fmt::Debug,
+{
+    debug_fmt_fields!(TakeWhileRef, iter);
+}
+
+/// Create a new `TakeWhileRef` from a reference to clonable iterator.
+pub fn take_while_ref<I, F>(iter: &mut I, f: F) -> TakeWhileRef<I, F>
+where
+    I: Iterator + Clone,
+{
+    TakeWhileRef { iter, f }
+}
+
+impl<I, F> Iterator for TakeWhileRef<'_, I, F>
+where
+    I: Iterator + Clone,
+    F: FnMut(&I::Item) -> bool,
+{
+    type Item = I::Item;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let old = self.iter.clone();
+        match self.iter.next() {
+            None => None,
+            Some(elt) => {
+                if (self.f)(&elt) {
+                    Some(elt)
+                } else {
+                    *self.iter = old;
+                    None
+                }
+            }
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (0, self.iter.size_hint().1)
+    }
+}
+
+/// An iterator adaptor that filters `Option<A>` iterator elements
+/// and produces `A`. Stops on the first `None` encountered.
+///
+/// See [`.while_some()`](crate::Itertools::while_some) for more information.
+#[derive(Clone, Debug)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct WhileSome<I> {
+    iter: I,
+}
+
+/// Create a new `WhileSome<I>`.
+pub fn while_some<I>(iter: I) -> WhileSome<I> {
+    WhileSome { iter }
+}
+
+impl<I, A> Iterator for WhileSome<I>
+where
+    I: Iterator<Item = Option<A>>,
+{
+    type Item = A;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match self.iter.next() {
+            None | Some(None) => None,
+            Some(elt) => elt,
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (0, self.iter.size_hint().1)
+    }
+
+    fn fold<B, F>(mut self, acc: B, mut f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let res = self.iter.try_fold(acc, |acc, item| match item {
+            Some(item) => Ok(f(acc, item)),
+            None => Err(acc),
+        });
+
+        match res {
+            Ok(val) => val,
+            Err(val) => val,
+        }
+    }
+}
+
+/// An iterator to iterate through all combinations in a `Clone`-able iterator
+/// that produces tuples of a specific size.
+///
+/// See [`.tuple_combinations()`](crate::Itertools::tuple_combinations) for more
+/// information.
+#[derive(Clone, Debug)]
+#[must_use = "this iterator adaptor is not lazy but does nearly nothing unless consumed"]
+pub struct TupleCombinations<I, T>
+where
+    I: Iterator,
+    T: HasCombination<I>,
+{
+    iter: T::Combination,
+    _mi: PhantomData<I>,
+}
+
+pub trait HasCombination<I>: Sized {
+    type Combination: From<I> + Iterator<Item = Self>;
+}
+
+/// Create a new `TupleCombinations` from a clonable iterator.
+pub fn tuple_combinations<T, I>(iter: I) -> TupleCombinations<I, T>
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
+    T: HasCombination<I>,
+{
+    TupleCombinations { iter: T::Combination::from(iter), _mi: PhantomData }
+}
+
+impl<I, T> Iterator for TupleCombinations<I, T>
+where
+    I: Iterator,
+    T: HasCombination<I>,
+{
+    type Item = T;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.iter.next()
+    }
+
+    fn size_hint(&self) -> SizeHint {
+        self.iter.size_hint()
+    }
+
+    fn count(self) -> usize {
+        self.iter.count()
+    }
+
+    fn fold<B, F>(self, init: B, f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        self.iter.fold(init, f)
+    }
+}
+
+impl<I, T> FusedIterator for TupleCombinations<I, T>
+where
+    I: FusedIterator,
+    T: HasCombination<I>,
+{
+}
+
+#[derive(Clone, Debug)]
+pub struct Tuple1Combination<I> {
+    iter: I,
+}
+
+impl<I> From<I> for Tuple1Combination<I> {
+    fn from(iter: I) -> Self {
+        Self { iter }
+    }
+}
+
+impl<I: Iterator> Iterator for Tuple1Combination<I> {
+    type Item = (I::Item,);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.iter.next().map(|x| (x,))
+    }
+
+    fn size_hint(&self) -> SizeHint {
+        self.iter.size_hint()
+    }
+
+    fn count(self) -> usize {
+        self.iter.count()
+    }
+
+    fn fold<B, F>(self, init: B, f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        self.iter.map(|x| (x,)).fold(init, f)
+    }
+}
+
+impl<I: Iterator> HasCombination<I> for (I::Item,) {
+    type Combination = Tuple1Combination<I>;
+}
+
+macro_rules! impl_tuple_combination {
+    ($C:ident $P:ident ; $($X:ident)*) => (
+        #[derive(Clone, Debug)]
+        pub struct $C<I: Iterator> {
+            item: Option<I::Item>,
+            iter: I,
+            c: $P<I>,
+        }
+
+        impl<I: Iterator + Clone> From<I> for $C<I> {
+            fn from(mut iter: I) -> Self {
+                Self {
+                    item: iter.next(),
+                    iter: iter.clone(),
+                    c: iter.into(),
+                }
+            }
+        }
+
+        impl<I: Iterator + Clone> From<I> for $C<Fuse<I>> {
+            fn from(iter: I) -> Self {
+                Self::from(iter.fuse())
+            }
+        }
+
+        impl<I, A> Iterator for $C<I>
+            where I: Iterator<Item = A> + Clone,
+                  A: Clone,
+        {
+            type Item = (A, $(ignore_ident!($X, A)),*);
+
+            fn next(&mut self) -> Option<Self::Item> {
+                if let Some(($($X,)*)) = self.c.next() {
+                    let z = self.item.clone().unwrap();
+                    Some((z, $($X),*))
+                } else {
+                    self.item = self.iter.next();
+                    self.item.clone().and_then(|z| {
+                        self.c = self.iter.clone().into();
+                        self.c.next().map(|($($X,)*)| (z, $($X),*))
+                    })
+                }
+            }
+
+            fn size_hint(&self) -> SizeHint {
+                const K: usize = 1 + count_ident!($($X)*);
+                let (mut n_min, mut n_max) = self.iter.size_hint();
+                n_min = checked_binomial(n_min, K).unwrap_or(usize::MAX);
+                n_max = n_max.and_then(|n| checked_binomial(n, K));
+                size_hint::add(self.c.size_hint(), (n_min, n_max))
+            }
+
+            fn count(self) -> usize {
+                const K: usize = 1 + count_ident!($($X)*);
+                let n = self.iter.count();
+                checked_binomial(n, K).unwrap() + self.c.count()
+            }
+
+            fn fold<B, F>(self, mut init: B, mut f: F) -> B
+            where
+                F: FnMut(B, Self::Item) -> B,
+            {
+                // We outline this closure to prevent it from unnecessarily
+                // capturing the type parameters `I`, `B`, and `F`. Not doing
+                // so ended up causing exponentially big types during MIR
+                // inlining when building itertools with optimizations enabled.
+                //
+                // This change causes a small improvement to compile times in
+                // release mode.
+                type CurrTuple<A> = (A, $(ignore_ident!($X, A)),*);
+                type PrevTuple<A> = ($(ignore_ident!($X, A),)*);
+                fn map_fn<A: Clone>(z: &A) -> impl FnMut(PrevTuple<A>) -> CurrTuple<A> + '_ {
+                    move |($($X,)*)| (z.clone(), $($X),*)
+                }
+                let Self { c, item, mut iter } = self;
+                if let Some(z) = item.as_ref() {
+                    init = c
+                        .map(map_fn::<A>(z))
+                        .fold(init, &mut f);
+                }
+                while let Some(z) = iter.next() {
+                    let c: $P<I> = iter.clone().into();
+                    init = c
+                        .map(map_fn::<A>(&z))
+                        .fold(init, &mut f);
+                }
+                init
+            }
+        }
+
+        impl<I, A> HasCombination<I> for (A, $(ignore_ident!($X, A)),*)
+            where I: Iterator<Item = A> + Clone,
+                  I::Item: Clone
+        {
+            type Combination = $C<Fuse<I>>;
+        }
+    )
+}
+
+// This snippet generates the twelve `impl_tuple_combination!` invocations:
+//    use core::iter;
+//    use itertools::Itertools;
+//
+//    for i in 2..=12 {
+//        println!("impl_tuple_combination!(Tuple{arity}Combination
+// Tuple{prev}Combination; {idents});",            arity = i,
+//            prev = i - 1,
+//            idents = ('a'..'z').take(i - 1).join(" "),
+//        );
+//    }
+// It could probably be replaced by a bit more macro cleverness.
+impl_tuple_combination!(Tuple2Combination Tuple1Combination; a);
+impl_tuple_combination!(Tuple3Combination Tuple2Combination; a b);
+impl_tuple_combination!(Tuple4Combination Tuple3Combination; a b c);
+impl_tuple_combination!(Tuple5Combination Tuple4Combination; a b c d);
+impl_tuple_combination!(Tuple6Combination Tuple5Combination; a b c d e);
+impl_tuple_combination!(Tuple7Combination Tuple6Combination; a b c d e f);
+impl_tuple_combination!(Tuple8Combination Tuple7Combination; a b c d e f g);
+impl_tuple_combination!(Tuple9Combination Tuple8Combination; a b c d e f g h);
+impl_tuple_combination!(Tuple10Combination Tuple9Combination; a b c d e f g h i);
+impl_tuple_combination!(Tuple11Combination Tuple10Combination; a b c d e f g h i j);
+impl_tuple_combination!(Tuple12Combination Tuple11Combination; a b c d e f g h i j k);
+
+// https://en.wikipedia.org/wiki/Binomial_coefficient#In_programming_languages
+pub(crate) fn checked_binomial(mut n: usize, mut k: usize) -> Option<usize> {
+    if n < k {
+        return Some(0);
+    }
+    // `factorial(n) / factorial(n - k) / factorial(k)` but trying to avoid it
+    // overflows:
+    k = (n - k).min(k); // symmetry
+    let mut c = 1;
+    for i in 1..=k {
+        c = (c / i).checked_mul(n)?.checked_add((c % i).checked_mul(n)? / i)?;
+        n -= 1;
+    }
+    Some(c)
+}
+
+#[test]
+fn test_checked_binomial() {
+    // With the first row: [1, 0, 0, ...] and the first column full of 1s, we check
+    // row by row the recurrence relation of binomials (which is an equivalent
+    // definition). For n >= 1 and k >= 1 we have:
+    //   binomial(n, k) == binomial(n - 1, k - 1) + binomial(n - 1, k)
+    const LIMIT: usize = 500;
+    let mut row = vec![Some(0); LIMIT + 1];
+    row[0] = Some(1);
+    for n in 0..=LIMIT {
+        for k in 0..=LIMIT {
+            assert_eq!(row[k], checked_binomial(n, k));
+        }
+        row = std::iter::once(Some(1))
+            .chain((1..=LIMIT).map(|k| row[k - 1]?.checked_add(row[k]?)))
+            .collect();
+    }
+}
+
+/// An iterator adapter to filter values within a nested `Result::Ok`.
+///
+/// See [`.filter_ok()`](crate::Itertools::filter_ok) for more information.
+#[derive(Clone)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct FilterOk<I, F> {
+    iter: I,
+    f: F,
+}
+
+impl<I, F> fmt::Debug for FilterOk<I, F>
+where
+    I: fmt::Debug,
+{
+    debug_fmt_fields!(FilterOk, iter);
+}
+
+/// Create a new `FilterOk` iterator.
+pub fn filter_ok<I, F, T, E>(iter: I, f: F) -> FilterOk<I, F>
+where
+    I: Iterator<Item = Result<T, E>>,
+    F: FnMut(&T) -> bool,
+{
+    FilterOk { iter, f }
+}
+
+impl<I, F, T, E> Iterator for FilterOk<I, F>
+where
+    I: Iterator<Item = Result<T, E>>,
+    F: FnMut(&T) -> bool,
+{
+    type Item = Result<T, E>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let f = &mut self.f;
+        self.iter.find(|res| match res {
+            Ok(t) => f(t),
+            _ => true,
+        })
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (0, self.iter.size_hint().1)
+    }
+
+    fn fold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc
+    where
+        Fold: FnMut(Acc, Self::Item) -> Acc,
+    {
+        let mut f = self.f;
+        self.iter.filter(|v| v.as_ref().map(&mut f).unwrap_or(true)).fold(init, fold_f)
+    }
+
+    fn collect<C>(self) -> C
+    where
+        C: FromIterator<Self::Item>,
+    {
+        let mut f = self.f;
+        self.iter.filter(|v| v.as_ref().map(&mut f).unwrap_or(true)).collect()
+    }
+}
+
+impl<I, F, T, E> DoubleEndedIterator for FilterOk<I, F>
+where
+    I: DoubleEndedIterator<Item = Result<T, E>>,
+    F: FnMut(&T) -> bool,
+{
+    fn next_back(&mut self) -> Option<Self::Item> {
+        let f = &mut self.f;
+        self.iter.rfind(|res| match res {
+            Ok(t) => f(t),
+            _ => true,
+        })
+    }
+
+    fn rfold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc
+    where
+        Fold: FnMut(Acc, Self::Item) -> Acc,
+    {
+        let mut f = self.f;
+        self.iter.filter(|v| v.as_ref().map(&mut f).unwrap_or(true)).rfold(init, fold_f)
+    }
+}
+
+impl<I, F, T, E> FusedIterator for FilterOk<I, F>
+where
+    I: FusedIterator<Item = Result<T, E>>,
+    F: FnMut(&T) -> bool,
+{
+}
+
+/// An iterator adapter to filter and apply a transformation on values within a
+/// nested `Result::Ok`.
+///
+/// See [`.filter_map_ok()`](crate::Itertools::filter_map_ok) for more
+/// information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[derive(Clone)]
+pub struct FilterMapOk<I, F> {
+    iter: I,
+    f: F,
+}
+
+impl<I, F> fmt::Debug for FilterMapOk<I, F>
+where
+    I: fmt::Debug,
+{
+    debug_fmt_fields!(FilterMapOk, iter);
+}
+
+fn transpose_result<T, E>(result: Result<Option<T>, E>) -> Option<Result<T, E>> {
+    match result {
+        Ok(Some(v)) => Some(Ok(v)),
+        Ok(None) => None,
+        Err(e) => Some(Err(e)),
+    }
+}
+
+/// Create a new `FilterOk` iterator.
+pub fn filter_map_ok<I, F, T, U, E>(iter: I, f: F) -> FilterMapOk<I, F>
+where
+    I: Iterator<Item = Result<T, E>>,
+    F: FnMut(T) -> Option<U>,
+{
+    FilterMapOk { iter, f }
+}
+
+impl<I, F, T, U, E> Iterator for FilterMapOk<I, F>
+where
+    I: Iterator<Item = Result<T, E>>,
+    F: FnMut(T) -> Option<U>,
+{
+    type Item = Result<U, E>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let f = &mut self.f;
+        self.iter.find_map(|res| match res {
+            Ok(t) => f(t).map(Ok),
+            Err(e) => Some(Err(e)),
+        })
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (0, self.iter.size_hint().1)
+    }
+
+    fn fold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc
+    where
+        Fold: FnMut(Acc, Self::Item) -> Acc,
+    {
+        let mut f = self.f;
+        self.iter.filter_map(|v| transpose_result(v.map(&mut f))).fold(init, fold_f)
+    }
+
+    fn collect<C>(self) -> C
+    where
+        C: FromIterator<Self::Item>,
+    {
+        let mut f = self.f;
+        self.iter.filter_map(|v| transpose_result(v.map(&mut f))).collect()
+    }
+}
+
+impl<I, F, T, U, E> DoubleEndedIterator for FilterMapOk<I, F>
+where
+    I: DoubleEndedIterator<Item = Result<T, E>>,
+    F: FnMut(T) -> Option<U>,
+{
+    fn next_back(&mut self) -> Option<Self::Item> {
+        let f = &mut self.f;
+        self.iter.by_ref().rev().find_map(|res| match res {
+            Ok(t) => f(t).map(Ok),
+            Err(e) => Some(Err(e)),
+        })
+    }
+
+    fn rfold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc
+    where
+        Fold: FnMut(Acc, Self::Item) -> Acc,
+    {
+        let mut f = self.f;
+        self.iter.filter_map(|v| transpose_result(v.map(&mut f))).rfold(init, fold_f)
+    }
+}
+
+impl<I, F, T, U, E> FusedIterator for FilterMapOk<I, F>
+where
+    I: FusedIterator<Item = Result<T, E>>,
+    F: FnMut(T) -> Option<U>,
+{
+}
+
+/// An iterator adapter to get the positions of each element that matches a
+/// predicate.
+///
+/// See [`.positions()`](crate::Itertools::positions) for more information.
+#[derive(Clone)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct Positions<I, F> {
+    iter: Enumerate<I>,
+    f: F,
+}
+
+impl<I, F> fmt::Debug for Positions<I, F>
+where
+    I: fmt::Debug,
+{
+    debug_fmt_fields!(Positions, iter);
+}
+
+/// Create a new `Positions` iterator.
+pub fn positions<I, F>(iter: I, f: F) -> Positions<I, F>
+where
+    I: Iterator,
+    F: FnMut(I::Item) -> bool,
+{
+    let iter = iter.enumerate();
+    Positions { iter, f }
+}
+
+impl<I, F> Iterator for Positions<I, F>
+where
+    I: Iterator,
+    F: FnMut(I::Item) -> bool,
+{
+    type Item = usize;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let f = &mut self.f;
+        self.iter.find_map(|(count, val)| f(val).then_some(count))
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (0, self.iter.size_hint().1)
+    }
+
+    fn fold<B, G>(self, init: B, mut func: G) -> B
+    where
+        G: FnMut(B, Self::Item) -> B,
+    {
+        let mut f = self.f;
+        self.iter.fold(init, |mut acc, (count, val)| {
+            if f(val) {
+                acc = func(acc, count);
+            }
+            acc
+        })
+    }
+}
+
+impl<I, F> DoubleEndedIterator for Positions<I, F>
+where
+    I: DoubleEndedIterator + ExactSizeIterator,
+    F: FnMut(I::Item) -> bool,
+{
+    fn next_back(&mut self) -> Option<Self::Item> {
+        let f = &mut self.f;
+        self.iter.by_ref().rev().find_map(|(count, val)| f(val).then_some(count))
+    }
+
+    fn rfold<B, G>(self, init: B, mut func: G) -> B
+    where
+        G: FnMut(B, Self::Item) -> B,
+    {
+        let mut f = self.f;
+        self.iter.rfold(init, |mut acc, (count, val)| {
+            if f(val) {
+                acc = func(acc, count);
+            }
+            acc
+        })
+    }
+}
+
+impl<I, F> FusedIterator for Positions<I, F>
+where
+    I: FusedIterator,
+    F: FnMut(I::Item) -> bool,
+{
+}
+
+/// An iterator adapter to apply a mutating function to each element before
+/// yielding it.
+///
+/// See [`.update()`](crate::Itertools::update) for more information.
+#[derive(Clone)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct Update<I, F> {
+    iter: I,
+    f: F,
+}
+
+impl<I, F> fmt::Debug for Update<I, F>
+where
+    I: fmt::Debug,
+{
+    debug_fmt_fields!(Update, iter);
+}
+
+/// Create a new `Update` iterator.
+pub fn update<I, F>(iter: I, f: F) -> Update<I, F>
+where
+    I: Iterator,
+    F: FnMut(&mut I::Item),
+{
+    Update { iter, f }
+}
+
+impl<I, F> Iterator for Update<I, F>
+where
+    I: Iterator,
+    F: FnMut(&mut I::Item),
+{
+    type Item = I::Item;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if let Some(mut v) = self.iter.next() {
+            (self.f)(&mut v);
+            Some(v)
+        } else {
+            None
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.iter.size_hint()
+    }
+
+    fn fold<Acc, G>(self, init: Acc, mut g: G) -> Acc
+    where
+        G: FnMut(Acc, Self::Item) -> Acc,
+    {
+        let mut f = self.f;
+        self.iter.fold(init, move |acc, mut v| {
+            f(&mut v);
+            g(acc, v)
+        })
+    }
+
+    // if possible, re-use inner iterator specializations in collect
+    fn collect<C>(self) -> C
+    where
+        C: FromIterator<Self::Item>,
+    {
+        let mut f = self.f;
+        self.iter
+            .map(move |mut v| {
+                f(&mut v);
+                v
+            })
+            .collect()
+    }
+}
+
+impl<I, F> ExactSizeIterator for Update<I, F>
+where
+    I: ExactSizeIterator,
+    F: FnMut(&mut I::Item),
+{
+}
+
+impl<I, F> DoubleEndedIterator for Update<I, F>
+where
+    I: DoubleEndedIterator,
+    F: FnMut(&mut I::Item),
+{
+    fn next_back(&mut self) -> Option<Self::Item> {
+        if let Some(mut v) = self.iter.next_back() {
+            (self.f)(&mut v);
+            Some(v)
+        } else {
+            None
+        }
+    }
+}
+
+impl<I, F> FusedIterator for Update<I, F>
+where
+    I: FusedIterator,
+    F: FnMut(&mut I::Item),
+{
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/multi_product.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/multi_product.rs
new file mode 100644
index 0000000..7d65a33
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/multi_product.rs
@@ -0,0 +1,212 @@
+#![cfg(feature = "use_alloc")]
+use Option::{self as State, None as ProductEnded, Some as ProductInProgress};
+use Option::{self as CurrentItems, None as NotYetPopulated, Some as Populated};
+
+use alloc::vec::Vec;
+
+use crate::size_hint;
+
+#[derive(Clone)]
+/// An iterator adaptor that iterates over the cartesian product of
+/// multiple iterators of type `I`.
+///
+/// An iterator element type is `Vec<I::Item>`.
+///
+/// See [`.multi_cartesian_product()`](crate::Itertools::multi_cartesian_product)
+/// for more information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct MultiProduct<I>(State<MultiProductInner<I>>)
+where
+    I: Iterator + Clone,
+    I::Item: Clone;
+
+#[derive(Clone)]
+/// Internals for `MultiProduct`.
+struct MultiProductInner<I>
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
+{
+    /// Holds the iterators.
+    iters: Vec<MultiProductIter<I>>,
+    /// Not populated at the beginning then it holds the current item of each
+    /// iterator.
+    cur: CurrentItems<Vec<I::Item>>,
+}
+
+impl<I> std::fmt::Debug for MultiProduct<I>
+where
+    I: Iterator + Clone + std::fmt::Debug,
+    I::Item: Clone + std::fmt::Debug,
+{
+    debug_fmt_fields!(MultiProduct, 0);
+}
+
+impl<I> std::fmt::Debug for MultiProductInner<I>
+where
+    I: Iterator + Clone + std::fmt::Debug,
+    I::Item: Clone + std::fmt::Debug,
+{
+    debug_fmt_fields!(MultiProductInner, iters, cur);
+}
+
+/// Create a new cartesian product iterator over an arbitrary number
+/// of iterators of the same type.
+///
+/// Iterator element is of type `Vec<H::Item::Item>`.
+pub fn multi_cartesian_product<H>(iters: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter>
+where
+    H: Iterator,
+    H::Item: IntoIterator,
+    <H::Item as IntoIterator>::IntoIter: Clone,
+    <H::Item as IntoIterator>::Item: Clone,
+{
+    let inner = MultiProductInner {
+        iters: iters.map(|i| MultiProductIter::new(i.into_iter())).collect(),
+        cur: NotYetPopulated,
+    };
+    MultiProduct(ProductInProgress(inner))
+}
+
+#[derive(Clone, Debug)]
+/// Holds the state of a single iterator within a `MultiProduct`.
+struct MultiProductIter<I>
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
+{
+    iter: I,
+    iter_orig: I,
+}
+
+impl<I> MultiProductIter<I>
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
+{
+    fn new(iter: I) -> Self {
+        Self { iter: iter.clone(), iter_orig: iter }
+    }
+}
+
+impl<I> Iterator for MultiProduct<I>
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
+{
+    type Item = Vec<I::Item>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        // This fuses the iterator.
+        let inner = self.0.as_mut()?;
+        match &mut inner.cur {
+            Populated(values) => {
+                debug_assert!(!inner.iters.is_empty());
+                // Find (from the right) a non-finished iterator and
+                // reset the finished ones encountered.
+                for (iter, item) in inner.iters.iter_mut().zip(values.iter_mut()).rev() {
+                    if let Some(new) = iter.iter.next() {
+                        *item = new;
+                        return Some(values.clone());
+                    } else {
+                        iter.iter = iter.iter_orig.clone();
+                        // `cur` is populated so the untouched `iter_orig` can not be empty.
+                        *item = iter.iter.next().unwrap();
+                    }
+                }
+                self.0 = ProductEnded;
+                None
+            }
+            // Only the first time.
+            NotYetPopulated => {
+                let next: Option<Vec<_>> = inner.iters.iter_mut().map(|i| i.iter.next()).collect();
+                if next.is_none() || inner.iters.is_empty() {
+                    // This cartesian product had at most one item to generate and now ends.
+                    self.0 = ProductEnded;
+                } else {
+                    inner.cur.clone_from(&next);
+                }
+                next
+            }
+        }
+    }
+
+    fn count(self) -> usize {
+        match self.0 {
+            ProductEnded => 0,
+            // The iterator is fresh so the count is the product of the length of each iterator:
+            // - If one of them is empty, stop counting.
+            // - Less `count()` calls than the general case.
+            ProductInProgress(MultiProductInner { iters, cur: NotYetPopulated }) => iters
+                .into_iter()
+                .map(|iter| iter.iter_orig.count())
+                .try_fold(1, |product, count| if count == 0 { None } else { Some(product * count) })
+                .unwrap_or_default(),
+            // The general case.
+            ProductInProgress(MultiProductInner { iters, cur: Populated(_) }) => {
+                iters.into_iter().fold(0, |mut acc, iter| {
+                    if acc != 0 {
+                        acc *= iter.iter_orig.count();
+                    }
+                    acc + iter.iter.count()
+                })
+            }
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        match &self.0 {
+            ProductEnded => (0, Some(0)),
+            ProductInProgress(MultiProductInner { iters, cur: NotYetPopulated }) => iters
+                .iter()
+                .map(|iter| iter.iter_orig.size_hint())
+                .fold((1, Some(1)), size_hint::mul),
+            ProductInProgress(MultiProductInner { iters, cur: Populated(_) }) => {
+                if let [first, tail @ ..] = &iters[..] {
+                    tail.iter().fold(first.iter.size_hint(), |mut sh, iter| {
+                        sh = size_hint::mul(sh, iter.iter_orig.size_hint());
+                        size_hint::add(sh, iter.iter.size_hint())
+                    })
+                } else {
+                    // Since it is populated, this cartesian product has started so `iters` is not
+                    // empty.
+                    unreachable!()
+                }
+            }
+        }
+    }
+
+    fn last(self) -> Option<Self::Item> {
+        let MultiProductInner { iters, cur } = self.0?;
+        // Collect the last item of each iterator of the product.
+        if let Populated(values) = cur {
+            let mut count = iters.len();
+            let last = iters
+                .into_iter()
+                .zip(values)
+                .map(|(i, value)| {
+                    i.iter.last().unwrap_or_else(|| {
+                        // The iterator is empty, use its current `value`.
+                        count -= 1;
+                        value
+                    })
+                })
+                .collect();
+            if count == 0 {
+                // `values` was the last item.
+                None
+            } else {
+                Some(last)
+            }
+        } else {
+            iters.into_iter().map(|i| i.iter.last()).collect()
+        }
+    }
+}
+
+impl<I> std::iter::FusedIterator for MultiProduct<I>
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
+{
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations.rs
new file mode 100644
index 0000000..8cda4a5
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations.rs
@@ -0,0 +1,305 @@
+use core::array;
+use core::borrow::BorrowMut;
+use std::fmt;
+use std::iter::FusedIterator;
+
+use super::lazy_buffer::LazyBuffer;
+use alloc::vec::Vec;
+
+use crate::adaptors::checked_binomial;
+
+/// Iterator for `Vec` valued combinations returned by
+/// [`.combinations()`](crate::Itertools::combinations)
+pub type Combinations<I> = CombinationsGeneric<I, Vec<usize>>;
+/// Iterator for const generic combinations returned by
+/// [`.array_combinations()`](crate::Itertools::array_combinations)
+pub type ArrayCombinations<I, const K: usize> = CombinationsGeneric<I, [usize; K]>;
+
+/// Create a new `Combinations` from a clonable iterator.
+pub fn combinations<I: Iterator>(iter: I, k: usize) -> Combinations<I>
+where
+    I::Item: Clone,
+{
+    Combinations::new(iter, (0..k).collect())
+}
+
+/// Create a new `ArrayCombinations` from a clonable iterator.
+pub fn array_combinations<I: Iterator, const K: usize>(iter: I) -> ArrayCombinations<I, K>
+where
+    I::Item: Clone,
+{
+    ArrayCombinations::new(iter, array::from_fn(|i| i))
+}
+
+/// An iterator to iterate through all the `k`-length combinations in an
+/// iterator.
+///
+/// See [`.combinations()`](crate::Itertools::combinations) and
+/// [`.array_combinations()`](crate::Itertools::array_combinations) for more
+/// information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct CombinationsGeneric<I: Iterator, Idx> {
+    indices: Idx,
+    pool: LazyBuffer<I>,
+    first: bool,
+}
+
+/// A type holding indices of elements in a pool or buffer of items from an
+/// inner iterator and used to pick out different combinations in a generic way.
+pub trait PoolIndex<T>: BorrowMut<[usize]> {
+    type Item;
+
+    fn extract_item<I: Iterator<Item = T>>(&self, pool: &LazyBuffer<I>) -> Self::Item
+    where
+        T: Clone;
+
+    fn len(&self) -> usize {
+        self.borrow().len()
+    }
+}
+
+impl<T> PoolIndex<T> for Vec<usize> {
+    type Item = Vec<T>;
+
+    fn extract_item<I: Iterator<Item = T>>(&self, pool: &LazyBuffer<I>) -> Vec<T>
+    where
+        T: Clone,
+    {
+        pool.get_at(self)
+    }
+}
+
+impl<T, const K: usize> PoolIndex<T> for [usize; K] {
+    type Item = [T; K];
+
+    fn extract_item<I: Iterator<Item = T>>(&self, pool: &LazyBuffer<I>) -> [T; K]
+    where
+        T: Clone,
+    {
+        pool.get_array(*self)
+    }
+}
+
+impl<I, Idx> Clone for CombinationsGeneric<I, Idx>
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
+    Idx: Clone,
+{
+    clone_fields!(indices, pool, first);
+}
+
+impl<I, Idx> fmt::Debug for CombinationsGeneric<I, Idx>
+where
+    I: Iterator + fmt::Debug,
+    I::Item: fmt::Debug,
+    Idx: fmt::Debug,
+{
+    debug_fmt_fields!(Combinations, indices, pool, first);
+}
+
+impl<I: Iterator, Idx: PoolIndex<I::Item>> CombinationsGeneric<I, Idx> {
+    /// Constructor with arguments the inner iterator and the initial state for
+    /// the indices.
+    fn new(iter: I, indices: Idx) -> Self {
+        Self { indices, pool: LazyBuffer::new(iter), first: true }
+    }
+
+    /// Returns the length of a combination produced by this iterator.
+    #[inline]
+    pub fn k(&self) -> usize {
+        self.indices.len()
+    }
+
+    /// Returns the (current) length of the pool from which combination elements
+    /// are selected. This value can change between invocations of
+    /// [`next`](Combinations::next).
+    #[inline]
+    pub fn n(&self) -> usize {
+        self.pool.len()
+    }
+
+    /// Returns a reference to the source pool.
+    #[inline]
+    pub(crate) fn src(&self) -> &LazyBuffer<I> {
+        &self.pool
+    }
+
+    /// Return the length of the inner iterator and the count of remaining
+    /// combinations.
+    pub(crate) fn n_and_count(self) -> (usize, usize) {
+        let Self { indices, pool, first } = self;
+        let n = pool.count();
+        (n, remaining_for(n, first, indices.borrow()).unwrap())
+    }
+
+    /// Initialises the iterator by filling a buffer with elements from the
+    /// iterator. Returns true if there are no combinations, false otherwise.
+    fn init(&mut self) -> bool {
+        self.pool.prefill(self.k());
+        let done = self.k() > self.n();
+        if !done {
+            self.first = false;
+        }
+
+        done
+    }
+
+    /// Increments indices representing the combination to advance to the next
+    /// (in lexicographic order by increasing sequence) combination. For example
+    /// if we have n=4 & k=2 then `[0, 1] -> [0, 2] -> [0, 3] -> [1, 2] -> ...`
+    ///
+    /// Returns true if we've run out of combinations, false otherwise.
+    fn increment_indices(&mut self) -> bool {
+        // Borrow once instead of noise each time it's indexed
+        let indices = self.indices.borrow_mut();
+
+        if indices.is_empty() {
+            return true; // Done
+        }
+        // Scan from the end, looking for an index to increment
+        let mut i: usize = indices.len() - 1;
+
+        // Check if we need to consume more from the iterator
+        if indices[i] == self.pool.len() - 1 {
+            self.pool.get_next(); // may change pool size
+        }
+
+        while indices[i] == i + self.pool.len() - indices.len() {
+            if i > 0 {
+                i -= 1;
+            } else {
+                // Reached the last combination
+                return true;
+            }
+        }
+
+        // Increment index, and reset the ones to its right
+        indices[i] += 1;
+        for j in i + 1..indices.len() {
+            indices[j] = indices[j - 1] + 1;
+        }
+        // If we've made it this far, we haven't run out of combos
+        false
+    }
+
+    /// Returns the n-th item or the number of successful steps.
+    pub(crate) fn try_nth(&mut self, n: usize) -> Result<<Self as Iterator>::Item, usize>
+    where
+        I: Iterator,
+        I::Item: Clone,
+    {
+        let done = if self.first { self.init() } else { self.increment_indices() };
+        if done {
+            return Err(0);
+        }
+        for i in 0..n {
+            if self.increment_indices() {
+                return Err(i + 1);
+            }
+        }
+        Ok(self.indices.extract_item(&self.pool))
+    }
+}
+
+impl<I, Idx> Iterator for CombinationsGeneric<I, Idx>
+where
+    I: Iterator,
+    I::Item: Clone,
+    Idx: PoolIndex<I::Item>,
+{
+    type Item = Idx::Item;
+    fn next(&mut self) -> Option<Self::Item> {
+        let done = if self.first { self.init() } else { self.increment_indices() };
+
+        if done {
+            return None;
+        }
+
+        Some(self.indices.extract_item(&self.pool))
+    }
+
+    fn nth(&mut self, n: usize) -> Option<Self::Item> {
+        self.try_nth(n).ok()
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let (mut low, mut upp) = self.pool.size_hint();
+        low = remaining_for(low, self.first, self.indices.borrow()).unwrap_or(usize::MAX);
+        upp = upp.and_then(|upp| remaining_for(upp, self.first, self.indices.borrow()));
+        (low, upp)
+    }
+
+    #[inline]
+    fn count(self) -> usize {
+        self.n_and_count().1
+    }
+}
+
+impl<I, Idx> FusedIterator for CombinationsGeneric<I, Idx>
+where
+    I: Iterator,
+    I::Item: Clone,
+    Idx: PoolIndex<I::Item>,
+{
+}
+
+impl<I: Iterator> Combinations<I> {
+    /// Resets this `Combinations` back to an initial state for combinations of
+    /// length `k` over the same pool data source. If `k` is larger than the
+    /// current length of the data pool an attempt is made to prefill the
+    /// pool so that it holds `k` elements.
+    pub(crate) fn reset(&mut self, k: usize) {
+        self.first = true;
+
+        if k < self.indices.len() {
+            self.indices.truncate(k);
+            for i in 0..k {
+                self.indices[i] = i;
+            }
+        } else {
+            for i in 0..self.indices.len() {
+                self.indices[i] = i;
+            }
+            self.indices.extend(self.indices.len()..k);
+            self.pool.prefill(k);
+        }
+    }
+}
+
+/// For a given size `n`, return the count of remaining combinations or None if
+/// it would overflow.
+fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option<usize> {
+    let k = indices.len();
+    if n < k {
+        Some(0)
+    } else if first {
+        checked_binomial(n, k)
+    } else {
+        // https://en.wikipedia.org/wiki/Combinatorial_number_system
+        // http://www.site.uottawa.ca/~lucia/courses/5165-09/GenCombObj.pdf
+
+        // The combinations generated after the current one can be counted by counting
+        // as follows:
+        // - The subsequent combinations that differ in indices[0]: If subsequent
+        //   combinations differ in indices[0], then their value for indices[0] must be
+        //   at least 1 greater than the current indices[0]. As indices is strictly
+        //   monotonically sorted, this means we can effectively choose k values from (n
+        //   - 1 - indices[0]), leading to binomial(n - 1 - indices[0], k)
+        //   possibilities.
+        // - The subsequent combinations with same indices[0], but differing indices[1]:
+        //   Here we can choose k - 1 values from (n - 1 - indices[1]) values, leading
+        //   to binomial(n - 1 - indices[1], k - 1) possibilities.
+        // - (...)
+        // - The subsequent combinations with same indices[0..=i], but differing
+        //   indices[i]: Here we can choose k - i values from (n - 1 - indices[i])
+        //   values: binomial(n - 1 - indices[i], k - i). Since subsequent combinations
+        //   can in any index, we must sum up the aforementioned binomial coefficients.
+
+        // Below, `n0` resembles indices[i].
+        indices
+            .iter()
+            .enumerate()
+            .try_fold(0usize, |sum, (i, n0)| sum.checked_add(checked_binomial(n - 1 - *n0, k - i)?))
+    }
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations_with_replacement.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations_with_replacement.rs
new file mode 100644
index 0000000..21ef322e
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations_with_replacement.rs
@@ -0,0 +1,184 @@
+use alloc::boxed::Box;
+use alloc::vec::Vec;
+use std::fmt;
+use std::iter::FusedIterator;
+
+use super::lazy_buffer::LazyBuffer;
+use crate::adaptors::checked_binomial;
+
+/// An iterator to iterate through all the `n`-length combinations in an
+/// iterator, with replacement.
+///
+/// See [`.combinations_with_replacement()`](crate::Itertools::combinations_with_replacement)
+/// for more information.
+#[derive(Clone)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct CombinationsWithReplacement<I>
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+    indices: Box<[usize]>,
+    pool: LazyBuffer<I>,
+    first: bool,
+}
+
+impl<I> fmt::Debug for CombinationsWithReplacement<I>
+where
+    I: Iterator + fmt::Debug,
+    I::Item: fmt::Debug + Clone,
+{
+    debug_fmt_fields!(CombinationsWithReplacement, indices, pool, first);
+}
+
+/// Create a new `CombinationsWithReplacement` from a clonable iterator.
+pub fn combinations_with_replacement<I>(iter: I, k: usize) -> CombinationsWithReplacement<I>
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+    let indices = alloc::vec![0; k].into_boxed_slice();
+    let pool: LazyBuffer<I> = LazyBuffer::new(iter);
+
+    CombinationsWithReplacement { indices, pool, first: true }
+}
+
+impl<I> CombinationsWithReplacement<I>
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+    /// Increments indices representing the combination to advance to the next
+    /// (in lexicographic order by increasing sequence) combination.
+    ///
+    /// Returns true if we've run out of combinations, false otherwise.
+    fn increment_indices(&mut self) -> bool {
+        // Check if we need to consume more from the iterator
+        // This will run while we increment our first index digit
+        self.pool.get_next();
+
+        // Work out where we need to update our indices
+        let mut increment = None;
+        for (i, indices_int) in self.indices.iter().enumerate().rev() {
+            if *indices_int < self.pool.len() - 1 {
+                increment = Some((i, indices_int + 1));
+                break;
+            }
+        }
+        match increment {
+            // If we can update the indices further
+            Some((increment_from, increment_value)) => {
+                // We need to update the rightmost non-max value
+                // and all those to the right
+                self.indices[increment_from..].fill(increment_value);
+                false
+            }
+            // Otherwise, we're done
+            None => true,
+        }
+    }
+}
+
+impl<I> Iterator for CombinationsWithReplacement<I>
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+    type Item = Vec<I::Item>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.first {
+            // In empty edge cases, stop iterating immediately
+            if !(self.indices.is_empty() || self.pool.get_next()) {
+                return None;
+            }
+            self.first = false;
+        } else if self.increment_indices() {
+            return None;
+        }
+        Some(self.pool.get_at(&self.indices))
+    }
+
+    fn nth(&mut self, n: usize) -> Option<Self::Item> {
+        if self.first {
+            // In empty edge cases, stop iterating immediately
+            if !(self.indices.is_empty() || self.pool.get_next()) {
+                return None;
+            }
+            self.first = false;
+        } else if self.increment_indices() {
+            return None;
+        }
+        for _ in 0..n {
+            if self.increment_indices() {
+                return None;
+            }
+        }
+        Some(self.pool.get_at(&self.indices))
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let (mut low, mut upp) = self.pool.size_hint();
+        low = remaining_for(low, self.first, &self.indices).unwrap_or(usize::MAX);
+        upp = upp.and_then(|upp| remaining_for(upp, self.first, &self.indices));
+        (low, upp)
+    }
+
+    fn count(self) -> usize {
+        let Self { indices, pool, first } = self;
+        let n = pool.count();
+        remaining_for(n, first, &indices).unwrap()
+    }
+}
+
+impl<I> FusedIterator for CombinationsWithReplacement<I>
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+}
+
+/// For a given size `n`, return the count of remaining combinations with
+/// replacement or None if it would overflow.
+fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option<usize> {
+    // With a "stars and bars" representation, choose k values with replacement from
+    // n values is like choosing k out of k + n − 1 positions (hence binomial(k
+    // + n - 1, k) possibilities) to place k stars and therefore n - 1 bars.
+    // Example (n=4, k=6): ***|*||** represents [0,0,0,1,3,3].
+    let count = |n: usize, k: usize| {
+        let positions = if n == 0 { k.saturating_sub(1) } else { (n - 1).checked_add(k)? };
+        checked_binomial(positions, k)
+    };
+    let k = indices.len();
+    if first {
+        count(n, k)
+    } else {
+        // The algorithm is similar to the one for combinations *without replacement*,
+        // except we choose values *with replacement* and indices are *non-strictly*
+        // monotonically sorted.
+
+        // The combinations generated after the current one can be counted by counting
+        // as follows:
+        // - The subsequent combinations that differ in indices[0]: If subsequent
+        //   combinations differ in indices[0], then their value for indices[0] must be
+        //   at least 1 greater than the current indices[0]. As indices is monotonically
+        //   sorted, this means we can effectively choose k values with replacement from
+        //   (n - 1 - indices[0]), leading to count(n - 1 - indices[0], k)
+        //   possibilities.
+        // - The subsequent combinations with same indices[0], but differing indices[1]:
+        //   Here we can choose k - 1 values with replacement from (n - 1 - indices[1])
+        //   values, leading to count(n - 1 - indices[1], k - 1) possibilities.
+        // - (...)
+        // - The subsequent combinations with same indices[0..=i], but differing
+        //   indices[i]: Here we can choose k - i values with replacement from (n - 1 -
+        //   indices[i]) values: count(n - 1 - indices[i], k - i). Since subsequent
+        //   combinations can in any index, we must sum up the aforementioned binomial
+        //   coefficients.
+
+        // Below, `n0` resembles indices[i].
+        indices
+            .iter()
+            .enumerate()
+            .try_fold(0usize, |sum, (i, n0)| sum.checked_add(count(n - 1 - *n0, k - i)?))
+    }
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/concat_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/concat_impl.rs
new file mode 100644
index 0000000..0a281a1
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/concat_impl.rs
@@ -0,0 +1,28 @@
+/// Combine all an iterator's elements into one element by using [`Extend`].
+///
+/// [`IntoIterator`]-enabled version of
+/// [`Itertools::concat`](crate::Itertools::concat).
+///
+/// This combinator will extend the first item with each of the rest of the
+/// items of the iterator. If the iterator is empty, the default value of
+/// `I::Item` is returned.
+///
+/// ```rust
+/// use itertools::concat;
+///
+/// let input = vec![vec![1], vec![2, 3], vec![4, 5, 6]];
+/// assert_eq!(concat(input), vec![1, 2, 3, 4, 5, 6]);
+/// ```
+pub fn concat<I>(iterable: I) -> I::Item
+where
+    I: IntoIterator,
+    I::Item: Extend<<<I as IntoIterator>::Item as IntoIterator>::Item> + IntoIterator + Default,
+{
+    iterable
+        .into_iter()
+        .reduce(|mut a, b| {
+            a.extend(b);
+            a
+        })
+        .unwrap_or_default()
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/cons_tuples_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/cons_tuples_impl.rs
new file mode 100644
index 0000000..854f47b5
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/cons_tuples_impl.rs
@@ -0,0 +1,36 @@
+use crate::adaptors::map::{MapSpecialCase, MapSpecialCaseFn};
+
+macro_rules! impl_cons_iter(
+    ($_A:ident, $_B:ident, ) => (); // stop
+
+    ($A:ident, $($B:ident,)*) => (
+        impl_cons_iter!($($B,)*);
+        #[allow(non_snake_case)]
+        impl<$($B),*, X> MapSpecialCaseFn<(($($B,)*), X)> for ConsTuplesFn {
+            type Out = ($($B,)* X, );
+            fn call(&mut self, (($($B,)*), X): (($($B,)*), X)) -> Self::Out {
+                ($($B,)* X, )
+            }
+        }
+    );
+);
+
+impl_cons_iter!(A, B, C, D, E, F, G, H, I, J, K, L,);
+
+#[derive(Debug, Clone)]
+pub struct ConsTuplesFn;
+
+/// An iterator that maps an iterator of tuples like
+/// `((A, B), C)` to an iterator of `(A, B, C)`.
+///
+/// Used by the `iproduct!()` macro.
+pub type ConsTuples<I> = MapSpecialCase<I, ConsTuplesFn>;
+
+/// Create an iterator that maps for example iterators of
+/// `((A, B), C)` to `(A, B, C)`.
+pub fn cons_tuples<I>(iterable: I) -> ConsTuples<I::IntoIter>
+where
+    I: IntoIterator,
+{
+    ConsTuples { iter: iterable.into_iter(), f: ConsTuplesFn }
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/diff.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/diff.rs
new file mode 100644
index 0000000..aa62a207
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/diff.rs
@@ -0,0 +1,105 @@
+//! "Diff"ing iterators for caching elements to sequential collections without
+//! requiring the new elements' iterator to be `Clone`.
+//!
+//! [`Diff`] (produced by the [`diff_with`] function)
+//! describes the difference between two non-`Clone` iterators `I` and `J` after
+//! breaking ASAP from a lock-step comparison.
+
+use std::fmt;
+
+use crate::free::put_back;
+use crate::structs::PutBack;
+
+/// A type returned by the [`diff_with`] function.
+///
+/// `Diff` represents the way in which the elements yielded by the iterator `I`
+/// differ to some iterator `J`.
+pub enum Diff<I, J>
+where
+    I: Iterator,
+    J: Iterator,
+{
+    /// The index of the first non-matching element along with both iterator's
+    /// remaining elements starting with the first mis-match.
+    FirstMismatch(usize, PutBack<I>, PutBack<J>),
+    /// The total number of elements that were in `J` along with the remaining
+    /// elements of `I`.
+    Shorter(usize, PutBack<I>),
+    /// The total number of elements that were in `I` along with the remaining
+    /// elements of `J`.
+    Longer(usize, PutBack<J>),
+}
+
+impl<I, J> fmt::Debug for Diff<I, J>
+where
+    I: Iterator,
+    J: Iterator,
+    PutBack<I>: fmt::Debug,
+    PutBack<J>: fmt::Debug,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::FirstMismatch(idx, i, j) => {
+                f.debug_tuple("FirstMismatch").field(idx).field(i).field(j).finish()
+            }
+            Self::Shorter(idx, i) => f.debug_tuple("Shorter").field(idx).field(i).finish(),
+            Self::Longer(idx, j) => f.debug_tuple("Longer").field(idx).field(j).finish(),
+        }
+    }
+}
+
+impl<I, J> Clone for Diff<I, J>
+where
+    I: Iterator,
+    J: Iterator,
+    PutBack<I>: Clone,
+    PutBack<J>: Clone,
+{
+    fn clone(&self) -> Self {
+        match self {
+            Self::FirstMismatch(idx, i, j) => Self::FirstMismatch(*idx, i.clone(), j.clone()),
+            Self::Shorter(idx, i) => Self::Shorter(*idx, i.clone()),
+            Self::Longer(idx, j) => Self::Longer(*idx, j.clone()),
+        }
+    }
+}
+
+/// Compares every element yielded by both `i` and `j` with the given function
+/// in lock-step and returns a [`Diff`] which describes how `j` differs from
+/// `i`.
+///
+/// If the number of elements yielded by `j` is less than the number of elements
+/// yielded by `i`, the number of `j` elements yielded will be returned along
+/// with `i`'s remaining elements as `Diff::Shorter`.
+///
+/// If the two elements of a step differ, the index of those elements along with
+/// the remaining elements of both `i` and `j` are returned as
+/// `Diff::FirstMismatch`.
+///
+/// If `i` becomes exhausted before `j` becomes exhausted, the number of
+/// elements in `i` along with the remaining `j` elements will be returned as
+/// `Diff::Longer`.
+pub fn diff_with<I, J, F>(i: I, j: J, mut is_equal: F) -> Option<Diff<I::IntoIter, J::IntoIter>>
+where
+    I: IntoIterator,
+    J: IntoIterator,
+    F: FnMut(&I::Item, &J::Item) -> bool,
+{
+    let mut i = i.into_iter();
+    let mut j = j.into_iter();
+    let mut idx = 0;
+    while let Some(i_elem) = i.next() {
+        match j.next() {
+            None => return Some(Diff::Shorter(idx, put_back(i).with_value(i_elem))),
+            Some(j_elem) => {
+                if !is_equal(&i_elem, &j_elem) {
+                    let remaining_i = put_back(i).with_value(i_elem);
+                    let remaining_j = put_back(j).with_value(j_elem);
+                    return Some(Diff::FirstMismatch(idx, remaining_i, remaining_j));
+                }
+            }
+        }
+        idx += 1;
+    }
+    j.next().map(|j_elem| Diff::Longer(idx, put_back(j).with_value(j_elem)))
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/duplicates_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/duplicates_impl.rs
similarity index 89%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/duplicates_impl.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/duplicates_impl.rs
index 28eda44..eb1347d 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/duplicates_impl.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/duplicates_impl.rs
@@ -2,8 +2,8 @@
 
 mod private {
     use std::collections::HashMap;
-    use std::hash::Hash;
     use std::fmt;
+    use std::hash::Hash;
 
     #[derive(Clone)]
     #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
@@ -22,14 +22,7 @@
 
     impl<I: Iterator, Key: Eq + Hash, F> DuplicatesBy<I, Key, F> {
         pub(crate) fn new(iter: I, key_method: F) -> Self {
-            DuplicatesBy {
-                iter,
-                meta: Meta {
-                    used: HashMap::new(),
-                    pending: 0,
-                    key_method,
-                },
-            }
+            Self { iter, meta: Meta { used: HashMap::new(), pending: 0, key_method } }
         }
     }
 
@@ -44,8 +37,9 @@
     where
         Key: Eq + Hash,
     {
-        /// Takes an item and returns it back to the caller if it's the second time we see it.
-        /// Otherwise the item is consumed and None is returned
+        /// Takes an item and returns it back to the caller if it's the second
+        /// time we see it. Otherwise the item is consumed and None is
+        /// returned
         #[inline(always)]
         fn filter<I>(&mut self, item: I) -> Option<I>
         where
@@ -77,7 +71,7 @@
         type Item = I::Item;
 
         fn next(&mut self) -> Option<Self::Item> {
-            let DuplicatesBy { iter, meta } = self;
+            let Self { iter, meta } = self;
             iter.find_map(|v| meta.filter(v))
         }
 
@@ -109,7 +103,7 @@
         F: KeyMethod<Key, I::Item>,
     {
         fn next_back(&mut self) -> Option<Self::Item> {
-            let DuplicatesBy { iter, meta } = self;
+            let Self { iter, meta } = self;
             iter.rev().find_map(|v| meta.filter(v))
         }
     }
@@ -121,8 +115,9 @@
         fn make(&mut self, value: V) -> Self::Container;
     }
 
-    /// Apply the identity function to elements before checking them for equality.
-    #[derive(Debug)]
+    /// Apply the identity function to elements before checking them for
+    /// equality.
+    #[derive(Debug, Clone)]
     pub struct ById;
     impl<V> KeyMethod<V, V> for ById {
         type Container = JustValue<V>;
@@ -132,7 +127,9 @@
         }
     }
 
-    /// Apply a user-supplied function to elements before checking them for equality.
+    /// Apply a user-supplied function to elements before checking them for
+    /// equality.
+    #[derive(Clone)]
     pub struct ByFn<F>(pub(crate) F);
     impl<F> fmt::Debug for ByFn<F> {
         debug_fmt_fields!(ByFn,);
@@ -148,8 +145,9 @@
         }
     }
 
-    // Implementors of this trait can hold onto a key and a value but only give access to one of them
-    // at a time. This allows the key and the value to be the same value internally
+    // Implementors of this trait can hold onto a key and a value but only give
+    // access to one of them at a time. This allows the key and the value to be
+    // the same value internally
     pub trait KeyXorValue<K, V> {
         fn key_ref(&self) -> &K;
         fn key(self) -> K;
@@ -187,7 +185,8 @@
 
 /// An iterator adapter to filter for duplicate elements.
 ///
-/// See [`.duplicates_by()`](crate::Itertools::duplicates_by) for more information.
+/// See [`.duplicates_by()`](crate::Itertools::duplicates_by) for more
+/// information.
 pub type DuplicatesBy<I, V, F> = private::DuplicatesBy<I, V, private::ByFn<F>>;
 
 /// Create a new `DuplicatesBy` iterator.
@@ -213,4 +212,3 @@
 {
     Duplicates::new(iter, private::ById)
 }
-
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/either_or_both.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/either_or_both.rs
similarity index 79%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/either_or_both.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/either_or_both.rs
index cf65fe7..288bd76 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/either_or_both.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/either_or_both.rs
@@ -6,7 +6,7 @@
 
 /// Value that either holds a single A or B, or both.
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
-pub enum EitherOrBoth<A, B> {
+pub enum EitherOrBoth<A, B = A> {
     /// Both values are present.
     Both(A, B),
     /// Only the left value of type `A` is present.
@@ -29,19 +29,13 @@
     /// If `Left`, return true. Otherwise, return false.
     /// Exclusive version of [`has_left`](EitherOrBoth::has_left).
     pub fn is_left(&self) -> bool {
-        match *self {
-            Left(_) => true,
-            _ => false,
-        }
+        matches!(self, Left(_))
     }
 
     /// If `Right`, return true. Otherwise, return false.
     /// Exclusive version of [`has_right`](EitherOrBoth::has_right).
     pub fn is_right(&self) -> bool {
-        match *self {
-            Right(_) => true,
-            _ => false,
-        }
+        matches!(self, Right(_))
     }
 
     /// If `Both`, return true. Otherwise, return false.
@@ -49,7 +43,8 @@
         self.as_ref().both().is_some()
     }
 
-    /// If `Left`, or `Both`, return `Some` with the left value. Otherwise, return `None`.
+    /// If `Left`, or `Both`, return `Some` with the left value. Otherwise,
+    /// return `None`.
     pub fn left(self) -> Option<A> {
         match self {
             Left(left) | Both(left, _) => Some(left),
@@ -57,7 +52,8 @@
         }
     }
 
-    /// If `Right`, or `Both`, return `Some` with the right value. Otherwise, return `None`.
+    /// If `Right`, or `Both`, return `Some` with the right value. Otherwise,
+    /// return `None`.
     pub fn right(self) -> Option<B> {
         match self {
             Right(right) | Both(_, right) => Some(right),
@@ -65,7 +61,17 @@
         }
     }
 
-    /// If `Left`, return `Some` with the left value. If `Right` or `Both`, return `None`.
+    /// Return tuple of options corresponding to the left and right value
+    /// respectively
+    ///
+    /// If `Left` return `(Some(..), None)`, if `Right` return
+    /// `(None,Some(..))`, else return `(Some(..),Some(..))`
+    pub fn left_and_right(self) -> (Option<A>, Option<B>) {
+        self.map_any(Some, Some).or_default()
+    }
+
+    /// If `Left`, return `Some` with the left value. If `Right` or `Both`,
+    /// return `None`.
     ///
     /// # Examples
     ///
@@ -90,7 +96,8 @@
         }
     }
 
-    /// If `Right`, return `Some` with the right value. If `Left` or `Both`, return `None`.
+    /// If `Right`, return `Some` with the right value. If `Left` or `Both`,
+    /// return `None`.
     ///
     /// # Examples
     ///
@@ -115,7 +122,8 @@
         }
     }
 
-    /// If `Both`, return `Some` containing the left and right values. Otherwise, return `None`.
+    /// If `Both`, return `Some` containing the left and right values.
+    /// Otherwise, return `None`.
     pub fn both(self) -> Option<(A, B)> {
         match self {
             Both(a, b) => Some((a, b)),
@@ -123,7 +131,8 @@
         }
     }
 
-    /// If `Left` or `Both`, return the left value. Otherwise, convert the right value and return it.
+    /// If `Left` or `Both`, return the left value. Otherwise, convert the right
+    /// value and return it.
     pub fn into_left(self) -> A
     where
         B: Into<A>,
@@ -134,7 +143,8 @@
         }
     }
 
-    /// If `Right` or `Both`, return the right value. Otherwise, convert the left value and return it.
+    /// If `Right` or `Both`, return the right value. Otherwise, convert the
+    /// left value and return it.
     pub fn into_right(self) -> B
     where
         A: Into<B>,
@@ -154,7 +164,8 @@
         }
     }
 
-    /// Converts from `&mut EitherOrBoth<A, B>` to `EitherOrBoth<&mut A, &mut B>`.
+    /// Converts from `&mut EitherOrBoth<A, B>` to `EitherOrBoth<&mut A, &mut
+    /// B>`.
     pub fn as_mut(&mut self) -> EitherOrBoth<&mut A, &mut B> {
         match *self {
             Left(ref mut left) => Left(left),
@@ -163,7 +174,8 @@
         }
     }
 
-    /// Converts from `&EitherOrBoth<A, B>` to `EitherOrBoth<&_, &_>` using the [`Deref`] trait.
+    /// Converts from `&EitherOrBoth<A, B>` to `EitherOrBoth<&_, &_>` using the
+    /// [`Deref`] trait.
     pub fn as_deref(&self) -> EitherOrBoth<&A::Target, &B::Target>
     where
         A: Deref,
@@ -176,7 +188,8 @@
         }
     }
 
-    /// Converts from `&mut EitherOrBoth<A, B>` to `EitherOrBoth<&mut _, &mut _>` using the [`DerefMut`] trait.
+    /// Converts from `&mut EitherOrBoth<A, B>` to `EitherOrBoth<&mut _, &mut
+    /// _>` using the [`DerefMut`] trait.
     pub fn as_deref_mut(&mut self) -> EitherOrBoth<&mut A::Target, &mut B::Target>
     where
         A: DerefMut,
@@ -198,8 +211,9 @@
         }
     }
 
-    /// Apply the function `f` on the value `a` in `Left(a)` or `Both(a, b)` variants. If it is
-    /// present rewrapping the result in `self`'s original variant.
+    /// Apply the function `f` on the value `a` in `Left(a)` or `Both(a, b)`
+    /// variants. If it is present rewrapping the result in `self`'s
+    /// original variant.
     pub fn map_left<F, M>(self, f: F) -> EitherOrBoth<M, B>
     where
         F: FnOnce(A) -> M,
@@ -211,8 +225,9 @@
         }
     }
 
-    /// Apply the function `f` on the value `b` in `Right(b)` or `Both(a, b)` variants.
-    /// If it is present rewrapping the result in `self`'s original variant.
+    /// Apply the function `f` on the value `b` in `Right(b)` or `Both(a, b)`
+    /// variants. If it is present rewrapping the result in `self`'s
+    /// original variant.
     pub fn map_right<F, M>(self, f: F) -> EitherOrBoth<A, M>
     where
         F: FnOnce(B) -> M,
@@ -239,8 +254,8 @@
         }
     }
 
-    /// Apply the function `f` on the value `a` in `Left(a)` or `Both(a, _)` variants if it is
-    /// present.
+    /// Apply the function `f` on the value `a` in `Left(a)` or `Both(a, _)`
+    /// variants if it is present.
     pub fn left_and_then<F, L>(self, f: F) -> EitherOrBoth<L, B>
     where
         F: FnOnce(A) -> EitherOrBoth<L, B>,
@@ -263,10 +278,11 @@
         }
     }
 
-    /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present.
-    /// Otherwise, returns the wrapped value for the present element, and the supplied
-    /// value for the other. The first (`l`) argument is used for a missing `Left`
-    /// value. The second (`r`) argument is used for a missing `Right` value.
+    /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if
+    /// present. Otherwise, returns the wrapped value for the present
+    /// element, and the supplied value for the other. The first (`l`)
+    /// argument is used for a missing `Left` value. The second (`r`)
+    /// argument is used for a missing `Right` value.
     ///
     /// Arguments passed to `or` are eagerly evaluated; if you are passing
     /// the result of a function call, it is recommended to use [`or_else`],
@@ -290,25 +306,26 @@
         }
     }
 
-    /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present.
-    /// Otherwise, returns the wrapped value for the present element, and the [`default`](Default::default)
-    /// for the other.
+    /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if
+    /// present. Otherwise, returns the wrapped value for the present
+    /// element, and the [`default`](Default::default) for the other.
     pub fn or_default(self) -> (A, B)
     where
         A: Default,
         B: Default,
     {
         match self {
-            EitherOrBoth::Left(l) => (l, B::default()),
-            EitherOrBoth::Right(r) => (A::default(), r),
-            EitherOrBoth::Both(l, r) => (l, r),
+            Left(l) => (l, B::default()),
+            Right(r) => (A::default(), r),
+            Both(l, r) => (l, r),
         }
     }
 
-    /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if present.
-    /// Otherwise, returns the wrapped value for the present element, and computes the
-    /// missing value with the supplied closure. The first argument (`l`) is used for a
-    /// missing `Left` value. The second argument (`r`) is used for a missing `Right` value.
+    /// Returns a tuple consisting of the `l` and `r` in `Both(l, r)`, if
+    /// present. Otherwise, returns the wrapped value for the present
+    /// element, and computes the missing value with the supplied closure.
+    /// The first argument (`l`) is used for a missing `Left` value. The
+    /// second argument (`r`) is used for a missing `Right` value.
     ///
     /// # Examples
     ///
@@ -327,20 +344,21 @@
         }
     }
 
-    /// Returns a mutable reference to the left value. If the left value is not present,
-    /// it is replaced with `val`.
+    /// Returns a mutable reference to the left value. If the left value is not
+    /// present, it is replaced with `val`.
     pub fn left_or_insert(&mut self, val: A) -> &mut A {
         self.left_or_insert_with(|| val)
     }
 
-    /// Returns a mutable reference to the right value. If the right value is not present,
-    /// it is replaced with `val`.
+    /// Returns a mutable reference to the right value. If the right value is
+    /// not present, it is replaced with `val`.
     pub fn right_or_insert(&mut self, val: B) -> &mut B {
         self.right_or_insert_with(|| val)
     }
 
-    /// If the left value is not present, replace it the value computed by the closure `f`.
-    /// Returns a mutable reference to the now-present left value.
+    /// If the left value is not present, replace it the value computed by the
+    /// closure `f`. Returns a mutable reference to the now-present left
+    /// value.
     pub fn left_or_insert_with<F>(&mut self, f: F) -> &mut A
     where
         F: FnOnce() -> A,
@@ -351,8 +369,9 @@
         }
     }
 
-    /// If the right value is not present, replace it the value computed by the closure `f`.
-    /// Returns a mutable reference to the now-present right value.
+    /// If the right value is not present, replace it the value computed by the
+    /// closure `f`. Returns a mutable reference to the now-present right
+    /// value.
     pub fn right_or_insert_with<F>(&mut self, f: F) -> &mut B
     where
         F: FnOnce() -> B,
@@ -363,8 +382,8 @@
         }
     }
 
-    /// Sets the `left` value of this instance, and returns a mutable reference to it.
-    /// Does not affect the `right` value.
+    /// Sets the `left` value of this instance, and returns a mutable reference
+    /// to it. Does not affect the `right` value.
     ///
     /// # Examples
     /// ```
@@ -389,7 +408,8 @@
                 // This is like a map in place operation. We move out of the reference,
                 // change the value, and then move back into the reference.
                 unsafe {
-                    // SAFETY: We know this pointer is valid for reading since we got it from a reference.
+                    // SAFETY: We know this pointer is valid for reading since we got it from a
+                    // reference.
                     let right = std::ptr::read(right as *mut _);
                     // SAFETY: Again, we know the pointer is valid since we got it from a reference.
                     std::ptr::write(self as *mut _, Both(val, right));
@@ -406,8 +426,8 @@
         }
     }
 
-    /// Sets the `right` value of this instance, and returns a mutable reference to it.
-    /// Does not affect the `left` value.
+    /// Sets the `right` value of this instance, and returns a mutable reference
+    /// to it. Does not affect the `left` value.
     ///
     /// # Examples
     /// ```
@@ -431,7 +451,8 @@
                 // This is like a map in place operation. We move out of the reference,
                 // change the value, and then move back into the reference.
                 unsafe {
-                    // SAFETY: We know this pointer is valid for reading since we got it from a reference.
+                    // SAFETY: We know this pointer is valid for reading since we got it from a
+                    // reference.
                     let left = std::ptr::read(left as *mut _);
                     // SAFETY: Again, we know the pointer is valid since we got it from a reference.
                     std::ptr::write(self as *mut _, Both(left, val));
@@ -447,8 +468,8 @@
         }
     }
 
-    /// Set `self` to `Both(..)`, containing the specified left and right values,
-    /// and returns a mutable reference to those values.
+    /// Set `self` to `Both(..)`, containing the specified left and right
+    /// values, and returns a mutable reference to those values.
     pub fn insert_both(&mut self, left: A, right: B) -> (&mut A, &mut B) {
         *self = Both(left, right);
         if let Both(left, right) = self {
@@ -462,15 +483,25 @@
 }
 
 impl<T> EitherOrBoth<T, T> {
-    /// Return either value of left, right, or apply a function `f` to both values if both are present.
-    /// The input function has to return the same type as both Right and Left carry.
-    /// 
+    /// Return either value of left, right, or apply a function `f` to both
+    /// values if both are present. The input function has to return the
+    /// same type as both Right and Left carry.
+    ///
+    /// This function can be used to preferrably extract the left resp. right
+    /// value, but fall back to the other (i.e. right resp. left) if the
+    /// preferred one is not present.
+    ///
     /// # Examples
     /// ```
     /// # use itertools::EitherOrBoth;
     /// assert_eq!(EitherOrBoth::Both(3, 7).reduce(u32::max), 7);
     /// assert_eq!(EitherOrBoth::Left(3).reduce(u32::max), 3);
     /// assert_eq!(EitherOrBoth::Right(7).reduce(u32::max), 7);
+    ///
+    /// // Extract the left value if present, fall back to the right otherwise.
+    /// assert_eq!(EitherOrBoth::Left("left").reduce(|l, _r| l), "left");
+    /// assert_eq!(EitherOrBoth::Right("right").reduce(|l, _r| l), "right");
+    /// assert_eq!(EitherOrBoth::Both("left", "right").reduce(|l, _r| l), "left");
     /// ```
     pub fn reduce<F>(self, f: F) -> T
     where
@@ -484,12 +515,21 @@
     }
 }
 
-impl<A, B> Into<Option<Either<A, B>>> for EitherOrBoth<A, B> {
-    fn into(self) -> Option<Either<A, B>> {
-        match self {
-            EitherOrBoth::Left(l) => Some(Either::Left(l)),
-            EitherOrBoth::Right(r) => Some(Either::Right(r)),
-            _ => None,
+impl<A, B> From<EitherOrBoth<A, B>> for Option<Either<A, B>> {
+    fn from(value: EitherOrBoth<A, B>) -> Self {
+        match value {
+            Left(l) => Some(Either::Left(l)),
+            Right(r) => Some(Either::Right(r)),
+            Both(..) => None,
+        }
+    }
+}
+
+impl<A, B> From<Either<A, B>> for EitherOrBoth<A, B> {
+    fn from(either: Either<A, B>) -> Self {
+        match either {
+            Either::Left(l) => Left(l),
+            Either::Right(l) => Right(l),
         }
     }
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/exactly_one_err.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/exactly_one_err.rs
similarity index 63%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/exactly_one_err.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/exactly_one_err.rs
index c54ae77..767d0e7f7 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/exactly_one_err.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/exactly_one_err.rs
@@ -8,14 +8,15 @@
 
 use crate::size_hint;
 
-/// Iterator returned for the error case of `IterTools::exactly_one()`
+/// Iterator returned for the error case of `Itertools::exactly_one()`
 /// This iterator yields exactly the same elements as the input iterator.
 ///
-/// During the execution of `exactly_one` the iterator must be mutated.  This wrapper
-/// effectively "restores" the state of the input iterator when it's handed back.
+/// During the execution of `exactly_one` the iterator must be mutated.  This
+/// wrapper effectively "restores" the state of the input iterator when it's
+/// handed back.
 ///
-/// This is very similar to `PutBackN` except this iterator only supports 0-2 elements and does not
-/// use a `Vec`.
+/// This is very similar to `PutBackN` except this iterator only supports 0-2
+/// elements and does not use a `Vec`.
 #[derive(Clone)]
 pub struct ExactlyOneError<I>
 where
@@ -54,26 +55,37 @@
             Some(Either::Left([first, second])) => {
                 self.first_two = Some(Either::Right(second));
                 Some(first)
-            },
-            Some(Either::Right(second)) => {
-                Some(second)
             }
-            None => {
-                self.inner.next()
-            }
+            Some(Either::Right(second)) => Some(second),
+            None => self.inner.next(),
         }
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
         size_hint::add_scalar(self.inner.size_hint(), self.additional_len())
     }
-}
 
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        match self.first_two {
+            Some(Either::Left([first, second])) => {
+                init = f(init, first);
+                init = f(init, second);
+            }
+            Some(Either::Right(second)) => init = f(init, second),
+            None => {}
+        }
+        self.inner.fold(init, f)
+    }
+}
 
 impl<I> ExactSizeIterator for ExactlyOneError<I> where I: ExactSizeIterator {}
 
-impl<I> Display for ExactlyOneError<I> 
-    where I: Iterator,
+impl<I> Display for ExactlyOneError<I>
+where
+    I: Iterator,
 {
     fn fmt(&self, f: &mut Formatter) -> FmtResult {
         let additional = self.additional_len();
@@ -85,26 +97,30 @@
     }
 }
 
-impl<I> Debug for ExactlyOneError<I> 
-    where I: Iterator + Debug,
-          I::Item: Debug,
+impl<I> Debug for ExactlyOneError<I>
+where
+    I: Iterator + Debug,
+    I::Item: Debug,
 {
     fn fmt(&self, f: &mut Formatter) -> FmtResult {
+        let mut dbg = f.debug_struct("ExactlyOneError");
         match &self.first_two {
             Some(Either::Left([first, second])) => {
-                write!(f, "ExactlyOneError[First: {:?}, Second: {:?}, RemainingIter: {:?}]", first, second, self.inner)
-            },
+                dbg.field("first", first).field("second", second);
+            }
             Some(Either::Right(second)) => {
-                write!(f, "ExactlyOneError[Second: {:?}, RemainingIter: {:?}]", second, self.inner)
+                dbg.field("second", second);
             }
-            None => {
-                write!(f, "ExactlyOneError[RemainingIter: {:?}]", self.inner)
-            }
+            None => {}
         }
+        dbg.field("inner", &self.inner).finish()
     }
 }
 
 #[cfg(feature = "use_std")]
-impl<I> Error for ExactlyOneError<I>  where I: Iterator + Debug, I::Item: Debug, {}
-
-
+impl<I> Error for ExactlyOneError<I>
+where
+    I: Iterator + Debug,
+    I::Item: Debug,
+{
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/extrema_set.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/extrema_set.rs
similarity index 92%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/extrema_set.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/extrema_set.rs
index ae12836..9e2d5a95 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/extrema_set.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/extrema_set.rs
@@ -1,3 +1,4 @@
+use alloc::{vec, vec::Vec};
 use std::cmp::Ordering;
 
 /// Implementation guts for `min_set`, `min_set_by`, and `min_set_by_key`.
@@ -42,7 +43,5 @@
     F: FnMut(&I::Item) -> K,
     Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering,
 {
-    min_set_impl(it, key_for, |it1, it2, key1, key2| {
-        compare(it2, it1, key2, key1)
-    })
+    min_set_impl(it, key_for, |it1, it2, key1, key2| compare(it2, it1, key2, key1))
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/flatten_ok.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/flatten_ok.rs
similarity index 76%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/flatten_ok.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/flatten_ok.rs
index 21ae1f72..c1e06b5 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/flatten_ok.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/flatten_ok.rs
@@ -9,11 +9,7 @@
     I: Iterator<Item = Result<T, E>>,
     T: IntoIterator,
 {
-    FlattenOk {
-        iter,
-        inner_front: None,
-        inner_back: None,
-    }
+    FlattenOk { iter, inner_front: None, inner_back: None }
 }
 
 /// An iterator adaptor that flattens `Result::Ok` values and
@@ -72,16 +68,37 @@
         }
     }
 
+    fn fold<B, F>(self, init: B, mut f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        // Front
+        let mut acc = match self.inner_front {
+            Some(x) => x.fold(init, |a, o| f(a, Ok(o))),
+            None => init,
+        };
+
+        acc = self.iter.fold(acc, |acc, x| match x {
+            Ok(it) => it.into_iter().fold(acc, |a, o| f(a, Ok(o))),
+            Err(e) => f(acc, Err(e)),
+        });
+
+        // Back
+        match self.inner_back {
+            Some(x) => x.fold(acc, |a, o| f(a, Ok(o))),
+            None => acc,
+        }
+    }
+
     fn size_hint(&self) -> (usize, Option<usize>) {
         let inner_hint = |inner: &Option<T::IntoIter>| {
-            inner
-                .as_ref()
-                .map(Iterator::size_hint)
-                .unwrap_or((0, Some(0)))
+            inner.as_ref().map(Iterator::size_hint).unwrap_or((0, Some(0)))
         };
         let inner_front = inner_hint(&self.inner_front);
         let inner_back = inner_hint(&self.inner_back);
-        // The outer iterator `Ok` case could be (0, None) as we don't know its size_hint yet.
+        // The outer iterator `Ok` case could be (0, None) as we don't know its
+        // size_hint yet.
         let outer = match self.iter.size_hint() {
             (0, Some(0)) => (0, Some(0)),
             _ => (0, None),
@@ -130,6 +147,29 @@
             }
         }
     }
+
+    fn rfold<B, F>(self, init: B, mut f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        // Back
+        let mut acc = match self.inner_back {
+            Some(x) => x.rfold(init, |a, o| f(a, Ok(o))),
+            None => init,
+        };
+
+        acc = self.iter.rfold(acc, |acc, x| match x {
+            Ok(it) => it.into_iter().rfold(acc, |a, o| f(a, Ok(o))),
+            Err(e) => f(acc, Err(e)),
+        });
+
+        // Front
+        match self.inner_front {
+            Some(x) => x.rfold(acc, |a, o| f(a, Ok(o))),
+            None => acc,
+        }
+    }
 }
 
 impl<I, T, E> Clone for FlattenOk<I, T, E>
@@ -147,13 +187,7 @@
     T: IntoIterator,
     T::IntoIter: fmt::Debug,
 {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("FlattenOk")
-            .field("iter", &self.iter)
-            .field("inner_front", &self.inner_front)
-            .field("inner_back", &self.inner_back)
-            .finish()
-    }
+    debug_fmt_fields!(FlattenOk, iter, inner_front, inner_back);
 }
 
 /// Only the iterator being flattened needs to implement [`FusedIterator`].
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/format.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/format.rs
similarity index 74%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/format.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/format.rs
index c4cb65d..f909ec9 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/format.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/format.rs
@@ -9,7 +9,8 @@
 /// See [`.format_with()`](crate::Itertools::format_with) for more information.
 pub struct FormatWith<'a, I, F> {
     sep: &'a str,
-    /// FormatWith uses interior mutability because Display::fmt takes &self.
+    /// `FormatWith` uses interior mutability because `Display::fmt` takes
+    /// `&self`.
     inner: Cell<Option<(I, F)>>,
 }
 
@@ -22,7 +23,7 @@
 /// for more information.
 pub struct Format<'a, I> {
     sep: &'a str,
-    /// Format uses interior mutability because Display::fmt takes &self.
+    /// `Format` uses interior mutability because `Display::fmt` takes `&self`.
     inner: Cell<Option<I>>,
 }
 
@@ -31,23 +32,17 @@
     I: Iterator,
     F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result,
 {
-    FormatWith {
-        sep: separator,
-        inner: Cell::new(Some((iter, f))),
-    }
+    FormatWith { sep: separator, inner: Cell::new(Some((iter, f))) }
 }
 
 pub fn new_format_default<I>(iter: I, separator: &str) -> Format<'_, I>
 where
     I: Iterator,
 {
-    Format {
-        sep: separator,
-        inner: Cell::new(Some(iter)),
-    }
+    Format { sep: separator, inner: Cell::new(Some(iter)) }
 }
 
-impl<'a, I, F> fmt::Display for FormatWith<'a, I, F>
+impl<I, F> fmt::Display for FormatWith<'_, I, F>
 where
     I: Iterator,
     F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result,
@@ -71,7 +66,17 @@
     }
 }
 
-impl<'a, I> Format<'a, I>
+impl<I, F> fmt::Debug for FormatWith<'_, I, F>
+where
+    I: Iterator,
+    F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Display::fmt(self, f)
+    }
+}
+
+impl<I> Format<'_, I>
 where
     I: Iterator,
 {
@@ -115,7 +120,7 @@
 
 impl_format! {Display Debug UpperExp LowerExp UpperHex LowerHex Octal Binary Pointer}
 
-impl<'a, I, F> Clone for FormatWith<'a, I, F>
+impl<I, F> Clone for FormatWith<'_, I, F>
 where
     (I, F): Clone,
 {
@@ -124,24 +129,19 @@
             into: &'r FormatWith<'a, I, F>,
             inner: Option<(I, F)>,
         }
-        // This ensures we preserve the state of the original `FormatWith` if `Clone` panics
-        impl<'r, 'a, I, F> Drop for PutBackOnDrop<'r, 'a, I, F> {
+        // This ensures we preserve the state of the original `FormatWith` if `Clone`
+        // panics
+        impl<I, F> Drop for PutBackOnDrop<'_, '_, I, F> {
             fn drop(&mut self) {
                 self.into.inner.set(self.inner.take())
             }
         }
-        let pbod = PutBackOnDrop {
-            inner: self.inner.take(),
-            into: self,
-        };
-        Self {
-            inner: Cell::new(pbod.inner.clone()),
-            sep: self.sep,
-        }
+        let pbod = PutBackOnDrop { inner: self.inner.take(), into: self };
+        Self { inner: Cell::new(pbod.inner.clone()), sep: self.sep }
     }
 }
 
-impl<'a, I> Clone for Format<'a, I>
+impl<I> Clone for Format<'_, I>
 where
     I: Clone,
 {
@@ -150,19 +150,14 @@
             into: &'r Format<'a, I>,
             inner: Option<I>,
         }
-        // This ensures we preserve the state of the original `FormatWith` if `Clone` panics
-        impl<'r, 'a, I> Drop for PutBackOnDrop<'r, 'a, I> {
+        // This ensures we preserve the state of the original `FormatWith` if `Clone`
+        // panics
+        impl<I> Drop for PutBackOnDrop<'_, '_, I> {
             fn drop(&mut self) {
                 self.into.inner.set(self.inner.take())
             }
         }
-        let pbod = PutBackOnDrop {
-            inner: self.inner.take(),
-            into: self,
-        };
-        Self {
-            inner: Cell::new(pbod.inner.clone()),
-            sep: self.sep,
-        }
+        let pbod = PutBackOnDrop { inner: self.inner.take(), into: self };
+        Self { inner: Cell::new(pbod.inner.clone()), sep: self.sep }
     }
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/free.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/free.rs
similarity index 72%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/free.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/free.rs
index 19e3e28..4c68205 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/free.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/free.rs
@@ -10,30 +10,24 @@
 type VecIntoIter<T> = alloc::vec::IntoIter<T>;
 
 #[cfg(feature = "use_alloc")]
-use alloc::{
-    string::String,
-};
+use alloc::string::String;
 
-use crate::Itertools;
 use crate::intersperse::{Intersperse, IntersperseWith};
+use crate::Itertools;
 
-pub use crate::adaptors::{
-    interleave,
-    merge,
-    put_back,
-};
+pub use crate::adaptors::{interleave, put_back};
 #[cfg(feature = "use_alloc")]
-pub use crate::put_back_n_impl::put_back_n;
+pub use crate::kmerge_impl::kmerge;
+pub use crate::merge_join::{merge, merge_join_by};
 #[cfg(feature = "use_alloc")]
 pub use crate::multipeek_impl::multipeek;
 #[cfg(feature = "use_alloc")]
 pub use crate::peek_nth::peek_nth;
 #[cfg(feature = "use_alloc")]
-pub use crate::kmerge_impl::kmerge;
-pub use crate::zip_eq_impl::zip_eq;
-pub use crate::merge_join::merge_join_by;
+pub use crate::put_back_n_impl::put_back_n;
 #[cfg(feature = "use_alloc")]
 pub use crate::rciter_impl::rciter;
+pub use crate::zip_eq_impl::zip_eq;
 
 /// Iterate `iterable` with a particular value inserted between each element.
 ///
@@ -42,11 +36,12 @@
 /// ```
 /// use itertools::intersperse;
 ///
-/// itertools::assert_equal(intersperse((0..3), 8), vec![0, 8, 1, 8, 2]);
+/// itertools::assert_equal(intersperse(0..3, 8), vec![0, 8, 1, 8, 2]);
 /// ```
 pub fn intersperse<I>(iterable: I, element: I::Item) -> Intersperse<I::IntoIter>
-    where I: IntoIterator,
-          <I as IntoIterator>::Item: Clone
+where
+    I: IntoIterator,
+    <I as IntoIterator>::Item: Clone,
 {
     Itertools::intersperse(iterable.into_iter(), element)
 }
@@ -60,12 +55,13 @@
 /// use itertools::intersperse_with;
 ///
 /// let mut i = 10;
-/// itertools::assert_equal(intersperse_with((0..3), || { i -= 1; i }), vec![0, 9, 1, 8, 2]);
+/// itertools::assert_equal(intersperse_with(0..3, || { i -= 1; i }), vec![0, 9, 1, 8, 2]);
 /// assert_eq!(i, 8);
 /// ```
 pub fn intersperse_with<I, F>(iterable: I, element: F) -> IntersperseWith<I::IntoIter, F>
-    where I: IntoIterator,
-          F: FnMut() -> I::Item
+where
+    I: IntoIterator,
+    F: FnMut() -> I::Item,
 {
     Itertools::intersperse_with(iterable.into_iter(), element)
 }
@@ -79,10 +75,12 @@
 ///
 /// for (i, elt) in enumerate(&[1, 2, 3]) {
 ///     /* loop body */
+///     # let _ = (i, elt);
 /// }
 /// ```
 pub fn enumerate<I>(iterable: I) -> iter::Enumerate<I::IntoIter>
-    where I: IntoIterator
+where
+    I: IntoIterator,
 {
     iterable.into_iter().enumerate()
 }
@@ -96,11 +94,13 @@
 ///
 /// for elt in rev(&[1, 2, 3]) {
 ///     /* loop body */
+///     # let _ = elt;
 /// }
 /// ```
 pub fn rev<I>(iterable: I) -> iter::Rev<I::IntoIter>
-    where I: IntoIterator,
-          I::IntoIter: DoubleEndedIterator
+where
+    I: IntoIterator,
+    I::IntoIter: DoubleEndedIterator,
 {
     iterable.into_iter().rev()
 }
@@ -108,7 +108,7 @@
 /// Converts the arguments to iterators and zips them.
 ///
 /// [`IntoIterator`] enabled version of [`Iterator::zip`].
-/// 
+///
 /// ## Example
 ///
 /// ```
@@ -121,23 +121,26 @@
 /// }
 /// assert_eq!(result, vec![(1, 'a'),(2, 'b'),(3, 'c')]);
 /// ```
-#[deprecated(note="Use [std::iter::zip](https://doc.rust-lang.org/std/iter/fn.zip.html) instead", since="0.10.4")]
+#[deprecated(
+    note = "Use [std::iter::zip](https://doc.rust-lang.org/std/iter/fn.zip.html) instead",
+    since = "0.10.4"
+)]
 pub fn zip<I, J>(i: I, j: J) -> Zip<I::IntoIter, J::IntoIter>
-    where I: IntoIterator,
-          J: IntoIterator
+where
+    I: IntoIterator,
+    J: IntoIterator,
 {
     i.into_iter().zip(j)
 }
 
-
-/// Takes two iterables and creates a new iterator over both in sequence. 
+/// Takes two iterables and creates a new iterator over both in sequence.
 ///
 /// [`IntoIterator`] enabled version of [`Iterator::chain`].
 ///
 /// ## Example
 /// ```
 /// use itertools::chain;
-/// 
+///
 /// let mut result:Vec<i32> = Vec::new();
 ///
 /// for element in chain(&[1, 2, 3], &[4]) {
@@ -145,14 +148,18 @@
 /// }
 /// assert_eq!(result, vec![1, 2, 3, 4]);
 /// ```
-pub fn chain<I, J>(i: I, j: J) -> iter::Chain<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
-    where I: IntoIterator,
-          J: IntoIterator<Item = I::Item>
+pub fn chain<I, J>(
+    i: I,
+    j: J,
+) -> iter::Chain<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
+where
+    I: IntoIterator,
+    J: IntoIterator<Item = I::Item>,
 {
     i.into_iter().chain(j)
 }
 
-/// Create an iterator that clones each element from &T to T
+/// Create an iterator that clones each element from `&T` to `T`.
 ///
 /// [`IntoIterator`] enabled version of [`Iterator::cloned`].
 ///
@@ -161,9 +168,10 @@
 ///
 /// assert_eq!(cloned(b"abc").next(), Some(b'a'));
 /// ```
-pub fn cloned<'a, I, T: 'a>(iterable: I) -> iter::Cloned<I::IntoIter>
-    where I: IntoIterator<Item=&'a T>,
-          T: Clone,
+pub fn cloned<'a, I, T>(iterable: I) -> iter::Cloned<I::IntoIter>
+where
+    I: IntoIterator<Item = &'a T>,
+    T: Clone + 'a,
 {
     iterable.into_iter().cloned()
 }
@@ -178,8 +186,9 @@
 /// assert_eq!(fold(&[1., 2., 3.], 0., |a, &b| f32::max(a, b)), 3.);
 /// ```
 pub fn fold<I, B, F>(iterable: I, init: B, f: F) -> B
-    where I: IntoIterator,
-          F: FnMut(B, I::Item) -> B
+where
+    I: IntoIterator,
+    F: FnMut(B, I::Item) -> B,
 {
     iterable.into_iter().fold(init, f)
 }
@@ -194,8 +203,9 @@
 /// assert!(all(&[1, 2, 3], |elt| *elt > 0));
 /// ```
 pub fn all<I, F>(iterable: I, f: F) -> bool
-    where I: IntoIterator,
-          F: FnMut(I::Item) -> bool
+where
+    I: IntoIterator,
+    F: FnMut(I::Item) -> bool,
 {
     iterable.into_iter().all(f)
 }
@@ -210,8 +220,9 @@
 /// assert!(any(&[0, -1, 2], |elt| *elt > 0));
 /// ```
 pub fn any<I, F>(iterable: I, f: F) -> bool
-    where I: IntoIterator,
-          F: FnMut(I::Item) -> bool
+where
+    I: IntoIterator,
+    F: FnMut(I::Item) -> bool,
 {
     iterable.into_iter().any(f)
 }
@@ -226,8 +237,9 @@
 /// assert_eq!(max(0..10), Some(9));
 /// ```
 pub fn max<I>(iterable: I) -> Option<I::Item>
-    where I: IntoIterator,
-          I::Item: Ord
+where
+    I: IntoIterator,
+    I::Item: Ord,
 {
     iterable.into_iter().max()
 }
@@ -242,14 +254,14 @@
 /// assert_eq!(min(0..10), Some(0));
 /// ```
 pub fn min<I>(iterable: I) -> Option<I::Item>
-    where I: IntoIterator,
-          I::Item: Ord
+where
+    I: IntoIterator,
+    I::Item: Ord,
 {
     iterable.into_iter().min()
 }
 
-
-/// Combine all iterator elements into one String, separated by `sep`.
+/// Combine all iterator elements into one `String`, separated by `sep`.
 ///
 /// [`IntoIterator`] enabled version of [`Itertools::join`].
 ///
@@ -260,8 +272,9 @@
 /// ```
 #[cfg(feature = "use_alloc")]
 pub fn join<I>(iterable: I, sep: &str) -> String
-    where I: IntoIterator,
-          I::Item: Display
+where
+    I: IntoIterator,
+    I::Item: Display,
 {
     iterable.into_iter().join(sep)
 }
@@ -278,9 +291,29 @@
 /// ```
 #[cfg(feature = "use_alloc")]
 pub fn sorted<I>(iterable: I) -> VecIntoIter<I::Item>
-    where I: IntoIterator,
-          I::Item: Ord
+where
+    I: IntoIterator,
+    I::Item: Ord,
 {
     iterable.into_iter().sorted()
 }
 
+/// Sort all iterator elements into a new iterator in ascending order.
+/// This sort is unstable (i.e., may reorder equal elements).
+///
+/// [`IntoIterator`] enabled version of [`Itertools::sorted_unstable`].
+///
+/// ```
+/// use itertools::sorted_unstable;
+/// use itertools::assert_equal;
+///
+/// assert_equal(sorted_unstable("rust".chars()), "rstu".chars());
+/// ```
+#[cfg(feature = "use_alloc")]
+pub fn sorted_unstable<I>(iterable: I) -> VecIntoIter<I::Item>
+where
+    I: IntoIterator,
+    I::Item: Ord,
+{
+    iterable.into_iter().sorted_unstable()
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/group_map.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/group_map.rs
similarity index 65%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/group_map.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/group_map.rs
index a2d0ebb..3dcee83 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/group_map.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/group_map.rs
@@ -9,8 +9,9 @@
 /// See [`.into_group_map()`](crate::Itertools::into_group_map)
 /// for more information.
 pub fn into_group_map<I, K, V>(iter: I) -> HashMap<K, Vec<V>>
-    where I: Iterator<Item=(K, V)>,
-          K: Hash + Eq,
+where
+    I: Iterator<Item = (K, V)>,
+    K: Hash + Eq,
 {
     let mut lookup = HashMap::new();
 
@@ -21,12 +22,11 @@
     lookup
 }
 
-pub fn into_group_map_by<I, K, V>(iter: I, f: impl Fn(&V) -> K) -> HashMap<K, Vec<V>>
-    where
-        I: Iterator<Item=V>,
-        K: Hash + Eq,
+pub fn into_group_map_by<I, K, V, F>(iter: I, mut f: F) -> HashMap<K, Vec<V>>
+where
+    I: Iterator<Item = V>,
+    K: Hash + Eq,
+    F: FnMut(&V) -> K,
 {
-    into_group_map(
-        iter.map(|v| (f(&v), v))
-    )
+    into_group_map(iter.map(|v| (f(&v), v)))
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/groupbylazy.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/groupbylazy.rs
similarity index 79%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/groupbylazy.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/groupbylazy.rs
index 80c6f09..bb0087cc 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/groupbylazy.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/groupbylazy.rs
@@ -1,14 +1,15 @@
-use std::cell::{Cell, RefCell};
 use alloc::vec::{self, Vec};
+use std::cell::{Cell, RefCell};
 
-/// A trait to unify `FnMut` for `GroupBy` with the chunk key in `IntoChunks`
+/// A trait to unify `FnMut` for `ChunkBy` with the chunk key in `IntoChunks`
 trait KeyFunction<A> {
     type Key;
     fn call_mut(&mut self, arg: A) -> Self::Key;
 }
 
-impl<A, K, F: ?Sized> KeyFunction<A> for F
-    where F: FnMut(A) -> K
+impl<A, K, F> KeyFunction<A> for F
+where
+    F: FnMut(A) -> K + ?Sized,
 {
     type Key = K;
     #[inline]
@@ -17,7 +18,6 @@
     }
 }
 
-
 /// `ChunkIndex` acts like the grouping key function for `IntoChunks`
 #[derive(Debug, Clone)]
 struct ChunkIndex {
@@ -29,11 +29,7 @@
 impl ChunkIndex {
     #[inline(always)]
     fn new(size: usize) -> Self {
-        ChunkIndex {
-            size,
-            index: 0,
-            key: 0,
-        }
+        Self { size, index: 0, key: 0 }
     }
 }
 
@@ -52,7 +48,8 @@
 
 #[derive(Clone)]
 struct GroupInner<K, I, F>
-    where I: Iterator
+where
+    I: Iterator,
 {
     key: F,
     iter: I,
@@ -65,19 +62,21 @@
     /// Least index for which we still have elements buffered
     oldest_buffered_group: usize,
     /// Group index for `buffer[0]` -- the slots
-    /// bottom_group..oldest_buffered_group are unused and will be erased when
+    /// `bottom_group..oldest_buffered_group` are unused and will be erased when
     /// that range is large enough.
     bottom_group: usize,
     /// Buffered groups, from `bottom_group` (index 0) to `top_group`.
     buffer: Vec<vec::IntoIter<I::Item>>,
-    /// index of last group iter that was dropped, usize::MAX == none
+    /// index of last group iter that was dropped,
+    /// `usize::MAX` initially when no group was dropped
     dropped_group: usize,
 }
 
 impl<K, I, F> GroupInner<K, I, F>
-    where I: Iterator,
-          F: for<'a> KeyFunction<&'a I::Item, Key=K>,
-          K: PartialEq,
+where
+    I: Iterator,
+    F: for<'a> KeyFunction<&'a I::Item, Key = K>,
+    K: PartialEq,
 {
     /// `client`: Index of group that requests next element
     #[inline(always)]
@@ -90,9 +89,8 @@
         */
         if client < self.oldest_buffered_group {
             None
-        } else if client < self.top_group ||
-            (client == self.top_group &&
-             self.buffer.len() > self.top_group - self.bottom_group)
+        } else if client < self.top_group
+            || (client == self.top_group && self.buffer.len() > self.top_group - self.bottom_group)
         {
             self.lookup_buffer(client)
         } else if self.done {
@@ -115,11 +113,14 @@
         if elt.is_none() && client == self.oldest_buffered_group {
             // FIXME: VecDeque is unfortunately not zero allocation when empty,
             // so we do this job manually.
-            // `bottom_group..oldest_buffered_group` is unused, and if it's large enough, erase it.
+            // `bottom_group..oldest_buffered_group` is unused, and if it's large enough,
+            // erase it.
             self.oldest_buffered_group += 1;
             // skip forward further empty queues too
-            while self.buffer.get(self.oldest_buffered_group - self.bottom_group)
-                             .map_or(false, |buf| buf.len() == 0)
+            while self
+                .buffer
+                .get(self.oldest_buffered_group - self.bottom_group)
+                .map_or(false, |buf| buf.len() == 0)
             {
                 self.oldest_buffered_group += 1;
             }
@@ -144,12 +145,14 @@
     fn next_element(&mut self) -> Option<I::Item> {
         debug_assert!(!self.done);
         match self.iter.next() {
-            None => { self.done = true; None }
+            None => {
+                self.done = true;
+                None
+            }
             otherwise => otherwise,
         }
     }
 
-
     #[inline(never)]
     fn step_buffering(&mut self, client: usize) -> Option<I::Item> {
         // requested a later group -- walk through the current group up to
@@ -171,11 +174,13 @@
             let key = self.key.call_mut(&elt);
             match self.current_key.take() {
                 None => {}
-                Some(old_key) => if old_key != key {
-                    self.current_key = Some(key);
-                    first_elt = Some(elt);
-                    break;
-                },
+                Some(old_key) => {
+                    if old_key != key {
+                        self.current_key = Some(key);
+                        first_elt = Some(elt);
+                        break;
+                    }
+                }
             }
             self.current_key = Some(key);
             if self.top_group != self.dropped_group {
@@ -194,7 +199,8 @@
     }
 
     fn push_next_group(&mut self, group: Vec<I::Item>) {
-        // When we add a new buffered group, fill up slots between oldest_buffered_group and top_group
+        // When we add a new buffered group, fill up slots between oldest_buffered_group
+        // and top_group
         while self.top_group - self.bottom_group > self.buffer.len() {
             if self.buffer.is_empty() {
                 self.bottom_group += 1;
@@ -220,12 +226,14 @@
                 let key = self.key.call_mut(&elt);
                 match self.current_key.take() {
                     None => {}
-                    Some(old_key) => if old_key != key {
-                        self.current_key = Some(key);
-                        self.current_elt = Some(elt);
-                        self.top_group += 1;
-                        return None;
-                    },
+                    Some(old_key) => {
+                        if old_key != key {
+                            self.current_key = Some(key);
+                            self.current_elt = Some(elt);
+                            self.top_group += 1;
+                            return None;
+                        }
+                    }
                 }
                 self.current_key = Some(key);
                 Some(elt)
@@ -261,7 +269,8 @@
 }
 
 impl<K, I, F> GroupInner<K, I, F>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     /// Called when a group is dropped
     fn drop_group(&mut self, client: usize) {
@@ -272,10 +281,14 @@
     }
 }
 
-/// `GroupBy` is the storage for the lazy grouping operation.
+#[deprecated(note = "Use `ChunkBy` instead", since = "0.13.0")]
+/// See [`ChunkBy`](crate::structs::ChunkBy).
+pub type GroupBy<K, I, F> = ChunkBy<K, I, F>;
+
+/// `ChunkBy` is the storage for the lazy grouping operation.
 ///
 /// If the groups are consumed in their original order, or if each
-/// group is dropped without keeping it around, then `GroupBy` uses
+/// group is dropped without keeping it around, then `ChunkBy` uses
 /// no allocations. It needs allocations only if several group iterators
 /// are alive at the same time.
 ///
@@ -284,10 +297,11 @@
 /// value. It should be stored in a local variable or temporary and
 /// iterated.
 ///
-/// See [`.group_by()`](crate::Itertools::group_by) for more information.
+/// See [`.chunk_by()`](crate::Itertools::chunk_by) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct GroupBy<K, I, F>
-    where I: Iterator,
+pub struct ChunkBy<K, I, F>
+where
+    I: Iterator,
 {
     inner: RefCell<GroupInner<K, I, F>>,
     // the group iterator's current index. Keep this in the main value
@@ -296,11 +310,12 @@
 }
 
 /// Create a new
-pub fn new<K, J, F>(iter: J, f: F) -> GroupBy<K, J::IntoIter, F>
-    where J: IntoIterator,
-          F: FnMut(&J::Item) -> K,
+pub fn new<K, J, F>(iter: J, f: F) -> ChunkBy<K, J::IntoIter, F>
+where
+    J: IntoIterator,
+    F: FnMut(&J::Item) -> K,
 {
-    GroupBy {
+    ChunkBy {
         inner: RefCell::new(GroupInner {
             key: f,
             iter: iter.into_iter(),
@@ -317,13 +332,15 @@
     }
 }
 
-impl<K, I, F> GroupBy<K, I, F>
-    where I: Iterator,
+impl<K, I, F> ChunkBy<K, I, F>
+where
+    I: Iterator,
 {
     /// `client`: Index of group that requests next element
     fn step(&self, client: usize) -> Option<I::Item>
-        where F: FnMut(&I::Item) -> K,
-              K: PartialEq,
+    where
+        F: FnMut(&I::Item) -> K,
+        K: PartialEq,
     {
         self.inner.borrow_mut().step(client)
     }
@@ -334,11 +351,12 @@
     }
 }
 
-impl<'a, K, I, F> IntoIterator for &'a GroupBy<K, I, F>
-    where I: Iterator,
-          I::Item: 'a,
-          F: FnMut(&I::Item) -> K,
-          K: PartialEq
+impl<'a, K, I, F> IntoIterator for &'a ChunkBy<K, I, F>
+where
+    I: Iterator,
+    I::Item: 'a,
+    F: FnMut(&I::Item) -> K,
+    K: PartialEq,
 {
     type Item = (K, Group<'a, K, I, F>);
     type IntoIter = Groups<'a, K, I, F>;
@@ -348,26 +366,29 @@
     }
 }
 
-
 /// An iterator that yields the Group iterators.
 ///
 /// Iterator element type is `(K, Group)`:
 /// the group's key `K` and the group's iterator.
 ///
-/// See [`.group_by()`](crate::Itertools::group_by) for more information.
+/// See [`.chunk_by()`](crate::Itertools::chunk_by) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct Groups<'a, K: 'a, I: 'a, F: 'a>
-    where I: Iterator,
-          I::Item: 'a
+pub struct Groups<'a, K, I, F>
+where
+    I: Iterator + 'a,
+    I::Item: 'a,
+    K: 'a,
+    F: 'a,
 {
-    parent: &'a GroupBy<K, I, F>,
+    parent: &'a ChunkBy<K, I, F>,
 }
 
 impl<'a, K, I, F> Iterator for Groups<'a, K, I, F>
-    where I: Iterator,
-          I::Item: 'a,
-          F: FnMut(&I::Item) -> K,
-          K: PartialEq
+where
+    I: Iterator,
+    I::Item: 'a,
+    F: FnMut(&I::Item) -> K,
+    K: PartialEq,
 {
     type Item = (K, Group<'a, K, I, F>);
 
@@ -378,11 +399,7 @@
         let inner = &mut *self.parent.inner.borrow_mut();
         inner.step(index).map(|elt| {
             let key = inner.group_key(index);
-            (key, Group {
-                parent: self.parent,
-                index,
-                first: Some(elt),
-            })
+            (key, Group { parent: self.parent, index, first: Some(elt) })
         })
     }
 }
@@ -390,18 +407,22 @@
 /// An iterator for the elements in a single group.
 ///
 /// Iterator element type is `I::Item`.
-pub struct Group<'a, K: 'a, I: 'a, F: 'a>
-    where I: Iterator,
-          I::Item: 'a,
+pub struct Group<'a, K, I, F>
+where
+    I: Iterator + 'a,
+    I::Item: 'a,
+    K: 'a,
+    F: 'a,
 {
-    parent: &'a GroupBy<K, I, F>,
+    parent: &'a ChunkBy<K, I, F>,
     index: usize,
     first: Option<I::Item>,
 }
 
 impl<'a, K, I, F> Drop for Group<'a, K, I, F>
-    where I: Iterator,
-          I::Item: 'a,
+where
+    I: Iterator,
+    I::Item: 'a,
 {
     fn drop(&mut self) {
         self.parent.drop_group(self.index);
@@ -409,10 +430,11 @@
 }
 
 impl<'a, K, I, F> Iterator for Group<'a, K, I, F>
-    where I: Iterator,
-          I::Item: 'a,
-          F: FnMut(&I::Item) -> K,
-          K: PartialEq,
+where
+    I: Iterator,
+    I::Item: 'a,
+    F: FnMut(&I::Item) -> K,
+    K: PartialEq,
 {
     type Item = I::Item;
     #[inline]
@@ -428,7 +450,8 @@
 
 /// Create a new
 pub fn new_chunks<J>(iter: J, size: usize) -> IntoChunks<J::IntoIter>
-    where J: IntoIterator,
+where
+    J: IntoIterator,
 {
     IntoChunks {
         inner: RefCell::new(GroupInner {
@@ -447,10 +470,9 @@
     }
 }
 
-
 /// `ChunkLazy` is the storage for a lazy chunking operation.
 ///
-/// `IntoChunks` behaves just like `GroupBy`: it is iterable, and
+/// `IntoChunks` behaves just like `ChunkBy`: it is iterable, and
 /// it only buffers if several chunk iterators are alive at the same time.
 ///
 /// This type implements [`IntoIterator`] (it is **not** an iterator
@@ -463,7 +485,8 @@
 /// See [`.chunks()`](crate::Itertools::chunks) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct IntoChunks<I>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     inner: RefCell<GroupInner<usize, I, ChunkIndex>>,
     // the chunk iterator's current index. Keep this in the main value
@@ -472,15 +495,16 @@
 }
 
 impl<I> Clone for IntoChunks<I>
-    where I: Clone + Iterator,
-          I::Item: Clone,
+where
+    I: Clone + Iterator,
+    I::Item: Clone,
 {
     clone_fields!(inner, index);
 }
 
-
 impl<I> IntoChunks<I>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     /// `client`: Index of chunk that requests next element
     fn step(&self, client: usize) -> Option<I::Item> {
@@ -494,20 +518,18 @@
 }
 
 impl<'a, I> IntoIterator for &'a IntoChunks<I>
-    where I: Iterator,
-          I::Item: 'a,
+where
+    I: Iterator,
+    I::Item: 'a,
 {
     type Item = Chunk<'a, I>;
     type IntoIter = Chunks<'a, I>;
 
     fn into_iter(self) -> Self::IntoIter {
-        Chunks {
-            parent: self,
-        }
+        Chunks { parent: self }
     }
 }
 
-
 /// An iterator that yields the Chunk iterators.
 ///
 /// Iterator element type is `Chunk`.
@@ -515,16 +537,18 @@
 /// See [`.chunks()`](crate::Itertools::chunks) for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 #[derive(Clone)]
-pub struct Chunks<'a, I: 'a>
-    where I: Iterator,
-          I::Item: 'a,
+pub struct Chunks<'a, I>
+where
+    I: Iterator + 'a,
+    I::Item: 'a,
 {
     parent: &'a IntoChunks<I>,
 }
 
 impl<'a, I> Iterator for Chunks<'a, I>
-    where I: Iterator,
-          I::Item: 'a,
+where
+    I: Iterator,
+    I::Item: 'a,
 {
     type Item = Chunk<'a, I>;
 
@@ -533,22 +557,17 @@
         let index = self.parent.index.get();
         self.parent.index.set(index + 1);
         let inner = &mut *self.parent.inner.borrow_mut();
-        inner.step(index).map(|elt| {
-            Chunk {
-                parent: self.parent,
-                index,
-                first: Some(elt),
-            }
-        })
+        inner.step(index).map(|elt| Chunk { parent: self.parent, index, first: Some(elt) })
     }
 }
 
 /// An iterator for the elements in a single chunk.
 ///
 /// Iterator element type is `I::Item`.
-pub struct Chunk<'a, I: 'a>
-    where I: Iterator,
-          I::Item: 'a,
+pub struct Chunk<'a, I>
+where
+    I: Iterator + 'a,
+    I::Item: 'a,
 {
     parent: &'a IntoChunks<I>,
     index: usize,
@@ -556,8 +575,9 @@
 }
 
 impl<'a, I> Drop for Chunk<'a, I>
-    where I: Iterator,
-          I::Item: 'a,
+where
+    I: Iterator,
+    I::Item: 'a,
 {
     fn drop(&mut self) {
         self.parent.drop_group(self.index);
@@ -565,8 +585,9 @@
 }
 
 impl<'a, I> Iterator for Chunk<'a, I>
-    where I: Iterator,
-          I::Item: 'a,
+where
+    I: Iterator,
+    I::Item: 'a,
 {
     type Item = I::Item;
     #[inline]
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/grouping_map.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/grouping_map.rs
similarity index 64%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/grouping_map.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/grouping_map.rs
index bb5b582..b23b372 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/grouping_map.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/grouping_map.rs
@@ -1,50 +1,57 @@
-#![cfg(feature = "use_std")]
-
-use crate::MinMaxResult;
-use std::collections::HashMap;
+use crate::{
+    adaptors::map::{MapSpecialCase, MapSpecialCaseFn},
+    MinMaxResult,
+};
 use std::cmp::Ordering;
+use std::collections::HashMap;
 use std::hash::Hash;
 use std::iter::Iterator;
 use std::ops::{Add, Mul};
 
-/// A wrapper to allow for an easy [`into_grouping_map_by`](crate::Itertools::into_grouping_map_by)
-#[derive(Clone, Debug)]
-pub struct MapForGrouping<I, F>(I, F);
+/// A wrapper to allow for an easy
+/// [`into_grouping_map_by`](crate::Itertools::into_grouping_map_by)
+pub type MapForGrouping<I, F> = MapSpecialCase<I, GroupingMapFn<F>>;
 
-impl<I, F> MapForGrouping<I, F> {
-    pub(crate) fn new(iter: I, key_mapper: F) -> Self {
-        Self(iter, key_mapper)
+#[derive(Clone)]
+pub struct GroupingMapFn<F>(F);
+
+impl<F> std::fmt::Debug for GroupingMapFn<F> {
+    debug_fmt_fields!(GroupingMapFn,);
+}
+
+impl<V, K, F: FnMut(&V) -> K> MapSpecialCaseFn<V> for GroupingMapFn<F> {
+    type Out = (K, V);
+    fn call(&mut self, v: V) -> Self::Out {
+        ((self.0)(&v), v)
     }
 }
 
-impl<K, V, I, F> Iterator for MapForGrouping<I, F>
-    where I: Iterator<Item = V>,
-          K: Hash + Eq,
-          F: FnMut(&V) -> K,
-{
-    type Item = (K, V);
-    fn next(&mut self) -> Option<Self::Item> {
-        self.0.next().map(|val| ((self.1)(&val), val))
-    }
+pub(crate) fn new_map_for_grouping<K, I: Iterator, F: FnMut(&I::Item) -> K>(
+    iter: I,
+    key_mapper: F,
+) -> MapForGrouping<I, F> {
+    MapSpecialCase { iter, f: GroupingMapFn(key_mapper) }
 }
 
 /// Creates a new `GroupingMap` from `iter`
 pub fn new<I, K, V>(iter: I) -> GroupingMap<I>
-    where I: Iterator<Item = (K, V)>,
-          K: Hash + Eq,
+where
+    I: Iterator<Item = (K, V)>,
+    K: Hash + Eq,
 {
     GroupingMap { iter }
 }
 
-/// `GroupingMapBy` is an intermediate struct for efficient group-and-fold operations.
-/// 
+/// `GroupingMapBy` is an intermediate struct for efficient group-and-fold
+/// operations.
+///
 /// See [`GroupingMap`] for more informations.
 pub type GroupingMapBy<I, F> = GroupingMap<MapForGrouping<I, F>>;
 
-/// `GroupingMap` is an intermediate struct for efficient group-and-fold operations.
-/// It groups elements by their key and at the same time fold each group
-/// using some aggregating operation.
-/// 
+/// `GroupingMap` is an intermediate struct for efficient group-and-fold
+/// operations. It groups elements by their key and at the same time fold each
+/// group using some aggregating operation.
+///
 /// No method on this struct performs temporary allocations.
 #[derive(Clone, Debug)]
 #[must_use = "GroupingMap is lazy and do nothing unless consumed"]
@@ -53,32 +60,37 @@
 }
 
 impl<I, K, V> GroupingMap<I>
-    where I: Iterator<Item = (K, V)>,
-          K: Hash + Eq,
+where
+    I: Iterator<Item = (K, V)>,
+    K: Hash + Eq,
 {
     /// This is the generic way to perform any operation on a `GroupingMap`.
     /// It's suggested to use this method only to implement custom operations
     /// when the already provided ones are not enough.
-    /// 
-    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
-    /// of each group sequentially, passing the previously accumulated value, a reference to the key
-    /// and the current element as arguments, and stores the results in an `HashMap`.
     ///
-    /// The `operation` function is invoked on each element with the following parameters:
-    ///  - the current value of the accumulator of the group if there is currently one;
+    /// Groups elements from the `GroupingMap` source by key and applies
+    /// `operation` to the elements of each group sequentially, passing the
+    /// previously accumulated value, a reference to the key and the current
+    /// element as arguments, and stores the results in an `HashMap`.
+    ///
+    /// The `operation` function is invoked on each element with the following
+    /// parameters:
+    ///  - the current value of the accumulator of the group if there is
+    ///    currently one;
     ///  - a reference to the key of the group this element belongs to;
     ///  - the element from the source being aggregated;
-    /// 
-    /// If `operation` returns `Some(element)` then the accumulator is updated with `element`,
-    /// otherwise the previous accumulation is discarded.
     ///
-    /// Return a `HashMap` associating the key of each group with the result of aggregation of
-    /// that group's elements. If the aggregation of the last element of a group discards the
-    /// accumulator then there won't be an entry associated to that group's key.
-    /// 
+    /// If `operation` returns `Some(element)` then the accumulator is updated
+    /// with `element`, otherwise the previous accumulation is discarded.
+    ///
+    /// Return a `HashMap` associating the key of each group with the result of
+    /// aggregation of that group's elements. If the aggregation of the last
+    /// element of a group discards the accumulator then there won't be an
+    /// entry associated to that group's key.
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let data = vec![2, 8, 5, 7, 9, 0, 4, 10];
     /// let lookup = data.into_iter()
     ///     .into_grouping_map_by(|&n| n % 4)
@@ -89,7 +101,7 @@
     ///             Some(acc.unwrap_or(0) + val)
     ///         }
     ///     });
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 4);        // 0 resets the accumulator so only 4 is summed
     /// assert_eq!(lookup[&1], 5 + 9);
     /// assert_eq!(lookup.get(&2), None); // 10 resets the accumulator and nothing is summed afterward
@@ -97,7 +109,8 @@
     /// assert_eq!(lookup.len(), 3);      // The final keys are only 0, 1 and 2
     /// ```
     pub fn aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R>
-        where FO: FnMut(Option<R>, &K, V) -> Option<R>,
+    where
+        FO: FnMut(Option<R>, &K, V) -> Option<R>,
     {
         let mut destination_map = HashMap::new();
 
@@ -111,70 +124,124 @@
         destination_map
     }
 
-    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
-    /// of each group sequentially, passing the previously accumulated value, a reference to the key
-    /// and the current element as arguments, and stores the results in a new map.
+    /// Groups elements from the `GroupingMap` source by key and applies
+    /// `operation` to the elements of each group sequentially, passing the
+    /// previously accumulated value, a reference to the key and the current
+    /// element as arguments, and stores the results in a new map.
     ///
-    /// `init` is the value from which will be cloned the initial value of each accumulator.
+    /// `init` is called to obtain the initial value of each accumulator.
     ///
-    /// `operation` is a function that is invoked on each element with the following parameters:
+    /// `operation` is a function that is invoked on each element with the
+    /// following parameters:
     ///  - the current value of the accumulator of the group;
     ///  - a reference to the key of the group this element belongs to;
     ///  - the element from the source being accumulated.
     ///
-    /// Return a `HashMap` associating the key of each group with the result of folding that group's elements.
-    /// 
+    /// Return a `HashMap` associating the key of each group with the result of
+    /// folding that group's elements.
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
+    /// #[derive(Debug, Default)]
+    /// struct Accumulator {
+    ///   acc: usize,
+    /// }
+    ///
     /// let lookup = (1..=7)
     ///     .into_grouping_map_by(|&n| n % 3)
-    ///     .fold(0, |acc, _key, val| acc + val);
-    /// 
-    /// assert_eq!(lookup[&0], 3 + 6);
-    /// assert_eq!(lookup[&1], 1 + 4 + 7);
-    /// assert_eq!(lookup[&2], 2 + 5);
+    ///     .fold_with(|_key, _val| Default::default(), |Accumulator { acc }, _key, val| {
+    ///         let acc = acc + val;
+    ///         Accumulator { acc }
+    ///      });
+    ///
+    /// assert_eq!(lookup[&0].acc, 3 + 6);
+    /// assert_eq!(lookup[&1].acc, 1 + 4 + 7);
+    /// assert_eq!(lookup[&2].acc, 2 + 5);
     /// assert_eq!(lookup.len(), 3);
     /// ```
-    pub fn fold<FO, R>(self, init: R, mut operation: FO) -> HashMap<K, R>
-        where R: Clone,
-              FO: FnMut(R, &K, V) -> R,
+    pub fn fold_with<FI, FO, R>(self, mut init: FI, mut operation: FO) -> HashMap<K, R>
+    where
+        FI: FnMut(&K, &V) -> R,
+        FO: FnMut(R, &K, V) -> R,
     {
         self.aggregate(|acc, key, val| {
-            let acc = acc.unwrap_or_else(|| init.clone());
+            let acc = acc.unwrap_or_else(|| init(key, &val));
             Some(operation(acc, key, val))
         })
     }
 
-    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
-    /// of each group sequentially, passing the previously accumulated value, a reference to the key
-    /// and the current element as arguments, and stores the results in a new map.
+    /// Groups elements from the `GroupingMap` source by key and applies
+    /// `operation` to the elements of each group sequentially, passing the
+    /// previously accumulated value, a reference to the key and the current
+    /// element as arguments, and stores the results in a new map.
     ///
-    /// This is similar to [`fold`] but the initial value of the accumulator is the first element of the group.
+    /// `init` is the value from which will be cloned the initial value of each
+    /// accumulator.
     ///
-    /// `operation` is a function that is invoked on each element with the following parameters:
+    /// `operation` is a function that is invoked on each element with the
+    /// following parameters:
     ///  - the current value of the accumulator of the group;
     ///  - a reference to the key of the group this element belongs to;
     ///  - the element from the source being accumulated.
     ///
-    /// Return a `HashMap` associating the key of each group with the result of folding that group's elements.
-    /// 
-    /// [`fold`]: GroupingMap::fold
-    /// 
+    /// Return a `HashMap` associating the key of each group with the result of
+    /// folding that group's elements.
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = (1..=7)
     ///     .into_grouping_map_by(|&n| n % 3)
-    ///     .fold_first(|acc, _key, val| acc + val);
-    /// 
+    ///     .fold(0, |acc, _key, val| acc + val);
+    ///
     /// assert_eq!(lookup[&0], 3 + 6);
     /// assert_eq!(lookup[&1], 1 + 4 + 7);
     /// assert_eq!(lookup[&2], 2 + 5);
     /// assert_eq!(lookup.len(), 3);
     /// ```
-    pub fn fold_first<FO>(self, mut operation: FO) -> HashMap<K, V>
-        where FO: FnMut(V, &K, V) -> V,
+    pub fn fold<FO, R>(self, init: R, operation: FO) -> HashMap<K, R>
+    where
+        R: Clone,
+        FO: FnMut(R, &K, V) -> R,
+    {
+        self.fold_with(|_, _| init.clone(), operation)
+    }
+
+    /// Groups elements from the `GroupingMap` source by key and applies
+    /// `operation` to the elements of each group sequentially, passing the
+    /// previously accumulated value, a reference to the key and the current
+    /// element as arguments, and stores the results in a new map.
+    ///
+    /// This is similar to [`fold`] but the initial value of the accumulator is
+    /// the first element of the group.
+    ///
+    /// `operation` is a function that is invoked on each element with the
+    /// following parameters:
+    ///  - the current value of the accumulator of the group;
+    ///  - a reference to the key of the group this element belongs to;
+    ///  - the element from the source being accumulated.
+    ///
+    /// Return a `HashMap` associating the key of each group with the result of
+    /// folding that group's elements.
+    ///
+    /// [`fold`]: GroupingMap::fold
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let lookup = (1..=7)
+    ///     .into_grouping_map_by(|&n| n % 3)
+    ///     .reduce(|acc, _key, val| acc + val);
+    ///
+    /// assert_eq!(lookup[&0], 3 + 6);
+    /// assert_eq!(lookup[&1], 1 + 4 + 7);
+    /// assert_eq!(lookup[&2], 2 + 5);
+    /// assert_eq!(lookup.len(), 3);
+    /// ```
+    pub fn reduce<FO>(self, mut operation: FO) -> HashMap<K, V>
+    where
+        FO: FnMut(V, &K, V) -> V,
     {
         self.aggregate(|acc, key, val| {
             Some(match acc {
@@ -184,26 +251,38 @@
         })
     }
 
-    /// Groups elements from the `GroupingMap` source by key and collects the elements of each group in
-    /// an instance of `C`. The iteration order is preserved when inserting elements. 
-    /// 
-    /// Return a `HashMap` associating the key of each group with the collection containing that group's elements.
-    /// 
+    /// See [`.reduce()`](GroupingMap::reduce).
+    #[deprecated(note = "Use .reduce() instead", since = "0.13.0")]
+    pub fn fold_first<FO>(self, operation: FO) -> HashMap<K, V>
+    where
+        FO: FnMut(V, &K, V) -> V,
+    {
+        self.reduce(operation)
+    }
+
+    /// Groups elements from the `GroupingMap` source by key and collects the
+    /// elements of each group in an instance of `C`. The iteration order is
+    /// preserved when inserting elements.
+    ///
+    /// Return a `HashMap` associating the key of each group with the collection
+    /// containing that group's elements.
+    ///
     /// ```
     /// use itertools::Itertools;
     /// use std::collections::HashSet;
-    /// 
+    ///
     /// let lookup = vec![0, 1, 2, 3, 4, 5, 6, 2, 3, 6].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .collect::<HashSet<_>>();
-    /// 
+    ///
     /// assert_eq!(lookup[&0], vec![0, 3, 6].into_iter().collect::<HashSet<_>>());
     /// assert_eq!(lookup[&1], vec![1, 4].into_iter().collect::<HashSet<_>>());
     /// assert_eq!(lookup[&2], vec![2, 5].into_iter().collect::<HashSet<_>>());
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn collect<C>(self) -> HashMap<K, C>
-        where C: Default + Extend<V>,
+    where
+        C: Default + Extend<V>,
     {
         let mut destination_map = HashMap::new();
 
@@ -214,220 +293,244 @@
         destination_map
     }
 
-    /// Groups elements from the `GroupingMap` source by key and finds the maximum of each group.
-    /// 
+    /// Groups elements from the `GroupingMap` source by key and finds the
+    /// maximum of each group.
+    ///
     /// If several elements are equally maximum, the last element is picked.
-    /// 
-    /// Returns a `HashMap` associating the key of each group with the maximum of that group's elements.
-    /// 
+    ///
+    /// Returns a `HashMap` associating the key of each group with the maximum
+    /// of that group's elements.
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .max();
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 12);
     /// assert_eq!(lookup[&1], 7);
     /// assert_eq!(lookup[&2], 8);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn max(self) -> HashMap<K, V>
-        where V: Ord,
+    where
+        V: Ord,
     {
         self.max_by(|_, v1, v2| V::cmp(v1, v2))
     }
 
-    /// Groups elements from the `GroupingMap` source by key and finds the maximum of each group
-    /// with respect to the specified comparison function.
-    /// 
+    /// Groups elements from the `GroupingMap` source by key and finds the
+    /// maximum of each group with respect to the specified comparison
+    /// function.
+    ///
     /// If several elements are equally maximum, the last element is picked.
-    /// 
-    /// Returns a `HashMap` associating the key of each group with the maximum of that group's elements.
-    /// 
+    ///
+    /// Returns a `HashMap` associating the key of each group with the maximum
+    /// of that group's elements.
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .max_by(|_key, x, y| y.cmp(x));
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 3);
     /// assert_eq!(lookup[&1], 1);
     /// assert_eq!(lookup[&2], 5);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn max_by<F>(self, mut compare: F) -> HashMap<K, V>
-        where F: FnMut(&K, &V, &V) -> Ordering,
+    where
+        F: FnMut(&K, &V, &V) -> Ordering,
     {
-        self.fold_first(|acc, key, val| match compare(key, &acc, &val) {
+        self.reduce(|acc, key, val| match compare(key, &acc, &val) {
             Ordering::Less | Ordering::Equal => val,
-            Ordering::Greater => acc
+            Ordering::Greater => acc,
         })
     }
 
-    /// Groups elements from the `GroupingMap` source by key and finds the element of each group
-    /// that gives the maximum from the specified function.
-    /// 
+    /// Groups elements from the `GroupingMap` source by key and finds the
+    /// element of each group that gives the maximum from the specified
+    /// function.
+    ///
     /// If several elements are equally maximum, the last element is picked.
-    /// 
-    /// Returns a `HashMap` associating the key of each group with the maximum of that group's elements.
-    /// 
+    ///
+    /// Returns a `HashMap` associating the key of each group with the maximum
+    /// of that group's elements.
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .max_by_key(|_key, &val| val % 4);
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 3);
     /// assert_eq!(lookup[&1], 7);
     /// assert_eq!(lookup[&2], 5);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn max_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
-        where F: FnMut(&K, &V) -> CK,
-              CK: Ord,
+    where
+        F: FnMut(&K, &V) -> CK,
+        CK: Ord,
     {
         self.max_by(|key, v1, v2| f(key, v1).cmp(&f(key, v2)))
     }
 
-    /// Groups elements from the `GroupingMap` source by key and finds the minimum of each group.
-    /// 
+    /// Groups elements from the `GroupingMap` source by key and finds the
+    /// minimum of each group.
+    ///
     /// If several elements are equally minimum, the first element is picked.
-    /// 
-    /// Returns a `HashMap` associating the key of each group with the minimum of that group's elements.
-    /// 
+    ///
+    /// Returns a `HashMap` associating the key of each group with the minimum
+    /// of that group's elements.
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .min();
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 3);
     /// assert_eq!(lookup[&1], 1);
     /// assert_eq!(lookup[&2], 5);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn min(self) -> HashMap<K, V>
-        where V: Ord,
+    where
+        V: Ord,
     {
         self.min_by(|_, v1, v2| V::cmp(v1, v2))
     }
 
-    /// Groups elements from the `GroupingMap` source by key and finds the minimum of each group
-    /// with respect to the specified comparison function.
-    /// 
+    /// Groups elements from the `GroupingMap` source by key and finds the
+    /// minimum of each group with respect to the specified comparison
+    /// function.
+    ///
     /// If several elements are equally minimum, the first element is picked.
-    /// 
-    /// Returns a `HashMap` associating the key of each group with the minimum of that group's elements.
-    /// 
+    ///
+    /// Returns a `HashMap` associating the key of each group with the minimum
+    /// of that group's elements.
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .min_by(|_key, x, y| y.cmp(x));
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 12);
     /// assert_eq!(lookup[&1], 7);
     /// assert_eq!(lookup[&2], 8);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn min_by<F>(self, mut compare: F) -> HashMap<K, V>
-        where F: FnMut(&K, &V, &V) -> Ordering,
+    where
+        F: FnMut(&K, &V, &V) -> Ordering,
     {
-        self.fold_first(|acc, key, val| match compare(key, &acc, &val) {
+        self.reduce(|acc, key, val| match compare(key, &acc, &val) {
             Ordering::Less | Ordering::Equal => acc,
-            Ordering::Greater => val
+            Ordering::Greater => val,
         })
     }
 
-    /// Groups elements from the `GroupingMap` source by key and finds the element of each group
-    /// that gives the minimum from the specified function.
-    /// 
+    /// Groups elements from the `GroupingMap` source by key and finds the
+    /// element of each group that gives the minimum from the specified
+    /// function.
+    ///
     /// If several elements are equally minimum, the first element is picked.
-    /// 
-    /// Returns a `HashMap` associating the key of each group with the minimum of that group's elements.
-    /// 
+    ///
+    /// Returns a `HashMap` associating the key of each group with the minimum
+    /// of that group's elements.
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .min_by_key(|_key, &val| val % 4);
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 12);
     /// assert_eq!(lookup[&1], 4);
     /// assert_eq!(lookup[&2], 8);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn min_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
-        where F: FnMut(&K, &V) -> CK,
-              CK: Ord,
+    where
+        F: FnMut(&K, &V) -> CK,
+        CK: Ord,
     {
         self.min_by(|key, v1, v2| f(key, v1).cmp(&f(key, v2)))
     }
 
-    /// Groups elements from the `GroupingMap` source by key and find the maximum and minimum of
-    /// each group.
-    /// 
+    /// Groups elements from the `GroupingMap` source by key and find the
+    /// maximum and minimum of each group.
+    ///
     /// If several elements are equally maximum, the last element is picked.
     /// If several elements are equally minimum, the first element is picked.
-    /// 
-    /// See [.minmax()](crate::Itertools::minmax) for the non-grouping version.
-    /// 
+    ///
+    /// See [`Itertools::minmax`](crate::Itertools::minmax) for the non-grouping
+    /// version.
+    ///
     /// Differences from the non grouping version:
     /// - It never produces a `MinMaxResult::NoElements`
     /// - It doesn't have any speedup
-    /// 
-    /// Returns a `HashMap` associating the key of each group with the minimum and maximum of that group's elements.
-    /// 
+    ///
+    /// Returns a `HashMap` associating the key of each group with the minimum
+    /// and maximum of that group's elements.
+    ///
     /// ```
     /// use itertools::Itertools;
     /// use itertools::MinMaxResult::{OneElement, MinMax};
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .minmax();
-    /// 
+    ///
     /// assert_eq!(lookup[&0], MinMax(3, 12));
     /// assert_eq!(lookup[&1], MinMax(1, 7));
     /// assert_eq!(lookup[&2], OneElement(5));
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn minmax(self) -> HashMap<K, MinMaxResult<V>>
-        where V: Ord,
+    where
+        V: Ord,
     {
         self.minmax_by(|_, v1, v2| V::cmp(v1, v2))
     }
 
-    /// Groups elements from the `GroupingMap` source by key and find the maximum and minimum of
-    /// each group with respect to the specified comparison function.
-    /// 
+    /// Groups elements from the `GroupingMap` source by key and find the
+    /// maximum and minimum of each group with respect to the specified
+    /// comparison function.
+    ///
     /// If several elements are equally maximum, the last element is picked.
     /// If several elements are equally minimum, the first element is picked.
-    /// 
+    ///
     /// It has the same differences from the non-grouping version as `minmax`.
-    /// 
-    /// Returns a `HashMap` associating the key of each group with the minimum and maximum of that group's elements.
-    /// 
+    ///
+    /// Returns a `HashMap` associating the key of each group with the minimum
+    /// and maximum of that group's elements.
+    ///
     /// ```
     /// use itertools::Itertools;
     /// use itertools::MinMaxResult::{OneElement, MinMax};
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .minmax_by(|_key, x, y| y.cmp(x));
-    /// 
+    ///
     /// assert_eq!(lookup[&0], MinMax(12, 3));
     /// assert_eq!(lookup[&1], MinMax(7, 1));
     /// assert_eq!(lookup[&2], OneElement(5));
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn minmax_by<F>(self, mut compare: F) -> HashMap<K, MinMaxResult<V>>
-        where F: FnMut(&K, &V, &V) -> Ordering,
+    where
+        F: FnMut(&K, &V, &V) -> Ordering,
     {
         self.aggregate(|acc, key, val| {
             Some(match acc {
@@ -453,83 +556,92 @@
         })
     }
 
-    /// Groups elements from the `GroupingMap` source by key and find the elements of each group
-    /// that gives the minimum and maximum from the specified function.
-    /// 
+    /// Groups elements from the `GroupingMap` source by key and find the
+    /// elements of each group that gives the minimum and maximum from the
+    /// specified function.
+    ///
     /// If several elements are equally maximum, the last element is picked.
     /// If several elements are equally minimum, the first element is picked.
-    /// 
+    ///
     /// It has the same differences from the non-grouping version as `minmax`.
-    /// 
-    /// Returns a `HashMap` associating the key of each group with the minimum and maximum of that group's elements.
-    /// 
+    ///
+    /// Returns a `HashMap` associating the key of each group with the minimum
+    /// and maximum of that group's elements.
+    ///
     /// ```
     /// use itertools::Itertools;
     /// use itertools::MinMaxResult::{OneElement, MinMax};
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .minmax_by_key(|_key, &val| val % 4);
-    /// 
+    ///
     /// assert_eq!(lookup[&0], MinMax(12, 3));
     /// assert_eq!(lookup[&1], MinMax(4, 7));
     /// assert_eq!(lookup[&2], OneElement(5));
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn minmax_by_key<F, CK>(self, mut f: F) -> HashMap<K, MinMaxResult<V>>
-        where F: FnMut(&K, &V) -> CK,
-              CK: Ord,
+    where
+        F: FnMut(&K, &V) -> CK,
+        CK: Ord,
     {
         self.minmax_by(|key, v1, v2| f(key, v1).cmp(&f(key, v2)))
     }
-    
+
     /// Groups elements from the `GroupingMap` source by key and sums them.
-    /// 
-    /// This is just a shorthand for `self.fold_first(|acc, _, val| acc + val)`.
-    /// It is more limited than `Iterator::sum` since it doesn't use the `Sum` trait.
-    /// 
-    /// Returns a `HashMap` associating the key of each group with the sum of that group's elements.
-    /// 
+    ///
+    /// This is just a shorthand for `self.reduce(|acc, _, val| acc + val)`.
+    /// It is more limited than `Iterator::sum` since it doesn't use the `Sum`
+    /// trait.
+    ///
+    /// Returns a `HashMap` associating the key of each group with the sum of
+    /// that group's elements.
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .sum();
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 3 + 9 + 12);
     /// assert_eq!(lookup[&1], 1 + 4 + 7);
     /// assert_eq!(lookup[&2], 5 + 8);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn sum(self) -> HashMap<K, V>
-        where V: Add<V, Output = V>
+    where
+        V: Add<V, Output = V>,
     {
-        self.fold_first(|acc, _, val| acc + val)
+        self.reduce(|acc, _, val| acc + val)
     }
 
     /// Groups elements from the `GroupingMap` source by key and multiply them.
-    /// 
-    /// This is just a shorthand for `self.fold_first(|acc, _, val| acc * val)`.
-    /// It is more limited than `Iterator::product` since it doesn't use the `Product` trait.
-    /// 
-    /// Returns a `HashMap` associating the key of each group with the product of that group's elements.
-    /// 
+    ///
+    /// This is just a shorthand for `self.reduce(|acc, _, val| acc * val)`.
+    /// It is more limited than `Iterator::product` since it doesn't use the
+    /// `Product` trait.
+    ///
+    /// Returns a `HashMap` associating the key of each group with the product
+    /// of that group's elements.
+    ///
     /// ```
     /// use itertools::Itertools;
-    /// 
+    ///
     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
     ///     .into_grouping_map_by(|&n| n % 3)
     ///     .product();
-    /// 
+    ///
     /// assert_eq!(lookup[&0], 3 * 9 * 12);
     /// assert_eq!(lookup[&1], 1 * 4 * 7);
     /// assert_eq!(lookup[&2], 5 * 8);
     /// assert_eq!(lookup.len(), 3);
     /// ```
     pub fn product(self) -> HashMap<K, V>
-        where V: Mul<V, Output = V>,
+    where
+        V: Mul<V, Output = V>,
     {
-        self.fold_first(|acc, _, val| acc * val)
+        self.reduce(|acc, _, val| acc * val)
     }
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/impl_macros.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/impl_macros.rs
similarity index 87%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/impl_macros.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/impl_macros.rs
index a029843..3db5ba0 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/impl_macros.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/impl_macros.rs
@@ -1,4 +1,4 @@
-//! 
+//!
 //! Implementation's internal macros
 
 macro_rules! debug_fmt_fields {
@@ -27,3 +27,8 @@
 macro_rules! ignore_ident{
     ($id:ident, $($t:tt)*) => {$($t)*};
 }
+
+macro_rules! count_ident {
+    () => {0};
+    ($i0:ident $($i:ident)*) => {1 + count_ident!($($i)*)};
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/intersperse.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/intersperse.rs
new file mode 100644
index 0000000..36277db0
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/intersperse.rs
@@ -0,0 +1,132 @@
+use super::size_hint;
+use std::iter::{Fuse, FusedIterator};
+
+pub trait IntersperseElement<Item> {
+    fn generate(&mut self) -> Item;
+}
+
+#[derive(Debug, Clone)]
+pub struct IntersperseElementSimple<Item>(Item);
+
+impl<Item: Clone> IntersperseElement<Item> for IntersperseElementSimple<Item> {
+    fn generate(&mut self) -> Item {
+        self.0.clone()
+    }
+}
+
+/// An iterator adaptor to insert a particular value
+/// between each element of the adapted iterator.
+///
+/// Iterator element type is `I::Item`
+///
+/// This iterator is *fused*.
+///
+/// See [`.intersperse()`](crate::Itertools::intersperse) for more information.
+pub type Intersperse<I> = IntersperseWith<I, IntersperseElementSimple<<I as Iterator>::Item>>;
+
+/// Create a new Intersperse iterator
+pub fn intersperse<I>(iter: I, elt: I::Item) -> Intersperse<I>
+where
+    I: Iterator,
+{
+    intersperse_with(iter, IntersperseElementSimple(elt))
+}
+
+impl<Item, F: FnMut() -> Item> IntersperseElement<Item> for F {
+    fn generate(&mut self) -> Item {
+        self()
+    }
+}
+
+/// An iterator adaptor to insert a particular value created by a function
+/// between each element of the adapted iterator.
+///
+/// Iterator element type is `I::Item`
+///
+/// This iterator is *fused*.
+///
+/// See [`.intersperse_with()`](crate::Itertools::intersperse_with) for more
+/// information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[derive(Clone, Debug)]
+pub struct IntersperseWith<I, ElemF>
+where
+    I: Iterator,
+{
+    element: ElemF,
+    iter: Fuse<I>,
+    /// `peek` is None while no item have been taken out of `iter` (at
+    /// definition). Then `peek` will alternatively be `Some(None)` and
+    /// `Some(Some(item))`, where `None` indicates it's time to generate
+    /// from `element` (unless `iter` is empty).
+    peek: Option<Option<I::Item>>,
+}
+
+/// Create a new `IntersperseWith` iterator
+pub fn intersperse_with<I, ElemF>(iter: I, elt: ElemF) -> IntersperseWith<I, ElemF>
+where
+    I: Iterator,
+{
+    IntersperseWith { peek: None, iter: iter.fuse(), element: elt }
+}
+
+impl<I, ElemF> Iterator for IntersperseWith<I, ElemF>
+where
+    I: Iterator,
+    ElemF: IntersperseElement<I::Item>,
+{
+    type Item = I::Item;
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        let Self { element, iter, peek } = self;
+        match peek {
+            Some(item @ Some(_)) => item.take(),
+            Some(None) => match iter.next() {
+                new @ Some(_) => {
+                    *peek = Some(new);
+                    Some(element.generate())
+                }
+                None => None,
+            },
+            None => {
+                *peek = Some(None);
+                iter.next()
+            }
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let mut sh = self.iter.size_hint();
+        sh = size_hint::add(sh, sh);
+        match self.peek {
+            Some(Some(_)) => size_hint::add_scalar(sh, 1),
+            Some(None) => sh,
+            None => size_hint::sub_scalar(sh, 1),
+        }
+    }
+
+    fn fold<B, F>(self, init: B, mut f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let Self { mut element, mut iter, peek } = self;
+        let mut accum = init;
+
+        if let Some(x) = peek.unwrap_or_else(|| iter.next()) {
+            accum = f(accum, x);
+        }
+
+        iter.fold(accum, |accum, x| {
+            let accum = f(accum, element.generate());
+            f(accum, x)
+        })
+    }
+}
+
+impl<I, ElemF> FusedIterator for IntersperseWith<I, ElemF>
+where
+    I: Iterator,
+    ElemF: IntersperseElement<I::Item>,
+{
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/iter_index.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/iter_index.rs
new file mode 100644
index 0000000..aadaa72
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/iter_index.rs
@@ -0,0 +1,116 @@
+use core::iter::{Skip, Take};
+use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
+
+#[cfg(doc)]
+use crate::Itertools;
+
+mod private_iter_index {
+    use core::ops;
+
+    pub trait Sealed {}
+
+    impl Sealed for ops::Range<usize> {}
+    impl Sealed for ops::RangeInclusive<usize> {}
+    impl Sealed for ops::RangeTo<usize> {}
+    impl Sealed for ops::RangeToInclusive<usize> {}
+    impl Sealed for ops::RangeFrom<usize> {}
+    impl Sealed for ops::RangeFull {}
+}
+
+/// Used by [`Itertools::get`] to know which iterator
+/// to turn different ranges into.
+pub trait IteratorIndex<I>: private_iter_index::Sealed
+where
+    I: Iterator,
+{
+    /// The type returned for this type of index.
+    type Output: Iterator<Item = I::Item>;
+
+    /// Returns an adapted iterator for the current index.
+    ///
+    /// Prefer calling [`Itertools::get`] instead
+    /// of calling this directly.
+    fn index(self, from: I) -> Self::Output;
+}
+
+impl<I> IteratorIndex<I> for Range<usize>
+where
+    I: Iterator,
+{
+    type Output = Skip<Take<I>>;
+
+    fn index(self, iter: I) -> Self::Output {
+        iter.take(self.end).skip(self.start)
+    }
+}
+
+impl<I> IteratorIndex<I> for RangeInclusive<usize>
+where
+    I: Iterator,
+{
+    type Output = Take<Skip<I>>;
+
+    fn index(self, iter: I) -> Self::Output {
+        // end - start + 1 without overflowing if possible
+        let length = if *self.end() == usize::MAX {
+            assert_ne!(*self.start(), 0);
+            self.end() - self.start() + 1
+        } else {
+            (self.end() + 1).saturating_sub(*self.start())
+        };
+        iter.skip(*self.start()).take(length)
+    }
+}
+
+impl<I> IteratorIndex<I> for RangeTo<usize>
+where
+    I: Iterator,
+{
+    type Output = Take<I>;
+
+    fn index(self, iter: I) -> Self::Output {
+        iter.take(self.end)
+    }
+}
+
+impl<I> IteratorIndex<I> for RangeToInclusive<usize>
+where
+    I: Iterator,
+{
+    type Output = Take<I>;
+
+    fn index(self, iter: I) -> Self::Output {
+        assert_ne!(self.end, usize::MAX);
+        iter.take(self.end + 1)
+    }
+}
+
+impl<I> IteratorIndex<I> for RangeFrom<usize>
+where
+    I: Iterator,
+{
+    type Output = Skip<I>;
+
+    fn index(self, iter: I) -> Self::Output {
+        iter.skip(self.start)
+    }
+}
+
+impl<I> IteratorIndex<I> for RangeFull
+where
+    I: Iterator,
+{
+    type Output = I;
+
+    fn index(self, iter: I) -> Self::Output {
+        iter
+    }
+}
+
+pub fn get<I, R>(iter: I, index: R) -> R::Output
+where
+    I: IntoIterator,
+    R: IteratorIndex<I::IntoIter>,
+{
+    index.index(iter.into_iter())
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/k_smallest.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/k_smallest.rs
new file mode 100644
index 0000000..c1ae1986
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/k_smallest.rs
@@ -0,0 +1,144 @@
+use alloc::vec::Vec;
+use core::cmp::Ordering;
+
+/// Consumes a given iterator, returning the minimum elements in **ascending**
+/// order.
+pub(crate) fn k_smallest_general<I, F>(iter: I, k: usize, mut comparator: F) -> Vec<I::Item>
+where
+    I: Iterator,
+    F: FnMut(&I::Item, &I::Item) -> Ordering,
+{
+    /// Sift the element currently at `origin` away from the root until it is
+    /// properly ordered.
+    ///
+    /// This will leave **larger** elements closer to the root of the heap.
+    fn sift_down<T, F>(heap: &mut [T], is_less_than: &mut F, mut origin: usize)
+    where
+        F: FnMut(&T, &T) -> bool,
+    {
+        #[inline]
+        fn children_of(n: usize) -> (usize, usize) {
+            (2 * n + 1, 2 * n + 2)
+        }
+
+        while origin < heap.len() {
+            let (left_idx, right_idx) = children_of(origin);
+            if left_idx >= heap.len() {
+                return;
+            }
+
+            let replacement_idx =
+                if right_idx < heap.len() && is_less_than(&heap[left_idx], &heap[right_idx]) {
+                    right_idx
+                } else {
+                    left_idx
+                };
+
+            if is_less_than(&heap[origin], &heap[replacement_idx]) {
+                heap.swap(origin, replacement_idx);
+                origin = replacement_idx;
+            } else {
+                return;
+            }
+        }
+    }
+
+    if k == 0 {
+        iter.last();
+        return Vec::new();
+    }
+    if k == 1 {
+        return iter.min_by(comparator).into_iter().collect();
+    }
+    let mut iter = iter.fuse();
+    let mut storage: Vec<I::Item> = iter.by_ref().take(k).collect();
+
+    let mut is_less_than = move |a: &_, b: &_| comparator(a, b) == Ordering::Less;
+
+    // Rearrange the storage into a valid heap by reordering from the
+    // second-bottom-most layer up to the root. Slightly faster than ordering on
+    // each insert, but only by a factor of lg(k). The resulting heap has the
+    // **largest** item on top.
+    for i in (0..=(storage.len() / 2)).rev() {
+        sift_down(&mut storage, &mut is_less_than, i);
+    }
+
+    iter.for_each(|val| {
+        debug_assert_eq!(storage.len(), k);
+        if is_less_than(&val, &storage[0]) {
+            // Treating this as an push-and-pop saves having to write a sift-up
+            // implementation. https://en.wikipedia.org/wiki/Binary_heap#Insert_then_extract
+            storage[0] = val;
+            // We retain the smallest items we've seen so far, but ordered largest first so
+            // we can drop the largest efficiently.
+            sift_down(&mut storage, &mut is_less_than, 0);
+        }
+    });
+
+    // Ultimately the items need to be in least-first, strict order, but the heap is
+    // currently largest-first. To achieve this, repeatedly,
+    // 1) "pop" the largest item off the heap into the tail slot of the underlying
+    //    storage,
+    // 2) shrink the logical size of the heap by 1,
+    // 3) restore the heap property over the remaining items.
+    let mut heap = &mut storage[..];
+    while heap.len() > 1 {
+        let last_idx = heap.len() - 1;
+        heap.swap(0, last_idx);
+        // Sifting over a truncated slice means that the sifting will not disturb
+        // already popped elements.
+        heap = &mut heap[..last_idx];
+        sift_down(heap, &mut is_less_than, 0);
+    }
+
+    storage
+}
+
+pub(crate) fn k_smallest_relaxed_general<I, F>(iter: I, k: usize, mut comparator: F) -> Vec<I::Item>
+where
+    I: Iterator,
+    F: FnMut(&I::Item, &I::Item) -> Ordering,
+{
+    if k == 0 {
+        iter.last();
+        return Vec::new();
+    }
+
+    let mut iter = iter.fuse();
+    let mut buf = iter.by_ref().take(2 * k).collect::<Vec<_>>();
+
+    if buf.len() < k {
+        buf.sort_unstable_by(&mut comparator);
+        return buf;
+    }
+
+    buf.select_nth_unstable_by(k - 1, &mut comparator);
+    buf.truncate(k);
+
+    iter.for_each(|val| {
+        if comparator(&val, &buf[k - 1]) != Ordering::Less {
+            return;
+        }
+
+        assert_ne!(buf.len(), buf.capacity());
+        buf.push(val);
+
+        if buf.len() == 2 * k {
+            buf.select_nth_unstable_by(k - 1, &mut comparator);
+            buf.truncate(k);
+        }
+    });
+
+    buf.sort_unstable_by(&mut comparator);
+    buf.truncate(k);
+    buf
+}
+
+#[inline]
+pub(crate) fn key_to_cmp<T, K, F>(mut key: F) -> impl FnMut(&T, &T) -> Ordering
+where
+    F: FnMut(&T) -> K,
+    K: Ord,
+{
+    move |a, b| key(a).cmp(&key(b))
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/kmerge_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/kmerge_impl.rs
similarity index 69%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/kmerge_impl.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/kmerge_impl.rs
index 509d5fc..c3e6fdc 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/kmerge_impl.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/kmerge_impl.rs
@@ -1,41 +1,39 @@
 use crate::size_hint;
-use crate::Itertools;
 
 use alloc::vec::Vec;
+use std::fmt;
 use std::iter::FusedIterator;
 use std::mem::replace;
-use std::fmt;
 
 /// Head element and Tail iterator pair
 ///
-/// `PartialEq`, `Eq`, `PartialOrd` and `Ord` are implemented by comparing sequences based on
-/// first items (which are guaranteed to exist).
+/// `PartialEq`, `Eq`, `PartialOrd` and `Ord` are implemented by comparing
+/// sequences based on first items (which are guaranteed to exist).
 ///
-/// The meanings of `PartialOrd` and `Ord` are reversed so as to turn the heap used in
-/// `KMerge` into a min-heap.
+/// The meanings of `PartialOrd` and `Ord` are reversed so as to turn the heap
+/// used in `KMerge` into a min-heap.
 #[derive(Debug)]
 struct HeadTail<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
     head: I::Item,
     tail: I,
 }
 
 impl<I> HeadTail<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
-    /// Constructs a `HeadTail` from an `Iterator`. Returns `None` if the `Iterator` is empty.
-    fn new(mut it: I) -> Option<HeadTail<I>> {
+    /// Constructs a `HeadTail` from an `Iterator`. Returns `None` if the
+    /// `Iterator` is empty.
+    fn new(mut it: I) -> Option<Self> {
         let head = it.next();
-        head.map(|h| {
-            HeadTail {
-                head: h,
-                tail: it,
-            }
-        })
+        head.map(|h| Self { head: h, tail: it })
     }
 
-    /// Get the next element and update `head`, returning the old head in `Some`.
+    /// Get the next element and update `head`, returning the old head in
+    /// `Some`.
     ///
     /// Returns `None` when the tail is exhausted (only `head` then remains).
     fn next(&mut self) -> Option<I::Item> {
@@ -53,15 +51,17 @@
 }
 
 impl<I> Clone for HeadTail<I>
-    where I: Iterator + Clone,
-          I::Item: Clone
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
 {
     clone_fields!(head, tail);
 }
 
 /// Make `data` a heap (min-heap w.r.t the sorting).
 fn heapify<T, S>(data: &mut [T], mut less_than: S)
-    where S: FnMut(&T, &T) -> bool
+where
+    S: FnMut(&T, &T) -> bool,
 {
     for i in (0..data.len() / 2).rev() {
         sift_down(data, i, &mut less_than);
@@ -70,7 +70,8 @@
 
 /// Sift down element at `index` (`heap` is a min-heap wrt the ordering)
 fn sift_down<T, S>(heap: &mut [T], index: usize, mut less_than: S)
-    where S: FnMut(&T, &T) -> bool
+where
+    S: FnMut(&T, &T) -> bool,
 {
     debug_assert!(index <= heap.len());
     let mut pos = index;
@@ -81,7 +82,7 @@
     while child + 1 < heap.len() {
         // pick the smaller of the two children
         // use arithmetic to avoid an unpredictable branch
-        child += less_than(&heap[child+1], &heap[child]) as usize;
+        child += less_than(&heap[child + 1], &heap[child]) as usize;
 
         // sift down is done if we are already in order
         if !less_than(&heap[child], &heap[pos]) {
@@ -98,8 +99,9 @@
     }
 }
 
-/// An iterator adaptor that merges an abitrary number of base iterators in ascending order.
-/// If all base iterators are sorted (ascending), the result is sorted.
+/// An iterator adaptor that merges an abitrary number of base iterators in
+/// ascending order. If all base iterators are sorted (ascending), the result is
+/// sorted.
 ///
 /// Iterator element type is `I::Item`.
 ///
@@ -119,7 +121,7 @@
     }
 }
 
-impl<T, F: FnMut(&T, &T)->bool> KMergePredicate<T> for F {
+impl<T, F: FnMut(&T, &T) -> bool> KMergePredicate<T> for F {
     fn kmerge_pred(&mut self, a: &T, b: &T) -> bool {
         self(a, b)
     }
@@ -128,19 +130,22 @@
 /// Create an iterator that merges elements of the contained iterators using
 /// the ordering function.
 ///
-/// [`IntoIterator`] enabled version of [`Itertools::kmerge`].
+/// [`IntoIterator`] enabled version of
+/// [`Itertools::kmerge`](crate::Itertools::kmerge).
 ///
 /// ```
 /// use itertools::kmerge;
 ///
 /// for elt in kmerge(vec![vec![0, 2, 4], vec![1, 3, 5], vec![6, 7]]) {
 ///     /* loop body */
+///     # let _ = elt;
 /// }
 /// ```
 pub fn kmerge<I>(iterable: I) -> KMerge<<I::Item as IntoIterator>::IntoIter>
-    where I: IntoIterator,
-          I::Item: IntoIterator,
-          <<I as IntoIterator>::Item as IntoIterator>::Item: PartialOrd
+where
+    I: IntoIterator,
+    I::Item: IntoIterator,
+    <<I as IntoIterator>::Item as IntoIterator>::Item: PartialOrd,
 {
     kmerge_by(iterable, KMergeByLt)
 }
@@ -152,29 +157,35 @@
 ///
 /// See [`.kmerge_by()`](crate::Itertools::kmerge_by) for more
 /// information.
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "this iterator adaptor is not lazy but does nearly nothing unless consumed"]
 pub struct KMergeBy<I, F>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     heap: Vec<HeadTail<I>>,
     less_than: F,
 }
 
 impl<I, F> fmt::Debug for KMergeBy<I, F>
-    where I: Iterator + fmt::Debug,
-          I::Item: fmt::Debug,
+where
+    I: Iterator + fmt::Debug,
+    I::Item: fmt::Debug,
 {
     debug_fmt_fields!(KMergeBy, heap);
 }
 
 /// Create an iterator that merges elements of the contained iterators.
 ///
-/// [`IntoIterator`] enabled version of [`Itertools::kmerge_by`].
-pub fn kmerge_by<I, F>(iterable: I, mut less_than: F)
-    -> KMergeBy<<I::Item as IntoIterator>::IntoIter, F>
-    where I: IntoIterator,
-          I::Item: IntoIterator,
-          F: KMergePredicate<<<I as IntoIterator>::Item as IntoIterator>::Item>,
+/// [`IntoIterator`] enabled version of
+/// [`Itertools::kmerge_by`](crate::Itertools::kmerge_by).
+pub fn kmerge_by<I, F>(
+    iterable: I,
+    mut less_than: F,
+) -> KMergeBy<<I::Item as IntoIterator>::IntoIter, F>
+where
+    I: IntoIterator,
+    I::Item: IntoIterator,
+    F: KMergePredicate<<<I as IntoIterator>::Item as IntoIterator>::Item>,
 {
     let iter = iterable.into_iter();
     let (lower, _) = iter.size_hint();
@@ -185,16 +196,18 @@
 }
 
 impl<I, F> Clone for KMergeBy<I, F>
-    where I: Iterator + Clone,
-          I::Item: Clone,
-          F: Clone,
+where
+    I: Iterator + Clone,
+    I::Item: Clone,
+    F: Clone,
 {
     clone_fields!(heap, less_than);
 }
 
 impl<I, F> Iterator for KMergeBy<I, F>
-    where I: Iterator,
-          F: KMergePredicate<I::Item>
+where
+    I: Iterator,
+    F: KMergePredicate<I::Item>,
 {
     type Item = I::Item;
 
@@ -202,26 +215,21 @@
         if self.heap.is_empty() {
             return None;
         }
-        let result = if let Some(next) = self.heap[0].next() {
-            next
-        } else {
-            self.heap.swap_remove(0).head
-        };
+        let result =
+            if let Some(next) = self.heap[0].next() { next } else { self.heap.swap_remove(0).head };
         let less_than = &mut self.less_than;
         sift_down(&mut self.heap, 0, |a, b| less_than.kmerge_pred(&a.head, &b.head));
         Some(result)
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
-        #[allow(deprecated)] //TODO: once msrv hits 1.51. replace `fold1` with `reduce`
-        self.heap.iter()
-                 .map(|i| i.size_hint())
-                 .fold1(size_hint::add)
-                 .unwrap_or((0, Some(0)))
+        self.heap.iter().map(|i| i.size_hint()).reduce(size_hint::add).unwrap_or((0, Some(0)))
     }
 }
 
 impl<I, F> FusedIterator for KMergeBy<I, F>
-    where I: Iterator,
-          F: KMergePredicate<I::Item>
-{}
+where
+    I: Iterator,
+    F: KMergePredicate<I::Item>,
+{
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lazy_buffer.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lazy_buffer.rs
new file mode 100644
index 0000000..38e344d
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lazy_buffer.rs
@@ -0,0 +1,76 @@
+use alloc::vec::Vec;
+use std::iter::Fuse;
+use std::ops::Index;
+
+use crate::size_hint::{self, SizeHint};
+
+#[derive(Debug, Clone)]
+pub struct LazyBuffer<I: Iterator> {
+    it: Fuse<I>,
+    buffer: Vec<I::Item>,
+}
+
+impl<I> LazyBuffer<I>
+where
+    I: Iterator,
+{
+    pub fn new(it: I) -> Self {
+        Self { it: it.fuse(), buffer: Vec::new() }
+    }
+
+    pub fn len(&self) -> usize {
+        self.buffer.len()
+    }
+
+    pub fn size_hint(&self) -> SizeHint {
+        size_hint::add_scalar(self.it.size_hint(), self.len())
+    }
+
+    pub fn count(self) -> usize {
+        self.len() + self.it.count()
+    }
+
+    pub fn get_next(&mut self) -> bool {
+        if let Some(x) = self.it.next() {
+            self.buffer.push(x);
+            true
+        } else {
+            false
+        }
+    }
+
+    pub fn prefill(&mut self, len: usize) {
+        let buffer_len = self.buffer.len();
+        if len > buffer_len {
+            let delta = len - buffer_len;
+            self.buffer.extend(self.it.by_ref().take(delta));
+        }
+    }
+}
+
+impl<I> LazyBuffer<I>
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+    pub fn get_at(&self, indices: &[usize]) -> Vec<I::Item> {
+        indices.iter().map(|i| self.buffer[*i].clone()).collect()
+    }
+
+    pub fn get_array<const K: usize>(&self, indices: [usize; K]) -> [I::Item; K] {
+        indices.map(|i| self.buffer[i].clone())
+    }
+}
+
+impl<I, J> Index<J> for LazyBuffer<I>
+where
+    I: Iterator,
+    I::Item: Sized,
+    Vec<I::Item>: Index<J>,
+{
+    type Output = <Vec<I::Item> as Index<J>>::Output;
+
+    fn index(&self, index: J) -> &Self::Output {
+        self.buffer.index(index)
+    }
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lib.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lib.rs
similarity index 68%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lib.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lib.rs
index c23a65d..7c9f5325 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lib.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lib.rs
@@ -1,6 +1,7 @@
-#![warn(missing_docs)]
-#![crate_name="itertools"]
+#![warn(missing_docs, clippy::default_numeric_fallback)]
+#![crate_name = "itertools"]
 #![cfg_attr(not(feature = "use_std"), no_std)]
+#![doc(test(attr(deny(warnings), allow(deprecated, unstable_name_collisions))))]
 
 //! Extra iterator adaptors, functions and macros.
 //!
@@ -8,6 +9,7 @@
 //! the [`Itertools`] trait:
 //!
 //! ```
+//! # #[allow(unused_imports)]
 //! use itertools::Itertools;
 //! ```
 //!
@@ -29,6 +31,7 @@
 //!
 //! for elt in interleave(&[1, 2, 3], &[2, 3, 4]) {
 //!     /* loop body */
+//!     # let _ = elt;
 //! }
 //! ```
 //!
@@ -36,14 +39,17 @@
 //!
 //! - `use_std`
 //!   - Enabled by default.
-//!   - Disable to compile itertools using `#![no_std]`. This disables
-//!     any items that depend on collections (like `group_by`, `unique`,
-//!     `kmerge`, `join` and many more).
+//!   - Disable to compile itertools using `#![no_std]`. This disables any item
+//!     that depend on allocations (see the `use_alloc` feature) and hash maps
+//!     (like `unique`, `counts`, `into_grouping_map` and more).
+//! - `use_alloc`
+//!   - Enabled by default.
+//!   - Enables any item that depend on allocations (like `chunk_by`, `kmerge`,
+//!     `join` and many more).
 //!
 //! ## Rust Version
 //!
-//! This version of itertools requires Rust 1.32 or later.
-#![doc(html_root_url="https://docs.rs/itertools/0.8/")]
+//! This version of itertools requires Rust 1.63.0 or later.
 
 #[cfg(not(feature = "use_std"))]
 extern crate core as std;
@@ -52,28 +58,26 @@
 extern crate alloc;
 
 #[cfg(feature = "use_alloc")]
-use alloc::{
-    string::String,
-    vec::Vec,
-};
+use alloc::{collections::VecDeque, string::String, vec::Vec};
 
 pub use either::Either;
 
 use core::borrow::Borrow;
+use std::cmp::Ordering;
 #[cfg(feature = "use_std")]
 use std::collections::HashMap;
-use std::iter::{IntoIterator, once};
-use std::cmp::Ordering;
-use std::fmt;
 #[cfg(feature = "use_std")]
 use std::collections::HashSet;
-#[cfg(feature = "use_std")]
-use std::hash::Hash;
+use std::fmt;
 #[cfg(feature = "use_alloc")]
 use std::fmt::Write;
+#[cfg(feature = "use_std")]
+use std::hash::Hash;
+use std::iter::{once, IntoIterator};
+#[cfg(feature = "use_alloc")]
+type VecDequeIntoIter<T> = alloc::collections::vec_deque::IntoIter<T>;
 #[cfg(feature = "use_alloc")]
 type VecIntoIter<T> = alloc::vec::IntoIter<T>;
-#[cfg(feature = "use_alloc")]
 use std::iter::FromIterator;
 
 #[macro_use]
@@ -85,73 +89,56 @@
 
 /// The concrete iterator types.
 pub mod structs {
-    pub use crate::adaptors::{
-        Dedup,
-        DedupBy,
-        DedupWithCount,
-        DedupByWithCount,
-        Interleave,
-        InterleaveShortest,
-        FilterMapOk,
-        FilterOk,
-        Product,
-        PutBack,
-        Batching,
-        MapInto,
-        MapOk,
-        Merge,
-        MergeBy,
-        TakeWhileRef,
-        WhileSome,
-        Coalesce,
-        TupleCombinations,
-        Positions,
-        Update,
-    };
-    #[allow(deprecated)]
-    pub use crate::adaptors::{MapResults, Step};
     #[cfg(feature = "use_alloc")]
     pub use crate::adaptors::MultiProduct;
+    pub use crate::adaptors::{
+        Batching, Coalesce, Dedup, DedupBy, DedupByWithCount, DedupWithCount, FilterMapOk,
+        FilterOk, Interleave, InterleaveShortest, MapInto, MapOk, Positions, Product, PutBack,
+        TakeWhileRef, TupleCombinations, Update, WhileSome,
+    };
     #[cfg(feature = "use_alloc")]
-    pub use crate::combinations::Combinations;
+    pub use crate::combinations::{ArrayCombinations, Combinations};
     #[cfg(feature = "use_alloc")]
     pub use crate::combinations_with_replacement::CombinationsWithReplacement;
     pub use crate::cons_tuples_impl::ConsTuples;
+    #[cfg(feature = "use_std")]
+    pub use crate::duplicates_impl::{Duplicates, DuplicatesBy};
     pub use crate::exactly_one_err::ExactlyOneError;
-    pub use crate::format::{Format, FormatWith};
     pub use crate::flatten_ok::FlattenOk;
+    pub use crate::format::{Format, FormatWith};
+    #[allow(deprecated)]
+    #[cfg(feature = "use_alloc")]
+    pub use crate::groupbylazy::GroupBy;
+    #[cfg(feature = "use_alloc")]
+    pub use crate::groupbylazy::{Chunk, ChunkBy, Chunks, Group, Groups, IntoChunks};
     #[cfg(feature = "use_std")]
     pub use crate::grouping_map::{GroupingMap, GroupingMapBy};
-    #[cfg(feature = "use_alloc")]
-    pub use crate::groupbylazy::{IntoChunks, Chunk, Chunks, GroupBy, Group, Groups};
     pub use crate::intersperse::{Intersperse, IntersperseWith};
     #[cfg(feature = "use_alloc")]
     pub use crate::kmerge_impl::{KMerge, KMergeBy};
-    pub use crate::merge_join::MergeJoinBy;
+    pub use crate::merge_join::{Merge, MergeBy, MergeJoinBy};
     #[cfg(feature = "use_alloc")]
     pub use crate::multipeek_impl::MultiPeek;
+    pub use crate::pad_tail::PadUsing;
     #[cfg(feature = "use_alloc")]
     pub use crate::peek_nth::PeekNth;
-    pub use crate::pad_tail::PadUsing;
     pub use crate::peeking_take_while::PeekingTakeWhile;
     #[cfg(feature = "use_alloc")]
     pub use crate::permutations::Permutations;
-    pub use crate::process_results_impl::ProcessResults;
     #[cfg(feature = "use_alloc")]
     pub use crate::powerset::Powerset;
+    pub use crate::process_results_impl::ProcessResults;
     #[cfg(feature = "use_alloc")]
     pub use crate::put_back_n_impl::PutBackN;
     #[cfg(feature = "use_alloc")]
     pub use crate::rciter_impl::RcIter;
     pub use crate::repeatn::RepeatN;
     #[allow(deprecated)]
-    pub use crate::sources::{RepeatCall, Unfold, Iterate};
+    pub use crate::sources::{Iterate, Unfold};
     pub use crate::take_while_inclusive::TakeWhileInclusive;
     #[cfg(feature = "use_alloc")]
     pub use crate::tee::Tee;
-    pub use crate::tuple_impl::{TupleBuffer, TupleWindows, CircularTupleWindows, Tuples};
-    #[cfg(feature = "use_std")]
-    pub use crate::duplicates_impl::{Duplicates, DuplicatesBy};
+    pub use crate::tuple_impl::{CircularTupleWindows, TupleBuffer, TupleWindows, Tuples};
     #[cfg(feature = "use_std")]
     pub use crate::unique_impl::{Unique, UniqueBy};
     pub use crate::with_position::WithPosition;
@@ -162,25 +149,26 @@
 
 /// Traits helpful for using certain `Itertools` methods in generic contexts.
 pub mod traits {
+    pub use crate::iter_index::IteratorIndex;
     pub use crate::tuple_impl::HomogeneousTuple;
 }
 
-#[allow(deprecated)]
-pub use crate::structs::*;
 pub use crate::concat_impl::concat;
 pub use crate::cons_tuples_impl::cons_tuples;
 pub use crate::diff::diff_with;
 pub use crate::diff::Diff;
 #[cfg(feature = "use_alloc")]
-pub use crate::kmerge_impl::{kmerge_by};
+pub use crate::kmerge_impl::kmerge_by;
 pub use crate::minmax::MinMaxResult;
 pub use crate::peeking_take_while::PeekingNext;
 pub use crate::process_results_impl::process_results;
 pub use crate::repeatn::repeat_n;
 #[allow(deprecated)]
-pub use crate::sources::{repeat_call, unfold, iterate};
-pub use crate::with_position::Position;
+pub use crate::sources::{iterate, unfold};
+#[allow(deprecated)]
+pub use crate::structs::*;
 pub use crate::unziptuple::{multiunzip, MultiUnzip};
+pub use crate::with_position::Position;
 pub use crate::ziptuple::multizip;
 mod adaptors;
 mod either_or_both;
@@ -189,25 +177,28 @@
 pub mod free;
 #[doc(inline)]
 pub use crate::free::*;
-mod concat_impl;
-mod cons_tuples_impl;
 #[cfg(feature = "use_alloc")]
 mod combinations;
 #[cfg(feature = "use_alloc")]
 mod combinations_with_replacement;
-mod exactly_one_err;
+mod concat_impl;
+mod cons_tuples_impl;
 mod diff;
-mod flatten_ok;
 #[cfg(feature = "use_std")]
+mod duplicates_impl;
+mod exactly_one_err;
+#[cfg(feature = "use_alloc")]
 mod extrema_set;
+mod flatten_ok;
 mod format;
-#[cfg(feature = "use_std")]
-mod grouping_map;
 #[cfg(feature = "use_alloc")]
 mod group_map;
 #[cfg(feature = "use_alloc")]
 mod groupbylazy;
+#[cfg(feature = "use_std")]
+mod grouping_map;
 mod intersperse;
+mod iter_index;
 #[cfg(feature = "use_alloc")]
 mod k_smallest;
 #[cfg(feature = "use_alloc")]
@@ -218,6 +209,7 @@
 mod minmax;
 #[cfg(feature = "use_alloc")]
 mod multipeek_impl;
+mod next_array;
 mod pad_tail;
 #[cfg(feature = "use_alloc")]
 mod peek_nth;
@@ -239,8 +231,6 @@
 mod tee;
 mod tuple_impl;
 #[cfg(feature = "use_std")]
-mod duplicates_impl;
-#[cfg(feature = "use_std")]
 mod unique_impl;
 mod unziptuple;
 mod with_position;
@@ -252,7 +242,8 @@
 /// Create an iterator over the “cartesian product” of iterators.
 ///
 /// Iterator element type is like `(A, B, ..., E)` if formed
-/// from iterators `(I, J, ..., M)` with element types `I::Item = A`, `J::Item = B`, etc.
+/// from iterators `(I, J, ..., M)` with element types `I::Item = A`, `J::Item =
+/// B`, etc.
 ///
 /// ```
 /// # use itertools::iproduct;
@@ -262,6 +253,7 @@
 /// // from (0, 0, 0), (0, 0, 1), .., (0, 1, 0), (0, 1, 1), .. etc until (3, 3, 3)
 /// for (i, j, k) in iproduct!(0..4, 0..4, 0..4) {
 ///    // ..
+///    # let _ = (i, j, k);
 /// }
 /// # }
 /// ```
@@ -272,13 +264,22 @@
     (@flatten $I:expr, $J:expr, $($K:expr,)*) => (
         $crate::iproduct!(@flatten $crate::cons_tuples($crate::iproduct!($I, $J)), $($K,)*)
     );
-    ($I:expr) => (
-        $crate::__std_iter::IntoIterator::into_iter($I)
+    () => (
+        $crate::__std_iter::once(())
     );
-    ($I:expr, $J:expr) => (
-        $crate::Itertools::cartesian_product($crate::iproduct!($I), $crate::iproduct!($J))
+    ($I:expr $(,)?) => (
+        $crate::__std_iter::Iterator::map(
+            $crate::__std_iter::IntoIterator::into_iter($I),
+            |elt| (elt,)
+        )
     );
-    ($I:expr, $J:expr, $($K:expr),+) => (
+    ($I:expr, $J:expr $(,)?) => (
+        $crate::Itertools::cartesian_product(
+            $crate::__std_iter::IntoIterator::into_iter($I),
+            $crate::__std_iter::IntoIterator::into_iter($J),
+        )
+    );
+    ($I:expr, $J:expr, $($K:expr),+ $(,)?) => (
         $crate::iproduct!(@flatten $crate::iproduct!($I, $J), $($K,)+)
     );
 }
@@ -338,19 +339,24 @@
 
     // binary
     ($first:expr, $second:expr $(,)*) => {
-        $crate::izip!($first)
-            .zip($second)
+        $crate::__std_iter::Iterator::zip(
+            $crate::__std_iter::IntoIterator::into_iter($first),
+            $second,
+        )
     };
 
     // n-ary where n > 2
     ( $first:expr $( , $rest:expr )* $(,)* ) => {
-        $crate::izip!($first)
+        {
+            let iter = $crate::__std_iter::IntoIterator::into_iter($first);
             $(
-                .zip($rest)
+                let iter = $crate::__std_iter::Iterator::zip(iter, $rest);
             )*
-            .map(
+            $crate::__std_iter::Iterator::map(
+                iter,
                 $crate::izip!(@closure a => (a) $( , $rest )*)
             )
+        }
     };
 }
 
@@ -364,7 +370,8 @@
 ///
 /// # Examples
 ///
-/// Empty invocations of `chain!` expand to an invocation of [`std::iter::empty`]:
+/// Empty invocations of `chain!` expand to an invocation of
+/// [`std::iter::empty`]:
 /// ```
 /// use std::iter;
 /// use itertools::chain;
@@ -373,18 +380,20 @@
 /// let _: iter::Empty<i8> = chain!();
 /// ```
 ///
-/// Invocations of `chain!` with one argument expand to [`arg.into_iter()`](IntoIterator):
+/// Invocations of `chain!` with one argument expand to
+/// [`arg.into_iter()`](IntoIterator):
 /// ```
-/// use std::{ops::Range, slice};
+/// use std::ops::Range;
 /// use itertools::chain;
-/// let _: <Range<_> as IntoIterator>::IntoIter = chain!((2..6),); // trailing comma optional!
+/// let _: <Range<_> as IntoIterator>::IntoIter = chain!(2..6,); // trailing comma optional!
 /// let _:     <&[_] as IntoIterator>::IntoIter = chain!(&[2, 3, 4]);
 /// ```
 ///
-/// Invocations of `chain!` with multiple arguments [`.into_iter()`](IntoIterator) each
-/// argument, and then [`chain`] them together:
+/// Invocations of `chain!` with multiple arguments
+/// [`.into_iter()`](IntoIterator) each argument, and then [`chain`] them
+/// together:
 /// ```
-/// use std::{iter::*, ops::Range, slice};
+/// use std::{iter::*, slice};
 /// use itertools::{assert_equal, chain};
 ///
 /// // e.g., this:
@@ -401,16 +410,16 @@
 /// ```
 macro_rules! chain {
     () => {
-        core::iter::empty()
+        $crate::__std_iter::empty()
     };
     ($first:expr $(, $rest:expr )* $(,)?) => {
         {
-            let iter = core::iter::IntoIterator::into_iter($first);
+            let iter = $crate::__std_iter::IntoIterator::into_iter($first);
             $(
                 let iter =
-                    core::iter::Iterator::chain(
+                    $crate::__std_iter::Iterator::chain(
                         iter,
-                        core::iter::IntoIterator::into_iter($rest));
+                        $crate::__std_iter::IntoIterator::into_iter($rest));
             )*
             iter
         }
@@ -422,15 +431,15 @@
 ///
 /// This trait defines a number of methods. They are divided into two groups:
 ///
-/// * *Adaptors* take an iterator and parameter as input, and return
-/// a new iterator value. These are listed first in the trait. An example
-/// of an adaptor is [`.interleave()`](Itertools::interleave)
+/// * *Adaptors* take an iterator and parameter as input, and return a new
+///   iterator value. These are listed first in the trait. An example of an
+///   adaptor is [`.interleave()`](Itertools::interleave)
 ///
-/// * *Regular methods* are those that don't return iterators and instead
-/// return a regular value of some other kind.
-/// [`.next_tuple()`](Itertools::next_tuple) is an example and the first regular
-/// method in the list.
-pub trait Itertools : Iterator {
+/// * *Regular methods* are those that don't return iterators and instead return
+///   a regular value of some other kind.
+///   [`.next_tuple()`](Itertools::next_tuple) is an example and the first
+///   regular method in the list.
+pub trait Itertools: Iterator {
     // adaptors
 
     /// Alternate elements from two iterators until both have run out.
@@ -446,8 +455,9 @@
     /// itertools::assert_equal(it, vec![1, -1, 2, -2, 3, 4, 5, 6]);
     /// ```
     fn interleave<J>(self, other: J) -> Interleave<Self, J::IntoIter>
-        where J: IntoIterator<Item = Self::Item>,
-              Self: Sized
+    where
+        J: IntoIterator<Item = Self::Item>,
+        Self: Sized,
     {
         interleave(self, other)
     }
@@ -464,8 +474,9 @@
     /// itertools::assert_equal(it, vec![1, -1, 2, -2, 3]);
     /// ```
     fn interleave_shortest<J>(self, other: J) -> InterleaveShortest<Self, J::IntoIter>
-        where J: IntoIterator<Item = Self::Item>,
-              Self: Sized
+    where
+        J: IntoIterator<Item = Self::Item>,
+        Self: Sized,
     {
         adaptors::interleave_shortest(self, other.into_iter())
     }
@@ -483,8 +494,9 @@
     /// itertools::assert_equal((0..3).intersperse(8), vec![0, 8, 1, 8, 2]);
     /// ```
     fn intersperse(self, element: Self::Item) -> Intersperse<Self>
-        where Self: Sized,
-              Self::Item: Clone
+    where
+        Self: Sized,
+        Self::Item: Clone,
     {
         intersperse::intersperse(self, element)
     }
@@ -504,12 +516,66 @@
     /// assert_eq!(i, 8);
     /// ```
     fn intersperse_with<F>(self, element: F) -> IntersperseWith<Self, F>
-        where Self: Sized,
-        F: FnMut() -> Self::Item
+    where
+        Self: Sized,
+        F: FnMut() -> Self::Item,
     {
         intersperse::intersperse_with(self, element)
     }
 
+    /// Returns an iterator over a subsection of the iterator.
+    ///
+    /// Works similarly to [`slice::get`](https://doc.rust-lang.org/std/primitive.slice.html#method.get).
+    ///
+    /// **Panics** for ranges `..=usize::MAX` and `0..=usize::MAX`.
+    ///
+    /// It's a generalisation of [`Iterator::take`] and [`Iterator::skip`],
+    /// and uses these under the hood.
+    /// Therefore, the resulting iterator is:
+    /// - [`ExactSizeIterator`] if the adapted iterator is
+    ///   [`ExactSizeIterator`].
+    /// - [`DoubleEndedIterator`] if the adapted iterator is
+    ///   [`DoubleEndedIterator`] and [`ExactSizeIterator`].
+    ///
+    /// # Unspecified Behavior
+    /// The result of indexing with an exhausted [`core::ops::RangeInclusive`]
+    /// is unspecified.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let vec = vec![3, 1, 4, 1, 5];
+    ///
+    /// let mut range: Vec<_> =
+    ///         vec.iter().get(1..=3).copied().collect();
+    /// assert_eq!(&range, &[1, 4, 1]);
+    ///
+    /// // It works with other types of ranges, too
+    /// range = vec.iter().get(..2).copied().collect();
+    /// assert_eq!(&range, &[3, 1]);
+    ///
+    /// range = vec.iter().get(0..1).copied().collect();
+    /// assert_eq!(&range, &[3]);
+    ///
+    /// range = vec.iter().get(2..).copied().collect();
+    /// assert_eq!(&range, &[4, 1, 5]);
+    ///
+    /// range = vec.iter().get(..=2).copied().collect();
+    /// assert_eq!(&range, &[3, 1, 4]);
+    ///
+    /// range = vec.iter().get(..).copied().collect();
+    /// assert_eq!(range, vec);
+    /// ```
+    fn get<R>(self, index: R) -> R::Output
+    where
+        Self: Sized,
+        R: traits::IteratorIndex<Self>,
+    {
+        iter_index::get(self, index)
+    }
+
     /// Create an iterator which iterates over both this and the specified
     /// iterator simultaneously, yielding pairs of two optional elements.
     ///
@@ -518,8 +584,8 @@
     /// As long as neither input iterator is exhausted yet, it yields two values
     /// via `EitherOrBoth::Both`.
     ///
-    /// When the parameter iterator is exhausted, it only yields a value from the
-    /// `self` iterator via `EitherOrBoth::Left`.
+    /// When the parameter iterator is exhausted, it only yields a value from
+    /// the `self` iterator via `EitherOrBoth::Left`.
     ///
     /// When the `self` iterator is exhausted, it only yields a value from the
     /// parameter iterator via `EitherOrBoth::Right`.
@@ -538,8 +604,9 @@
     /// ```
     #[inline]
     fn zip_longest<J>(self, other: J) -> ZipLongest<Self, J::IntoIter>
-        where J: IntoIterator,
-              Self: Sized
+    where
+        J: IntoIterator,
+        Self: Sized,
     {
         zip_longest::zip_longest(self, other.into_iter())
     }
@@ -551,8 +618,9 @@
     /// lengths.
     #[inline]
     fn zip_eq<J>(self, other: J) -> ZipEq<Self, J::IntoIter>
-        where J: IntoIterator,
-              Self: Sized
+    where
+        J: IntoIterator,
+        Self: Sized,
     {
         zip_eq(self, other)
     }
@@ -579,10 +647,10 @@
     ///
     /// itertools::assert_equal(pit, vec![(0, 1), (2, 3)]);
     /// ```
-    ///
     fn batching<B, F>(self, f: F) -> Batching<Self, F>
-        where F: FnMut(&mut Self) -> Option<B>,
-              Self: Sized
+    where
+        F: FnMut(&mut Self) -> Option<B>,
+        Self: Sized,
     {
         adaptors::batching(self, f)
     }
@@ -591,10 +659,10 @@
     /// Consecutive elements that map to the same key (“runs”), are assigned
     /// to the same group.
     ///
-    /// `GroupBy` is the storage for the lazy grouping operation.
+    /// `ChunkBy` is the storage for the lazy grouping operation.
     ///
     /// If the groups are consumed in order, or if each group's iterator is
-    /// dropped without keeping it around, then `GroupBy` uses no
+    /// dropped without keeping it around, then `ChunkBy` uses no
     /// allocations.  It needs allocations only if several group iterators
     /// are alive at the same time.
     ///
@@ -609,34 +677,47 @@
     /// ```
     /// use itertools::Itertools;
     ///
-    /// // group data into runs of larger than zero or not.
+    /// // chunk data into runs of larger than zero or not.
     /// let data = vec![1, 3, -2, -2, 1, 0, 1, 2];
-    /// // groups:     |---->|------>|--------->|
+    /// // chunks:     |---->|------>|--------->|
     ///
-    /// // Note: The `&` is significant here, `GroupBy` is iterable
+    /// // Note: The `&` is significant here, `ChunkBy` is iterable
     /// // only by reference. You can also call `.into_iter()` explicitly.
     /// let mut data_grouped = Vec::new();
-    /// for (key, group) in &data.into_iter().group_by(|elt| *elt >= 0) {
-    ///     data_grouped.push((key, group.collect()));
+    /// for (key, chunk) in &data.into_iter().chunk_by(|elt| *elt >= 0) {
+    ///     data_grouped.push((key, chunk.collect()));
     /// }
     /// assert_eq!(data_grouped, vec![(true, vec![1, 3]), (false, vec![-2, -2]), (true, vec![1, 0, 1, 2])]);
     /// ```
     #[cfg(feature = "use_alloc")]
-    fn group_by<K, F>(self, key: F) -> GroupBy<K, Self, F>
-        where Self: Sized,
-              F: FnMut(&Self::Item) -> K,
-              K: PartialEq,
+    fn chunk_by<K, F>(self, key: F) -> ChunkBy<K, Self, F>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item) -> K,
+        K: PartialEq,
     {
         groupbylazy::new(self, key)
     }
 
+    /// See [`.chunk_by()`](Itertools::chunk_by).
+    #[deprecated(note = "Use .chunk_by() instead", since = "0.13.0")]
+    #[cfg(feature = "use_alloc")]
+    fn group_by<K, F>(self, key: F) -> ChunkBy<K, Self, F>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item) -> K,
+        K: PartialEq,
+    {
+        self.chunk_by(key)
+    }
+
     /// Return an *iterable* that can chunk the iterator.
     ///
     /// Yield subiterators (chunks) that each yield a fixed number elements,
     /// determined by `size`. The last chunk will be shorter if there aren't
     /// enough elements.
     ///
-    /// `IntoChunks` is based on `GroupBy`: it is iterable (implements
+    /// `IntoChunks` is based on `ChunkBy`: it is iterable (implements
     /// `IntoIterator`, **not** `Iterator`), and it only buffers if several
     /// chunk iterators are alive at the same time.
     ///
@@ -659,7 +740,8 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn chunks(self, size: usize) -> IntoChunks<Self>
-        where Self: Sized,
+    where
+        Self: Sized,
     {
         assert!(size != 0);
         groupbylazy::new_chunks(self, size)
@@ -699,9 +781,10 @@
     /// itertools::assert_equal(it, vec![(1, 2, 3), (2, 3, 4)]);
     /// ```
     fn tuple_windows<T>(self) -> TupleWindows<Self, T>
-        where Self: Sized + Iterator<Item = T::Item>,
-              T: traits::HomogeneousTuple,
-              T::Item: Clone
+    where
+        Self: Sized + Iterator<Item = T::Item>,
+        T: traits::HomogeneousTuple,
+        T::Item: Clone,
     {
         tuple_impl::tuple_windows(self)
     }
@@ -710,9 +793,9 @@
     /// elements when the window would otherwise exceed the length of the
     /// iterator, producing tuples of a specific size (up to 12).
     ///
-    /// `circular_tuple_windows` clones the iterator elements so that they can be
-    /// part of successive windows, this makes it most suited for iterators
-    /// of references and other values that are cheap to copy.
+    /// `circular_tuple_windows` clones the iterator elements so that they can
+    /// be part of successive windows, this makes it most suited for
+    /// iterators of references and other values that are cheap to copy.
     ///
     /// ```
     /// use itertools::Itertools;
@@ -734,9 +817,10 @@
     /// itertools::assert_equal(it, vec![(1, 2, 3), (2, 3, 4), (3, 4, 1), (4, 1, 2)]);
     /// ```
     fn circular_tuple_windows<T>(self) -> CircularTupleWindows<Self, T>
-        where Self: Sized + Clone + Iterator<Item = T::Item> + ExactSizeIterator,
-              T: tuple_impl::TupleCollect + Clone,
-              T::Item: Clone
+    where
+        Self: Sized + Clone + Iterator<Item = T::Item> + ExactSizeIterator,
+        T: tuple_impl::TupleCollect + Clone,
+        T::Item: Clone,
     {
         tuple_impl::circular_tuple_windows(self)
     }
@@ -772,8 +856,9 @@
     ///
     /// See also [`Tuples::into_buffer`].
     fn tuples<T>(self) -> Tuples<Self, T>
-        where Self: Sized + Iterator<Item = T::Item>,
-              T: traits::HomogeneousTuple
+    where
+        Self: Sized + Iterator<Item = T::Item>,
+        T: traits::HomogeneousTuple,
     {
         tuple_impl::tuples(self)
     }
@@ -797,36 +882,13 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn tee(self) -> (Tee<Self>, Tee<Self>)
-        where Self: Sized,
-              Self::Item: Clone
+    where
+        Self: Sized,
+        Self::Item: Clone,
     {
         tee::new(self)
     }
 
-    /// Return an iterator adaptor that steps `n` elements in the base iterator
-    /// for each iteration.
-    ///
-    /// The iterator steps by yielding the next element from the base iterator,
-    /// then skipping forward `n - 1` elements.
-    ///
-    /// Iterator element type is `Self::Item`.
-    ///
-    /// **Panics** if the step is 0.
-    ///
-    /// ```
-    /// use itertools::Itertools;
-    ///
-    /// let it = (0..8).step(3);
-    /// itertools::assert_equal(it, vec![0, 3, 6]);
-    /// ```
-    #[deprecated(note="Use std .step_by() instead", since="0.8.0")]
-    #[allow(deprecated)]
-    fn step(self, n: usize) -> Step<Self>
-        where Self: Sized
-    {
-        adaptors::step(self, n)
-    }
-
     /// Convert each item of the iterator using the [`Into`] trait.
     ///
     /// ```rust
@@ -835,21 +897,13 @@
     /// (1i32..42i32).map_into::<f64>().collect_vec();
     /// ```
     fn map_into<R>(self) -> MapInto<Self, R>
-        where Self: Sized,
-              Self::Item: Into<R>,
+    where
+        Self: Sized,
+        Self::Item: Into<R>,
     {
         adaptors::map_into(self)
     }
 
-    /// See [`.map_ok()`](Itertools::map_ok).
-    #[deprecated(note="Use .map_ok() instead", since="0.10.0")]
-    fn map_results<F, T, U, E>(self, f: F) -> MapOk<Self, F>
-        where Self: Iterator<Item = Result<T, E>> + Sized,
-              F: FnMut(T) -> U,
-    {
-        self.map_ok(f)
-    }
-
     /// Return an iterator adaptor that applies the provided closure
     /// to every `Result::Ok` value. `Result::Err` values are
     /// unchanged.
@@ -862,8 +916,9 @@
     /// itertools::assert_equal(it, vec![Ok(42), Err(false), Ok(12)]);
     /// ```
     fn map_ok<F, T, U, E>(self, f: F) -> MapOk<Self, F>
-        where Self: Iterator<Item = Result<T, E>> + Sized,
-              F: FnMut(T) -> U,
+    where
+        Self: Iterator<Item = Result<T, E>> + Sized,
+        F: FnMut(T) -> U,
     {
         adaptors::map_ok(self, f)
     }
@@ -880,8 +935,9 @@
     /// itertools::assert_equal(it, vec![Ok(22), Err(false)]);
     /// ```
     fn filter_ok<F, T, E>(self, f: F) -> FilterOk<Self, F>
-        where Self: Iterator<Item = Result<T, E>> + Sized,
-              F: FnMut(&T) -> bool,
+    where
+        Self: Iterator<Item = Result<T, E>> + Sized,
+        F: FnMut(&T) -> bool,
     {
         adaptors::filter_ok(self, f)
     }
@@ -898,8 +954,9 @@
     /// itertools::assert_equal(it, vec![Ok(44), Err(false)]);
     /// ```
     fn filter_map_ok<F, T, U, E>(self, f: F) -> FilterMapOk<Self, F>
-        where Self: Iterator<Item = Result<T, E>> + Sized,
-              F: FnMut(T) -> Option<U>,
+    where
+        Self: Iterator<Item = Result<T, E>> + Sized,
+        F: FnMut(T) -> Option<U>,
     {
         adaptors::filter_map_ok(self, f)
     }
@@ -908,7 +965,8 @@
     /// a series of `Result::Ok` values. `Result::Err` values are unchanged.
     ///
     /// This is useful when you have some common error type for your crate and
-    /// need to propagate it upwards, but the `Result::Ok` case needs to be flattened.
+    /// need to propagate it upwards, but the `Result::Ok` case needs to be
+    /// flattened.
     ///
     /// ```
     /// use itertools::Itertools;
@@ -922,8 +980,9 @@
     /// assert_eq!(output_result, Err(false));
     /// ```
     fn flatten_ok<T, E>(self) -> FlattenOk<Self, T, E>
-        where Self: Iterator<Item = Result<T, E>> + Sized,
-              T: IntoIterator
+    where
+        Self: Iterator<Item = Result<T, E>> + Sized,
+        T: IntoIterator,
     {
         flatten_ok::flatten_ok(self)
     }
@@ -931,9 +990,10 @@
     /// “Lift” a function of the values of the current iterator so as to process
     /// an iterator of `Result` values instead.
     ///
-    /// `processor` is a closure that receives an adapted version of the iterator
-    /// as the only argument — the adapted iterator produces elements of type `T`,
-    /// as long as the original iterator produces `Ok` values.
+    /// `processor` is a closure that receives an adapted version of the
+    /// iterator as the only argument — the adapted iterator produces
+    /// elements of type `T`, as long as the original iterator produces `Ok`
+    /// values.
     ///
     /// If the original iterable produces an error at any point, the adapted
     /// iterator ends and it will return the error iself.
@@ -959,8 +1019,9 @@
     /// assert!(second_max.is_err());
     /// ```
     fn process_results<F, T, E, R>(self, processor: F) -> Result<R, E>
-        where Self: Iterator<Item = Result<T, E>> + Sized,
-              F: FnOnce(ProcessResults<Self, E>) -> R
+    where
+        Self: Iterator<Item = Result<T, E>> + Sized,
+        F: FnOnce(ProcessResults<Self, E>) -> R,
     {
         process_results(self, processor)
     }
@@ -980,15 +1041,17 @@
     /// itertools::assert_equal(it, vec![0, 0, 3, 5, 6, 9, 10]);
     /// ```
     fn merge<J>(self, other: J) -> Merge<Self, J::IntoIter>
-        where Self: Sized,
-              Self::Item: PartialOrd,
-              J: IntoIterator<Item = Self::Item>
+    where
+        Self: Sized,
+        Self::Item: PartialOrd,
+        J: IntoIterator<Item = Self::Item>,
     {
         merge(self, other)
     }
 
     /// Return an iterator adaptor that merges the two base iterators in order.
-    /// This is much like [`.merge()`](Itertools::merge) but allows for a custom ordering.
+    /// This is much like [`.merge()`](Itertools::merge) but allows for a custom
+    /// ordering.
     ///
     /// This can be especially useful for sequences of tuples.
     ///
@@ -1002,13 +1065,13 @@
     /// let it = a.merge_by(b, |x, y| x.1 <= y.1);
     /// itertools::assert_equal(it, vec![(0, 'a'), (0, 'b'), (1, 'c'), (1, 'd')]);
     /// ```
-
     fn merge_by<J, F>(self, other: J, is_first: F) -> MergeBy<Self, J::IntoIter, F>
-        where Self: Sized,
-              J: IntoIterator<Item = Self::Item>,
-              F: FnMut(&Self::Item, &Self::Item) -> bool
+    where
+        Self: Sized,
+        J: IntoIterator<Item = Self::Item>,
+        F: FnMut(&Self::Item, &Self::Item) -> bool,
     {
-        adaptors::merge_by_new(self, other.into_iter(), is_first)
+        merge_join::merge_by_new(self, other, is_first)
     }
 
     /// Create an iterator that merges items from both this and the specified
@@ -1017,17 +1080,17 @@
     /// The function can either return an `Ordering` variant or a boolean.
     ///
     /// If `cmp_fn` returns `Ordering`,
-    /// it chooses whether to pair elements based on the `Ordering` returned by the
-    /// specified compare function. At any point, inspecting the tip of the
-    /// iterators `I` and `J` as items `i` of type `I::Item` and `j` of type
-    /// `J::Item` respectively, the resulting iterator will:
+    /// it chooses whether to pair elements based on the `Ordering` returned by
+    /// the specified compare function. At any point, inspecting the tip of
+    /// the iterators `I` and `J` as items `i` of type `I::Item` and `j` of
+    /// type `J::Item` respectively, the resulting iterator will:
     ///
-    /// - Emit `EitherOrBoth::Left(i)` when `i < j`,
-    ///   and remove `i` from its source iterator
-    /// - Emit `EitherOrBoth::Right(j)` when `i > j`,
-    ///   and remove `j` from its source iterator
-    /// - Emit `EitherOrBoth::Both(i, j)` when  `i == j`,
-    ///   and remove both `i` and `j` from their respective source iterators
+    /// - Emit `EitherOrBoth::Left(i)` when `i < j`, and remove `i` from its
+    ///   source iterator
+    /// - Emit `EitherOrBoth::Right(j)` when `i > j`, and remove `j` from its
+    ///   source iterator
+    /// - Emit `EitherOrBoth::Both(i, j)` when  `i == j`, and remove both `i`
+    ///   and `j` from their respective source iterators
     ///
     /// ```
     /// use itertools::Itertools;
@@ -1037,7 +1100,9 @@
     /// let b = (0..10).step_by(3);
     ///
     /// itertools::assert_equal(
-    ///     a.merge_join_by(b, |i, j| i.cmp(j)),
+    ///     // This performs a diff in the style of the Unix command comm(1),
+    ///     // generalized to arbitrary types rather than text.
+    ///     a.merge_join_by(b, Ord::cmp),
     ///     vec![Both(0, 0), Left(2), Right(3), Left(4), Both(6, 6), Left(1), Right(9)]
     /// );
     /// ```
@@ -1048,10 +1113,10 @@
     /// iterators `I` and `J` as items `i` of type `I::Item` and `j` of type
     /// `J::Item` respectively, the resulting iterator will:
     ///
-    /// - Emit `Either::Left(i)` when `true`,
-    ///   and remove `i` from its source iterator
-    /// - Emit `Either::Right(j)` when `false`,
-    ///   and remove `j` from its source iterator
+    /// - Emit `Either::Left(i)` when `true`, and remove `i` from its source
+    ///   iterator
+    /// - Emit `Either::Right(j)` when `false`, and remove `j` from its source
+    ///   iterator
     ///
     /// It is similar to the `Ordering` case if the first argument is considered
     /// "less" than the second argument.
@@ -1069,11 +1134,12 @@
     /// );
     /// ```
     #[inline]
+    #[doc(alias = "comm")]
     fn merge_join_by<J, F, T>(self, other: J, cmp_fn: F) -> MergeJoinBy<Self, J::IntoIter, F>
-        where J: IntoIterator,
-              F: FnMut(&Self::Item, &J::Item) -> T,
-              T: merge_join::OrderingOrBool<Self::Item, J::Item>,
-              Self: Sized
+    where
+        J: IntoIterator,
+        F: FnMut(&Self::Item, &J::Item) -> T,
+        Self: Sized,
     {
         merge_join_by(self, other, cmp_fn)
     }
@@ -1096,9 +1162,10 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn kmerge(self) -> KMerge<<Self::Item as IntoIterator>::IntoIter>
-        where Self: Sized,
-              Self::Item: IntoIterator,
-              <Self::Item as IntoIterator>::Item: PartialOrd,
+    where
+        Self: Sized,
+        Self::Item: IntoIterator,
+        <Self::Item as IntoIterator>::Item: PartialOrd,
     {
         kmerge(self)
     }
@@ -1124,12 +1191,11 @@
     /// assert_eq!(it.last(), Some(-7.));
     /// ```
     #[cfg(feature = "use_alloc")]
-    fn kmerge_by<F>(self, first: F)
-        -> KMergeBy<<Self::Item as IntoIterator>::IntoIter, F>
-        where Self: Sized,
-              Self::Item: IntoIterator,
-              F: FnMut(&<Self::Item as IntoIterator>::Item,
-                       &<Self::Item as IntoIterator>::Item) -> bool
+    fn kmerge_by<F>(self, first: F) -> KMergeBy<<Self::Item as IntoIterator>::IntoIter, F>
+    where
+        Self: Sized,
+        Self::Item: IntoIterator,
+        F: FnMut(&<Self::Item as IntoIterator>::Item, &<Self::Item as IntoIterator>::Item) -> bool,
     {
         kmerge_by(self, first)
     }
@@ -1146,10 +1212,11 @@
     /// itertools::assert_equal(it, vec![(0, 'α'), (0, 'β'), (1, 'α'), (1, 'β')]);
     /// ```
     fn cartesian_product<J>(self, other: J) -> Product<Self, J::IntoIter>
-        where Self: Sized,
-              Self::Item: Clone,
-              J: IntoIterator,
-              J::IntoIter: Clone
+    where
+        Self: Sized,
+        Self::Item: Clone,
+        J: IntoIterator,
+        J::IntoIter: Clone,
     {
         adaptors::cartesian_product(self, other.into_iter())
     }
@@ -1161,10 +1228,11 @@
     /// the product of iterators yielding multiple types, use the
     /// [`iproduct`] macro instead.
     ///
-    ///
     /// The iterator element type is `Vec<T>`, where `T` is the iterator element
     /// of the subiterators.
     ///
+    /// Note that the iterator is fused.
+    ///
     /// ```
     /// use itertools::Itertools;
     /// let mut multi_prod = (0..3).map(|i| (i * 2)..(i * 2 + 2))
@@ -1179,12 +1247,23 @@
     /// assert_eq!(multi_prod.next(), Some(vec![1, 3, 5]));
     /// assert_eq!(multi_prod.next(), None);
     /// ```
+    ///
+    /// If the adapted iterator is empty, the result is an iterator yielding a
+    /// single empty vector. This is known as the [nullary cartesian product](https://en.wikipedia.org/wiki/Empty_product#Nullary_Cartesian_product).
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    /// let mut nullary_cartesian_product = (0..0).map(|i| (i * 2)..(i * 2 + 2)).multi_cartesian_product();
+    /// assert_eq!(nullary_cartesian_product.next(), Some(vec![]));
+    /// assert_eq!(nullary_cartesian_product.next(), None);
+    /// ```
     #[cfg(feature = "use_alloc")]
     fn multi_cartesian_product(self) -> MultiProduct<<Self::Item as IntoIterator>::IntoIter>
-        where Self: Sized,
-              Self::Item: IntoIterator,
-              <Self::Item as IntoIterator>::IntoIter: Clone,
-              <Self::Item as IntoIterator>::Item: Clone
+    where
+        Self: Sized,
+        Self::Item: IntoIterator,
+        <Self::Item as IntoIterator>::IntoIter: Clone,
+        <Self::Item as IntoIterator>::Item: Clone,
     {
         adaptors::multi_cartesian_product(self)
     }
@@ -1218,9 +1297,9 @@
     ///         vec![-6., 4., -1.]);
     /// ```
     fn coalesce<F>(self, f: F) -> Coalesce<Self, F>
-        where Self: Sized,
-              F: FnMut(Self::Item, Self::Item)
-                       -> Result<Self::Item, (Self::Item, Self::Item)>
+    where
+        Self: Sized,
+        F: FnMut(Self::Item, Self::Item) -> Result<Self::Item, (Self::Item, Self::Item)>,
     {
         adaptors::coalesce(self, f)
     }
@@ -1240,8 +1319,9 @@
     ///                         vec![1., 2., 3., 2.]);
     /// ```
     fn dedup(self) -> Dedup<Self>
-        where Self: Sized,
-              Self::Item: PartialEq,
+    where
+        Self: Sized,
+        Self::Item: PartialEq,
     {
         adaptors::dedup(self)
     }
@@ -1262,14 +1342,15 @@
     ///                         vec![(0, 1.), (0, 2.), (0, 3.), (1, 2.)]);
     /// ```
     fn dedup_by<Cmp>(self, cmp: Cmp) -> DedupBy<Self, Cmp>
-        where Self: Sized,
-              Cmp: FnMut(&Self::Item, &Self::Item)->bool,
+    where
+        Self: Sized,
+        Cmp: FnMut(&Self::Item, &Self::Item) -> bool,
     {
         adaptors::dedup_by(self, cmp)
     }
 
-    /// Remove duplicates from sections of consecutive identical elements, while keeping a count of
-    /// how many repeated elements were present.
+    /// Remove duplicates from sections of consecutive identical elements, while
+    /// keeping a count of how many repeated elements were present.
     /// If the iterator is sorted, all elements will be unique.
     ///
     /// Iterator element type is `(usize, Self::Item)`.
@@ -1290,8 +1371,8 @@
         adaptors::dedup_with_count(self)
     }
 
-    /// Remove duplicates from sections of consecutive identical elements, while keeping a count of
-    /// how many repeated elements were present.
+    /// Remove duplicates from sections of consecutive identical elements, while
+    /// keeping a count of how many repeated elements were present.
     /// This will determine equality using a comparison function.
     /// If the iterator is sorted, all elements will be unique.
     ///
@@ -1314,12 +1395,14 @@
         adaptors::dedup_by_with_count(self, cmp)
     }
 
-    /// Return an iterator adaptor that produces elements that appear more than once during the
-    /// iteration. Duplicates are detected using hash and equality.
+    /// Return an iterator adaptor that produces elements that appear more than
+    /// once during the iteration. Duplicates are detected using hash and
+    /// equality.
     ///
-    /// The iterator is stable, returning the duplicate items in the order in which they occur in
-    /// the adapted iterator. Each duplicate item is returned exactly once. If an item appears more
-    /// than twice, the second item is the item retained and the rest are discarded.
+    /// The iterator is stable, returning the duplicate items in the order in
+    /// which they occur in the adapted iterator. Each duplicate item is
+    /// returned exactly once. If an item appears more than twice, the
+    /// second item is the item retained and the rest are discarded.
     ///
     /// ```
     /// use itertools::Itertools;
@@ -1330,21 +1413,25 @@
     /// ```
     #[cfg(feature = "use_std")]
     fn duplicates(self) -> Duplicates<Self>
-        where Self: Sized,
-              Self::Item: Eq + Hash
+    where
+        Self: Sized,
+        Self::Item: Eq + Hash,
     {
         duplicates_impl::duplicates(self)
     }
 
-    /// Return an iterator adaptor that produces elements that appear more than once during the
-    /// iteration. Duplicates are detected using hash and equality.
+    /// Return an iterator adaptor that produces elements that appear more than
+    /// once during the iteration. Duplicates are detected using hash and
+    /// equality.
     ///
-    /// Duplicates are detected by comparing the key they map to with the keying function `f` by
-    /// hash and equality. The keys are stored in a hash map in the iterator.
+    /// Duplicates are detected by comparing the key they map to with the keying
+    /// function `f` by hash and equality. The keys are stored in a hash map
+    /// in the iterator.
     ///
-    /// The iterator is stable, returning the duplicate items in the order in which they occur in
-    /// the adapted iterator. Each duplicate item is returned exactly once. If an item appears more
-    /// than twice, the second item is the item retained and the rest are discarded.
+    /// The iterator is stable, returning the duplicate items in the order in
+    /// which they occur in the adapted iterator. Each duplicate item is
+    /// returned exactly once. If an item appears more than twice, the
+    /// second item is the item retained and the rest are discarded.
     ///
     /// ```
     /// use itertools::Itertools;
@@ -1355,9 +1442,10 @@
     /// ```
     #[cfg(feature = "use_std")]
     fn duplicates_by<V, F>(self, f: F) -> DuplicatesBy<Self, V, F>
-        where Self: Sized,
-              V: Eq + Hash,
-              F: FnMut(&Self::Item) -> V
+    where
+        Self: Sized,
+        V: Eq + Hash,
+        F: FnMut(&Self::Item) -> V,
     {
         duplicates_impl::duplicates_by(self, f)
     }
@@ -1382,8 +1470,9 @@
     /// ```
     #[cfg(feature = "use_std")]
     fn unique(self) -> Unique<Self>
-        where Self: Sized,
-              Self::Item: Clone + Eq + Hash
+    where
+        Self: Sized,
+        Self::Item: Clone + Eq + Hash,
     {
         unique_impl::unique(self)
     }
@@ -1408,9 +1497,10 @@
     /// ```
     #[cfg(feature = "use_std")]
     fn unique_by<V, F>(self, f: F) -> UniqueBy<Self, V, F>
-        where Self: Sized,
-              V: Eq + Hash,
-              F: FnMut(&Self::Item) -> V
+    where
+        Self: Sized,
+        V: Eq + Hash,
+        F: FnMut(&Self::Item) -> V,
     {
         unique_impl::unique_by(self, f)
     }
@@ -1428,8 +1518,9 @@
     /// See also [`.take_while_ref()`](Itertools::take_while_ref)
     /// which is a similar adaptor.
     fn peeking_take_while<F>(&mut self, accept: F) -> PeekingTakeWhile<Self, F>
-        where Self: Sized + PeekingNext,
-              F: FnMut(&Self::Item) -> bool,
+    where
+        Self: Sized + PeekingNext,
+        F: FnMut(&Self::Item) -> bool,
     {
         peeking_take_while::peeking_take_while(self, accept)
     }
@@ -1450,11 +1541,11 @@
     ///                            .collect::<String>();
     /// assert_eq!(decimals, "0123456789");
     /// assert_eq!(hexadecimals.next(), Some('a'));
-    ///
     /// ```
     fn take_while_ref<F>(&mut self, accept: F) -> TakeWhileRef<Self, F>
-        where Self: Clone,
-              F: FnMut(&Self::Item) -> bool
+    where
+        Self: Clone,
+        F: FnMut(&Self::Item) -> bool,
     {
         adaptors::take_while_ref(self, accept)
     }
@@ -1519,7 +1610,8 @@
     ///     .collect();
     /// let expected: Vec<_> = vec![1, 2, 3].into_iter().map(NoCloneImpl).collect();
     /// assert_eq!(filtered, expected);
-    fn take_while_inclusive<F>(&mut self, accept: F) -> TakeWhileInclusive<Self, F>
+    #[doc(alias = "take_until")]
+    fn take_while_inclusive<F>(self, accept: F) -> TakeWhileInclusive<Self, F>
     where
         Self: Sized,
         F: FnMut(&Self::Item) -> bool,
@@ -1539,10 +1631,10 @@
     /// itertools::assert_equal(
     ///     (0..).map(|i| std::char::from_digit(i, 16)).while_some(),
     ///     "0123456789abcdef".chars());
-    ///
     /// ```
     fn while_some<A>(self) -> WhileSome<Self>
-        where Self: Sized + Iterator<Item = Option<A>>
+    where
+        Self: Sized + Iterator<Item = Option<A>>,
     {
         adaptors::while_some(self)
     }
@@ -1553,6 +1645,11 @@
     /// Iterator element can be any homogeneous tuple of type `Self::Item` with
     /// size up to 12.
     ///
+    /// # Guarantees
+    ///
+    /// If the adapted iterator is deterministic,
+    /// this iterator adapter yields items in a reliable order.
+    ///
     /// ```
     /// use itertools::Itertools;
     ///
@@ -1581,18 +1678,71 @@
     /// itertools::assert_equal(it, vec![(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]);
     /// ```
     fn tuple_combinations<T>(self) -> TupleCombinations<Self, T>
-        where Self: Sized + Clone,
-              Self::Item: Clone,
-              T: adaptors::HasCombination<Self>,
+    where
+        Self: Sized + Clone,
+        Self::Item: Clone,
+        T: adaptors::HasCombination<Self>,
     {
         adaptors::tuple_combinations(self)
     }
 
-    /// Return an iterator adaptor that iterates over the `k`-length combinations of
-    /// the elements from an iterator.
+    /// Return an iterator adaptor that iterates over the combinations of the
+    /// elements from an iterator.
     ///
-    /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new Vec per iteration,
-    /// and clones the iterator elements.
+    /// Iterator element type is [Self::Item; K]. The iterator produces a new
+    /// array per iteration, and clones the iterator elements.
+    ///
+    /// # Guarantees
+    ///
+    /// If the adapted iterator is deterministic,
+    /// this iterator adapter yields items in a reliable order.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let mut v = Vec::new();
+    /// for [a, b] in (1..5).array_combinations() {
+    ///     v.push([a, b]);
+    /// }
+    /// assert_eq!(v, vec![[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]);
+    ///
+    /// let mut it = (1..5).array_combinations();
+    /// assert_eq!(Some([1, 2, 3]), it.next());
+    /// assert_eq!(Some([1, 2, 4]), it.next());
+    /// assert_eq!(Some([1, 3, 4]), it.next());
+    /// assert_eq!(Some([2, 3, 4]), it.next());
+    /// assert_eq!(None, it.next());
+    ///
+    /// // this requires a type hint
+    /// let it = (1..5).array_combinations::<3>();
+    /// itertools::assert_equal(it, vec![[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]);
+    ///
+    /// // you can also specify the complete type
+    /// use itertools::ArrayCombinations;
+    /// use std::ops::Range;
+    ///
+    /// let it: ArrayCombinations<Range<u32>, 3> = (1..5).array_combinations();
+    /// itertools::assert_equal(it, vec![[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn array_combinations<const K: usize>(self) -> ArrayCombinations<Self, K>
+    where
+        Self: Sized + Clone,
+        Self::Item: Clone,
+    {
+        combinations::array_combinations(self)
+    }
+
+    /// Return an iterator adaptor that iterates over the `k`-length
+    /// combinations of the elements from an iterator.
+    ///
+    /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new
+    /// `Vec` per iteration, and clones the iterator elements.
+    ///
+    /// # Guarantees
+    ///
+    /// If the adapted iterator is deterministic,
+    /// this iterator adapter yields items in a reliable order.
     ///
     /// ```
     /// use itertools::Itertools;
@@ -1606,7 +1756,8 @@
     /// ]);
     /// ```
     ///
-    /// Note: Combinations does not take into account the equality of the iterated values.
+    /// Note: Combinations does not take into account the equality of the
+    /// iterated values.
     /// ```
     /// use itertools::Itertools;
     ///
@@ -1619,8 +1770,9 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn combinations(self, k: usize) -> Combinations<Self>
-        where Self: Sized,
-              Self::Item: Clone
+    where
+        Self: Sized,
+        Self::Item: Clone,
     {
         combinations::combinations(self, k)
     }
@@ -1628,8 +1780,8 @@
     /// Return an iterator that iterates over the `k`-length combinations of
     /// the elements from an iterator, with replacement.
     ///
-    /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new Vec per iteration,
-    /// and clones the iterator elements.
+    /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new
+    /// `Vec` per iteration, and clones the iterator elements.
     ///
     /// ```
     /// use itertools::Itertools;
@@ -1657,11 +1809,14 @@
     /// elements from an iterator.
     ///
     /// Iterator element type is `Vec<Self::Item>` with length `k`. The iterator
-    /// produces a new Vec per iteration, and clones the iterator elements.
+    /// produces a new `Vec` per iteration, and clones the iterator elements.
     ///
     /// If `k` is greater than the length of the input iterator, the resultant
     /// iterator adaptor will be empty.
     ///
+    /// If you are looking for permutations with replacements,
+    /// use `repeat_n(iter, k).multi_cartesian_product()` instead.
+    ///
     /// ```
     /// use itertools::Itertools;
     ///
@@ -1676,7 +1831,8 @@
     /// ]);
     /// ```
     ///
-    /// Note: Permutations does not take into account the equality of the iterated values.
+    /// Note: Permutations does not take into account the equality of the
+    /// iterated values.
     ///
     /// ```
     /// use itertools::Itertools;
@@ -1692,21 +1848,22 @@
     /// re-iterated if the permutations adaptor is completed and re-iterated.
     #[cfg(feature = "use_alloc")]
     fn permutations(self, k: usize) -> Permutations<Self>
-        where Self: Sized,
-              Self::Item: Clone
+    where
+        Self: Sized,
+        Self::Item: Clone,
     {
         permutations::permutations(self, k)
     }
 
-    /// Return an iterator that iterates through the powerset of the elements from an
-    /// iterator.
+    /// Return an iterator that iterates through the powerset of the elements
+    /// from an iterator.
     ///
-    /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new `Vec`
-    /// per iteration, and clones the iterator elements.
+    /// Iterator element type is `Vec<Self::Item>`. The iterator produces a new
+    /// `Vec` per iteration, and clones the iterator elements.
     ///
-    /// The powerset of a set contains all subsets including the empty set and the full
-    /// input set. A powerset has length _2^n_ where _n_ is the length of the input
-    /// set.
+    /// The powerset of a set contains all subsets including the empty set and
+    /// the full input set. A powerset has length _2^n_ where _n_ is the
+    /// length of the input set.
     ///
     /// Each `Vec` produced by this iterator represents a subset of the elements
     /// produced by the source iterator.
@@ -1728,8 +1885,9 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn powerset(self) -> Powerset<Self>
-        where Self: Sized,
-              Self::Item: Clone,
+    where
+        Self: Sized,
+        Self::Item: Clone,
     {
         powerset::powerset(self)
     }
@@ -1752,14 +1910,15 @@
     /// itertools::assert_equal(it, vec![18, 16, 14, 12, 10, 4, 3, 2, 1, 0]);
     /// ```
     fn pad_using<F>(self, min: usize, f: F) -> PadUsing<Self, F>
-        where Self: Sized,
-              F: FnMut(usize) -> Self::Item
+    where
+        Self: Sized,
+        F: FnMut(usize) -> Self::Item,
     {
         pad_tail::pad_using(self, min, f)
     }
 
-    /// Return an iterator adaptor that combines each element with a `Position` to
-    /// ease special-case handling of the first or last elements.
+    /// Return an iterator adaptor that combines each element with a `Position`
+    /// to ease special-case handling of the first or last elements.
     ///
     /// Iterator element type is
     /// [`(Position, Self::Item)`](Position)
@@ -1778,7 +1937,8 @@
     /// itertools::assert_equal(it, vec![(Position::Only, 0)]);
     /// ```
     fn with_position(self) -> WithPosition<Self>
-        where Self: Sized,
+    where
+        Self: Sized,
     {
         with_position::with_position(self)
     }
@@ -1786,7 +1946,8 @@
     /// Return an iterator adaptor that yields the indices of all elements
     /// satisfying a predicate, counted from the start of the iterator.
     ///
-    /// Equivalent to `iter.enumerate().filter(|(_, v)| predicate(v)).map(|(i, _)| i)`.
+    /// Equivalent to `iter.enumerate().filter(|(_, v)| predicate(*v)).map(|(i,
+    /// _)| i)`.
     ///
     /// ```
     /// use itertools::Itertools;
@@ -1797,8 +1958,9 @@
     /// itertools::assert_equal(data.iter().positions(|v| v % 2 == 1).rev(), vec![7, 6, 3, 2, 0]);
     /// ```
     fn positions<P>(self, predicate: P) -> Positions<Self, P>
-        where Self: Sized,
-              P: FnMut(Self::Item) -> bool,
+    where
+        Self: Sized,
+        P: FnMut(Self::Item) -> bool,
     {
         adaptors::positions(self, predicate)
     }
@@ -1810,17 +1972,62 @@
     /// use itertools::Itertools;
     ///
     /// let input = vec![vec![1], vec![3, 2, 1]];
-    /// let it = input.into_iter().update(|mut v| v.push(0));
+    /// let it = input.into_iter().update(|v| v.push(0));
     /// itertools::assert_equal(it, vec![vec![1, 0], vec![3, 2, 1, 0]]);
     /// ```
     fn update<F>(self, updater: F) -> Update<Self, F>
-        where Self: Sized,
-              F: FnMut(&mut Self::Item),
+    where
+        Self: Sized,
+        F: FnMut(&mut Self::Item),
     {
         adaptors::update(self, updater)
     }
 
     // non-adaptor methods
+    /// Advances the iterator and returns the next items grouped in an array of
+    /// a specific size.
+    ///
+    /// If there are enough elements to be grouped in an array, then the array
+    /// is returned inside `Some`, otherwise `None` is returned.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let mut iter = 1..5;
+    ///
+    /// assert_eq!(Some([1, 2]), iter.next_array());
+    /// ```
+    fn next_array<const N: usize>(&mut self) -> Option<[Self::Item; N]>
+    where
+        Self: Sized,
+    {
+        next_array::next_array(self)
+    }
+
+    /// Collects all items from the iterator into an array of a specific size.
+    ///
+    /// If the number of elements inside the iterator is **exactly** equal to
+    /// the array size, then the array is returned inside `Some`, otherwise
+    /// `None` is returned.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let iter = 1..3;
+    ///
+    /// if let Some([x, y]) = iter.collect_array() {
+    ///     assert_eq!([x, y], [1, 2])
+    /// } else {
+    ///     panic!("Expected two elements")
+    /// }
+    /// ```
+    fn collect_array<const N: usize>(mut self) -> Option<[Self::Item; N]>
+    where
+        Self: Sized,
+    {
+        self.next_array().filter(|_| self.next().is_none())
+    }
+
     /// Advances the iterator and returns the next items grouped in a tuple of
     /// a specific size (up to 12).
     ///
@@ -1835,8 +2042,9 @@
     /// assert_eq!(Some((1, 2)), iter.next_tuple());
     /// ```
     fn next_tuple<T>(&mut self) -> Option<T>
-        where Self: Sized + Iterator<Item = T::Item>,
-              T: traits::HomogeneousTuple
+    where
+        Self: Sized + Iterator<Item = T::Item>,
+        T: traits::HomogeneousTuple,
     {
         T::collect_from_iter_no_buf(self)
     }
@@ -1860,19 +2068,19 @@
     /// }
     /// ```
     fn collect_tuple<T>(mut self) -> Option<T>
-        where Self: Sized + Iterator<Item = T::Item>,
-              T: traits::HomogeneousTuple
+    where
+        Self: Sized + Iterator<Item = T::Item>,
+        T: traits::HomogeneousTuple,
     {
         match self.next_tuple() {
             elt @ Some(_) => match self.next() {
                 Some(_) => None,
                 None => elt,
             },
-            _ => None
+            _ => None,
         }
     }
 
-
     /// Find the position and value of the first element satisfying a predicate.
     ///
     /// The iterator is not advanced past the first element found.
@@ -1884,16 +2092,13 @@
     /// assert_eq!(text.chars().find_position(|ch| ch.is_lowercase()), Some((1, 'α')));
     /// ```
     fn find_position<P>(&mut self, mut pred: P) -> Option<(usize, Self::Item)>
-        where P: FnMut(&Self::Item) -> bool
+    where
+        P: FnMut(&Self::Item) -> bool,
     {
-        for (index, elt) in self.enumerate() {
-            if pred(&elt) {
-                return Some((index, elt));
-            }
-        }
-        None
+        self.enumerate().find(|(_, elt)| pred(elt))
     }
-    /// Find the value of the first element satisfying a predicate or return the last element, if any.
+    /// Find the value of the first element satisfying a predicate or return the
+    /// last element, if any.
     ///
     /// The iterator is not advanced past the first element found.
     ///
@@ -1904,16 +2109,34 @@
     /// assert_eq!(numbers.iter().find_or_last(|&&x| x > 5), Some(&4));
     /// assert_eq!(numbers.iter().find_or_last(|&&x| x > 2), Some(&3));
     /// assert_eq!(std::iter::empty::<i32>().find_or_last(|&x| x > 5), None);
+    ///
+    /// // An iterator of Results can return the first Ok or the last Err:
+    /// let input = vec![Err(()), Ok(11), Err(()), Ok(22)];
+    /// assert_eq!(input.into_iter().find_or_last(Result::is_ok), Some(Ok(11)));
+    ///
+    /// let input: Vec<Result<(), i32>> = vec![Err(11), Err(22)];
+    /// assert_eq!(input.into_iter().find_or_last(Result::is_ok), Some(Err(22)));
+    ///
+    /// assert_eq!(std::iter::empty::<Result<(), i32>>().find_or_last(Result::is_ok), None);
     /// ```
     fn find_or_last<P>(mut self, mut predicate: P) -> Option<Self::Item>
-        where Self: Sized,
-              P: FnMut(&Self::Item) -> bool,
+    where
+        Self: Sized,
+        P: FnMut(&Self::Item) -> bool,
     {
         let mut prev = None;
-        self.find_map(|x| if predicate(&x) { Some(x) } else { prev = Some(x); None })
-            .or(prev)
+        self.find_map(|x| {
+            if predicate(&x) {
+                Some(x)
+            } else {
+                prev = Some(x);
+                None
+            }
+        })
+        .or(prev)
     }
-    /// Find the value of the first element satisfying a predicate or return the first element, if any.
+    /// Find the value of the first element satisfying a predicate or return the
+    /// first element, if any.
     ///
     /// The iterator is not advanced past the first element found.
     ///
@@ -1924,17 +2147,23 @@
     /// assert_eq!(numbers.iter().find_or_first(|&&x| x > 5), Some(&1));
     /// assert_eq!(numbers.iter().find_or_first(|&&x| x > 2), Some(&3));
     /// assert_eq!(std::iter::empty::<i32>().find_or_first(|&x| x > 5), None);
+    ///
+    /// // An iterator of Results can return the first Ok or the first Err:
+    /// let input = vec![Err(()), Ok(11), Err(()), Ok(22)];
+    /// assert_eq!(input.into_iter().find_or_first(Result::is_ok), Some(Ok(11)));
+    ///
+    /// let input: Vec<Result<(), i32>> = vec![Err(11), Err(22)];
+    /// assert_eq!(input.into_iter().find_or_first(Result::is_ok), Some(Err(11)));
+    ///
+    /// assert_eq!(std::iter::empty::<Result<(), i32>>().find_or_first(Result::is_ok), None);
     /// ```
     fn find_or_first<P>(mut self, mut predicate: P) -> Option<Self::Item>
-        where Self: Sized,
-              P: FnMut(&Self::Item) -> bool,
+    where
+        Self: Sized,
+        P: FnMut(&Self::Item) -> bool,
     {
         let first = self.next()?;
-        Some(if predicate(&first) {
-            first
-        } else {
-            self.find(|x| predicate(x)).unwrap_or(first)
-        })
+        Some(if predicate(&first) { first } else { self.find(|x| predicate(x)).unwrap_or(first) })
     }
     /// Returns `true` if the given item is present in this iterator.
     ///
@@ -1965,7 +2194,7 @@
     where
         Self: Sized,
         Self::Item: Borrow<Q>,
-        Q: PartialEq,
+        Q: PartialEq + ?Sized,
     {
         self.any(|x| x.borrow() == query)
     }
@@ -1987,8 +2216,9 @@
     /// assert!(data.into_iter().all_equal());
     /// ```
     fn all_equal(&mut self) -> bool
-        where Self: Sized,
-              Self::Item: PartialEq,
+    where
+        Self: Sized,
+        Self::Item: PartialEq,
     {
         match self.next() {
             None => true,
@@ -1996,10 +2226,10 @@
         }
     }
 
-    /// If there are elements and they are all equal, return a single copy of that element.
-    /// If there are no elements, return an Error containing None.
-    /// If there are elements and they are not all equal, return a tuple containing the first
-    /// two non-equal elements found.
+    /// If there are elements and they are all equal, return a single copy of
+    /// that element. If there are no elements, return an Error containing
+    /// None. If there are elements and they are not all equal, return a
+    /// tuple containing the first two non-equal elements found.
     ///
     /// ```
     /// use itertools::Itertools;
@@ -2013,10 +2243,11 @@
     /// let data : Option<usize> = None;
     /// assert_eq!(data.into_iter().all_equal_value(), Err(None));
     /// ```
+    #[allow(clippy::type_complexity)]
     fn all_equal_value(&mut self) -> Result<Self::Item, Option<(Self::Item, Self::Item)>>
-        where
-            Self: Sized,
-            Self::Item: PartialEq
+    where
+        Self: Sized,
+        Self::Item: PartialEq,
     {
         let first = self.next().ok_or(None)?;
         let other = self.find(|x| x != &first);
@@ -2044,8 +2275,9 @@
     /// ```
     #[cfg(feature = "use_std")]
     fn all_unique(&mut self) -> bool
-        where Self: Sized,
-              Self::Item: Eq + Hash
+    where
+        Self: Sized,
+        Self::Item: Eq + Hash,
     {
         let mut used = HashSet::new();
         self.all(move |elt| used.insert(elt))
@@ -2054,20 +2286,22 @@
     /// Consume the first `n` elements from the iterator eagerly,
     /// and return the same iterator again.
     ///
-    /// It works similarly to *.skip(* `n` *)* except it is eager and
+    /// It works similarly to `.skip(n)` except it is eager and
     /// preserves the iterator type.
     ///
     /// ```
     /// use itertools::Itertools;
     ///
-    /// let mut iter = "αβγ".chars().dropping(2);
+    /// let iter = "αβγ".chars().dropping(2);
     /// itertools::assert_equal(iter, "γ".chars());
     /// ```
     ///
     /// *Fusing notes: if the iterator is exhausted by dropping,
-    /// the result of calling `.next()` again depends on the iterator implementation.*
+    /// the result of calling `.next()` again depends on the iterator
+    /// implementation.*
     fn dropping(mut self, n: usize) -> Self
-        where Self: Sized
+    where
+        Self: Sized,
     {
         if n > 0 {
             self.nth(n - 1);
@@ -2091,8 +2325,8 @@
     /// itertools::assert_equal(init, vec![0, 3, 6]);
     /// ```
     fn dropping_back(mut self, n: usize) -> Self
-        where Self: Sized,
-              Self: DoubleEndedIterator
+    where
+        Self: Sized + DoubleEndedIterator,
     {
         if n > 0 {
             (&mut self).rev().nth(n - 1);
@@ -2100,31 +2334,6 @@
         self
     }
 
-    /// Run the closure `f` eagerly on each element of the iterator.
-    ///
-    /// Consumes the iterator until its end.
-    ///
-    /// ```
-    /// use std::sync::mpsc::channel;
-    /// use itertools::Itertools;
-    ///
-    /// let (tx, rx) = channel();
-    ///
-    /// // use .foreach() to apply a function to each value -- sending it
-    /// (0..5).map(|x| x * 2 + 1).foreach(|x| { tx.send(x).unwrap(); } );
-    ///
-    /// drop(tx);
-    ///
-    /// itertools::assert_equal(rx.iter(), vec![1, 3, 5, 7, 9]);
-    /// ```
-    #[deprecated(note="Use .for_each() instead", since="0.8.0")]
-    fn foreach<F>(self, f: F)
-        where F: FnMut(Self::Item),
-              Self: Sized,
-    {
-        self.for_each(f);
-    }
-
     /// Combine all an iterator's elements into one element by using [`Extend`].
     ///
     /// This combinator will extend the first item with each of the rest of the
@@ -2139,17 +2348,20 @@
     ///            vec![1, 2, 3, 4, 5, 6]);
     /// ```
     fn concat(self) -> Self::Item
-        where Self: Sized,
-              Self::Item: Extend<<<Self as Iterator>::Item as IntoIterator>::Item> + IntoIterator + Default
+    where
+        Self: Sized,
+        Self::Item:
+            Extend<<<Self as Iterator>::Item as IntoIterator>::Item> + IntoIterator + Default,
     {
         concat(self)
     }
 
-    /// `.collect_vec()` is simply a type specialization of [`Iterator::collect`],
-    /// for convenience.
+    /// `.collect_vec()` is simply a type specialization of
+    /// [`Iterator::collect`], for convenience.
     #[cfg(feature = "use_alloc")]
     fn collect_vec(self) -> Vec<Self::Item>
-        where Self: Sized
+    where
+        Self: Sized,
     {
         self.collect()
     }
@@ -2165,16 +2377,18 @@
     ///
     /// fn process_dir_entries(entries: &[fs::DirEntry]) {
     ///     // ...
+    ///     # let _ = entries;
     /// }
     ///
-    /// fn do_stuff() -> std::io::Result<()> {
+    /// fn do_stuff() -> io::Result<()> {
     ///     let entries: Vec<_> = fs::read_dir(".")?.try_collect()?;
     ///     process_dir_entries(&entries);
     ///
     ///     Ok(())
     /// }
+    ///
+    /// # let _ = do_stuff;
     /// ```
-    #[cfg(feature = "use_alloc")]
     fn try_collect<T, U, E>(self) -> Result<U, E>
     where
         Self: Sized + Iterator<Item = Result<T, E>>,
@@ -2200,21 +2414,14 @@
     /// ```
     #[inline]
     fn set_from<'a, A: 'a, J>(&mut self, from: J) -> usize
-        where Self: Iterator<Item = &'a mut A>,
-              J: IntoIterator<Item = A>
+    where
+        Self: Iterator<Item = &'a mut A>,
+        J: IntoIterator<Item = A>,
     {
-        let mut count = 0;
-        for elt in from {
-            match self.next() {
-                None => break,
-                Some(ptr) => *ptr = elt,
-            }
-            count += 1;
-        }
-        count
+        from.into_iter().zip(self).map(|(new, old)| *old = new).count()
     }
 
-    /// Combine all iterator elements into one String, separated by `sep`.
+    /// Combine all iterator elements into one `String`, separated by `sep`.
     ///
     /// Use the `Display` implementation of each element.
     ///
@@ -2226,7 +2433,8 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn join(&mut self, sep: &str) -> String
-        where Self::Item: std::fmt::Display
+    where
+        Self::Item: std::fmt::Display,
     {
         match self.next() {
             None => String::new(),
@@ -2260,7 +2468,8 @@
     ///            "1.10, 2.72, -3.00");
     /// ```
     fn format(self, sep: &str) -> Format<Self>
-        where Self: Sized,
+    where
+        Self: Sized,
     {
         format::new_format_default(self, sep)
     }
@@ -2294,44 +2503,38 @@
     ///                              });
     /// assert_eq!(format!("{}", matrix_formatter),
     ///            "1, 2, 3\n4, 5, 6");
-    ///
-    ///
     /// ```
     fn format_with<F>(self, sep: &str, format: F) -> FormatWith<Self, F>
-        where Self: Sized,
-              F: FnMut(Self::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result,
+    where
+        Self: Sized,
+        F: FnMut(Self::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result,
     {
         format::new_format(self, sep, format)
     }
 
-    /// See [`.fold_ok()`](Itertools::fold_ok).
-    #[deprecated(note="Use .fold_ok() instead", since="0.10.0")]
-    fn fold_results<A, E, B, F>(&mut self, start: B, f: F) -> Result<B, E>
-        where Self: Iterator<Item = Result<A, E>>,
-              F: FnMut(B, A) -> B
-    {
-        self.fold_ok(start, f)
-    }
-
     /// Fold `Result` values from an iterator.
     ///
     /// Only `Ok` values are folded. If no error is encountered, the folded
     /// value is returned inside `Ok`. Otherwise, the operation terminates
-    /// and returns the first `Err` value it encounters. No iterator elements are
-    /// consumed after the first error.
+    /// and returns the first `Err` value it encounters. No iterator elements
+    /// are consumed after the first error.
     ///
     /// The first accumulator value is the `start` parameter.
-    /// Each iteration passes the accumulator value and the next value inside `Ok`
-    /// to the fold function `f` and its return value becomes the new accumulator value.
+    /// Each iteration passes the accumulator value and the next value inside
+    /// `Ok` to the fold function `f` and its return value becomes the new
+    /// accumulator value.
     ///
     /// For example the sequence *Ok(1), Ok(2), Ok(3)* will result in a
     /// computation like this:
     ///
-    /// ```ignore
+    /// ```no_run
+    /// # let start = 0;
+    /// # let f = |x, y| x + y;
     /// let mut accum = start;
     /// accum = f(accum, 1);
     /// accum = f(accum, 2);
     /// accum = f(accum, 3);
+    /// # let _ = accum;
     /// ```
     ///
     /// With a `start` value of 0 and an addition as folding function,
@@ -2356,8 +2559,9 @@
     /// );
     /// ```
     fn fold_ok<A, E, B, F>(&mut self, mut start: B, mut f: F) -> Result<B, E>
-        where Self: Iterator<Item = Result<A, E>>,
-              F: FnMut(B, A) -> B
+    where
+        Self: Iterator<Item = Result<A, E>>,
+        F: FnMut(B, A) -> B,
     {
         for elt in self {
             match elt {
@@ -2388,8 +2592,9 @@
     /// assert_eq!(more_values.next().unwrap(), Some(0));
     /// ```
     fn fold_options<A, B, F>(&mut self, mut start: B, mut f: F) -> Option<B>
-        where Self: Iterator<Item = Option<A>>,
-              F: FnMut(B, A) -> B
+    where
+        Self: Iterator<Item = Option<A>>,
+        F: FnMut(B, A) -> B,
     {
         for elt in self {
             match elt {
@@ -2412,10 +2617,14 @@
     /// assert_eq!((0..10).fold1(|x, y| x + y).unwrap_or(0), 45);
     /// assert_eq!((0..0).fold1(|x, y| x * y), None);
     /// ```
-    #[deprecated(since = "0.10.2", note = "Use `Iterator::reduce` instead")]
+    #[deprecated(
+        note = "Use [`Iterator::reduce`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.reduce) instead",
+        since = "0.10.2"
+    )]
     fn fold1<F>(mut self, f: F) -> Option<Self::Item>
-        where F: FnMut(Self::Item, Self::Item) -> Self::Item,
-              Self: Sized,
+    where
+        F: FnMut(Self::Item, Self::Item) -> Self::Item,
+        Self: Sized,
     {
         self.next().map(move |x| self.fold(x, f))
     }
@@ -2448,65 +2657,110 @@
     /// └─f─f─f─f─f─f
     /// ```
     ///
-    /// If `f` is associative, prefer the normal [`Iterator::reduce`] instead.
+    /// If `f` is associative you should also decide carefully:
+    ///
+    /// For an iterator producing `n` elements, both [`Iterator::reduce`] and
+    /// `tree_reduce` will call `f` `n - 1` times. However, `tree_reduce`
+    /// will call `f` on earlier intermediate results, which is beneficial
+    /// for `f` that allocate and produce longer results for longer
+    /// arguments. For example if `f` combines arguments using `format!`, then
+    /// `tree_reduce` will operate on average on shorter arguments resulting
+    /// in less bytes being allocated overall.
+    ///
+    /// Moreover, the output of `tree_reduce` is preferable to that of
+    /// [`Iterator::reduce`] in certain cases. For example, building a
+    /// binary search tree using `tree_reduce` will result in a balanced
+    /// tree with height `O(ln(n))`, while [`Iterator::reduce`] will output a
+    /// tree with height `O(n)`, essentially a linked list.
+    ///
+    /// If `f` does not benefit from such a reordering, like
+    /// `u32::wrapping_add`, prefer the normal [`Iterator::reduce`] instead
+    /// since it will most likely result in the generation of simpler code
+    /// because the compiler is able to optimize it.
     ///
     /// ```
     /// use itertools::Itertools;
     ///
+    /// let f = |a: String, b: String| {
+    ///     format!("f({a}, {b})")
+    /// };
+    ///
     /// // The same tree as above
-    /// let num_strings = (1..8).map(|x| x.to_string());
-    /// assert_eq!(num_strings.tree_fold1(|x, y| format!("f({}, {})", x, y)),
-    ///     Some(String::from("f(f(f(1, 2), f(3, 4)), f(f(5, 6), 7))")));
+    /// assert_eq!((1..8).map(|x| x.to_string()).tree_reduce(f),
+    ///            Some(String::from("f(f(f(1, 2), f(3, 4)), f(f(5, 6), 7))")));
     ///
-    /// // Like fold1, an empty iterator produces None
-    /// assert_eq!((0..0).tree_fold1(|x, y| x * y), None);
+    /// // Like reduce, an empty iterator produces None
+    /// assert_eq!((0..0).tree_reduce(|x, y| x * y), None);
     ///
-    /// // tree_fold1 matches fold1 for associative operations...
-    /// assert_eq!((0..10).tree_fold1(|x, y| x + y),
-    ///     (0..10).fold1(|x, y| x + y));
+    /// // tree_reduce matches reduce for associative operations...
+    /// assert_eq!((0..10).tree_reduce(|x, y| x + y),
+    ///     (0..10).reduce(|x, y| x + y));
+    ///
     /// // ...but not for non-associative ones
-    /// assert_ne!((0..10).tree_fold1(|x, y| x - y),
-    ///     (0..10).fold1(|x, y| x - y));
+    /// assert_ne!((0..10).tree_reduce(|x, y| x - y),
+    ///     (0..10).reduce(|x, y| x - y));
+    ///
+    /// let mut total_len_reduce = 0;
+    /// let reduce_res = (1..100).map(|x| x.to_string())
+    ///     .reduce(|a, b| {
+    ///         let r = f(a, b);
+    ///         total_len_reduce += r.len();
+    ///         r
+    ///     })
+    ///     .unwrap();
+    ///
+    /// let mut total_len_tree_reduce = 0;
+    /// let tree_reduce_res = (1..100).map(|x| x.to_string())
+    ///     .tree_reduce(|a, b| {
+    ///         let r = f(a, b);
+    ///         total_len_tree_reduce += r.len();
+    ///         r
+    ///     })
+    ///     .unwrap();
+    ///
+    /// assert_eq!(total_len_reduce, 33299);
+    /// assert_eq!(total_len_tree_reduce, 4228);
+    /// assert_eq!(reduce_res.len(), tree_reduce_res.len());
     /// ```
-    fn tree_fold1<F>(mut self, mut f: F) -> Option<Self::Item>
-        where F: FnMut(Self::Item, Self::Item) -> Self::Item,
-              Self: Sized,
+    fn tree_reduce<F>(mut self, mut f: F) -> Option<Self::Item>
+    where
+        F: FnMut(Self::Item, Self::Item) -> Self::Item,
+        Self: Sized,
     {
         type State<T> = Result<T, Option<T>>;
 
         fn inner0<T, II, FF>(it: &mut II, f: &mut FF) -> State<T>
-            where
-                II: Iterator<Item = T>,
-                FF: FnMut(T, T) -> T
+        where
+            II: Iterator<Item = T>,
+            FF: FnMut(T, T) -> T,
         {
             // This function could be replaced with `it.next().ok_or(None)`,
-            // but half the useful tree_fold1 work is combining adjacent items,
+            // but half the useful tree_reduce work is combining adjacent items,
             // so put that in a form that LLVM is more likely to optimize well.
 
-            let a =
-                if let Some(v) = it.next() { v }
-                else { return Err(None) };
-            let b =
-                if let Some(v) = it.next() { v }
-                else { return Err(Some(a)) };
+            let a = if let Some(v) = it.next() {
+                v
+            } else {
+                return Err(None);
+            };
+            let b = if let Some(v) = it.next() {
+                v
+            } else {
+                return Err(Some(a));
+            };
             Ok(f(a, b))
         }
 
         fn inner<T, II, FF>(stop: usize, it: &mut II, f: &mut FF) -> State<T>
-            where
-                II: Iterator<Item = T>,
-                FF: FnMut(T, T) -> T
+        where
+            II: Iterator<Item = T>,
+            FF: FnMut(T, T) -> T,
         {
             let mut x = inner0(it, f)?;
             for height in 0..stop {
                 // Try to get another tree the same size with which to combine it,
                 // creating a new tree that's twice as big for next time around.
-                let next =
-                    if height == 0 {
-                        inner0(it, f)
-                    } else {
-                        inner(height, it, f)
-                    };
+                let next = if height == 0 { inner0(it, f) } else { inner(height, it, f) };
                 match next {
                     Ok(y) => x = f(x, y),
 
@@ -2521,16 +2775,27 @@
             Ok(x)
         }
 
-        match inner(usize::max_value(), &mut self, &mut f) {
+        match inner(usize::MAX, &mut self, &mut f) {
             Err(x) => x,
             _ => unreachable!(),
         }
     }
 
-    /// An iterator method that applies a function, producing a single, final value.
+    /// See [`.tree_reduce()`](Itertools::tree_reduce).
+    #[deprecated(note = "Use .tree_reduce() instead", since = "0.13.0")]
+    fn tree_fold1<F>(self, f: F) -> Option<Self::Item>
+    where
+        F: FnMut(Self::Item, Self::Item) -> Self::Item,
+        Self: Sized,
+    {
+        self.tree_reduce(f)
+    }
+
+    /// An iterator method that applies a function, producing a single, final
+    /// value.
     ///
-    /// `fold_while()` is basically equivalent to [`Iterator::fold`] but with additional support for
-    /// early exit via short-circuiting.
+    /// `fold_while()` is basically equivalent to [`Iterator::fold`] but with
+    /// additional support for early exit via short-circuiting.
     ///
     /// ```
     /// use itertools::Itertools;
@@ -2563,23 +2828,24 @@
     /// assert_eq!(result2, result3);
     /// ```
     ///
-    /// The big difference between the computations of `result2` and `result3` is that while
-    /// `fold()` called the provided closure for every item of the callee iterator,
-    /// `fold_while()` actually stopped iterating as soon as it encountered `Fold::Done(_)`.
+    /// The big difference between the computations of `result2` and `result3`
+    /// is that while `fold()` called the provided closure for every item of
+    /// the callee iterator, `fold_while()` actually stopped iterating as
+    /// soon as it encountered `Fold::Done(_)`.
     fn fold_while<B, F>(&mut self, init: B, mut f: F) -> FoldWhile<B>
-        where Self: Sized,
-              F: FnMut(B, Self::Item) -> FoldWhile<B>
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> FoldWhile<B>,
     {
-        use Result::{
-            Ok as Continue,
-            Err as Break,
-        };
+        use Result::{Err as Break, Ok as Continue};
 
-        let result = self.try_fold(init, #[inline(always)] |acc, v|
-            match f(acc, v) {
-              FoldWhile::Continue(acc) => Continue(acc),
-              FoldWhile::Done(acc) => Break(acc),
-            }
+        let result = self.try_fold(
+            init,
+            #[inline(always)]
+            |acc, v| match f(acc, v) {
+                FoldWhile::Continue(acc) => Continue(acc),
+                FoldWhile::Done(acc) => Break(acc),
+            },
         );
 
         match result {
@@ -2594,9 +2860,9 @@
     ///
     /// # Panics
     ///
-    /// When calling `sum1()` and a primitive integer type is being returned, this
-    /// method will panic if the computation overflows and debug assertions are
-    /// enabled.
+    /// When calling `sum1()` and a primitive integer type is being returned,
+    /// this method will panic if the computation overflows and debug
+    /// assertions are enabled.
     ///
     /// # Examples
     ///
@@ -2610,11 +2876,11 @@
     /// assert_eq!(nonempty_sum, Some(55));
     /// ```
     fn sum1<S>(mut self) -> Option<S>
-        where Self: Sized,
-              S: std::iter::Sum<Self::Item>,
+    where
+        Self: Sized,
+        S: std::iter::Sum<Self::Item>,
     {
-        self.next()
-            .map(|first| once(first).chain(self).sum())
+        self.next().map(|first| once(first).chain(self).sum())
     }
 
     /// Iterate over the entire iterator and multiply all the elements.
@@ -2623,9 +2889,9 @@
     ///
     /// # Panics
     ///
-    /// When calling `product1()` and a primitive integer type is being returned,
-    /// method will panic if the computation overflows and debug assertions are
-    /// enabled.
+    /// When calling `product1()` and a primitive integer type is being
+    /// returned, method will panic if the computation overflows and debug
+    /// assertions are enabled.
     ///
     /// # Examples
     /// ```
@@ -2638,11 +2904,11 @@
     /// assert_eq!(nonempty_product, Some(3628800));
     /// ```
     fn product1<P>(mut self) -> Option<P>
-        where Self: Sized,
-              P: std::iter::Product<Self::Item>,
+    where
+        Self: Sized,
+        P: std::iter::Product<Self::Item>,
     {
-        self.next()
-            .map(|first| once(first).chain(self).product())
+        self.next().map(|first| once(first).chain(self).product())
     }
 
     /// Sort all iterator elements into a new iterator in ascending order.
@@ -2650,7 +2916,7 @@
     /// **Note:** This consumes the entire iterator, uses the
     /// [`slice::sort_unstable`] method and returns the result as a new
     /// iterator that owns its elements.
-    /// 
+    ///
     /// This sort is unstable (i.e., may reorder equal elements).
     ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
@@ -2666,8 +2932,9 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn sorted_unstable(self) -> VecIntoIter<Self::Item>
-        where Self: Sized,
-              Self::Item: Ord
+    where
+        Self: Sized,
+        Self::Item: Ord,
     {
         // Use .sort_unstable() directly since it is not quite identical with
         // .sort_by(Ord::cmp)
@@ -2681,7 +2948,7 @@
     /// **Note:** This consumes the entire iterator, uses the
     /// [`slice::sort_unstable_by`] method and returns the result as a new
     /// iterator that owns its elements.
-    /// 
+    ///
     /// This sort is unstable (i.e., may reorder equal elements).
     ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
@@ -2703,8 +2970,9 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn sorted_unstable_by<F>(self, cmp: F) -> VecIntoIter<Self::Item>
-        where Self: Sized,
-              F: FnMut(&Self::Item, &Self::Item) -> Ordering,
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
         let mut v = Vec::from_iter(self);
         v.sort_unstable_by(cmp);
@@ -2716,7 +2984,7 @@
     /// **Note:** This consumes the entire iterator, uses the
     /// [`slice::sort_unstable_by_key`] method and returns the result as a new
     /// iterator that owns its elements.
-    /// 
+    ///
     /// This sort is unstable (i.e., may reorder equal elements).
     ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
@@ -2738,9 +3006,10 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn sorted_unstable_by_key<K, F>(self, f: F) -> VecIntoIter<Self::Item>
-        where Self: Sized,
-              K: Ord,
-              F: FnMut(&Self::Item) -> K,
+    where
+        Self: Sized,
+        K: Ord,
+        F: FnMut(&Self::Item) -> K,
     {
         let mut v = Vec::from_iter(self);
         v.sort_unstable_by_key(f);
@@ -2752,7 +3021,7 @@
     /// **Note:** This consumes the entire iterator, uses the
     /// [`slice::sort`] method and returns the result as a new
     /// iterator that owns its elements.
-    /// 
+    ///
     /// This sort is stable (i.e., does not reorder equal elements).
     ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
@@ -2768,8 +3037,9 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn sorted(self) -> VecIntoIter<Self::Item>
-        where Self: Sized,
-              Self::Item: Ord
+    where
+        Self: Sized,
+        Self::Item: Ord,
     {
         // Use .sort() directly since it is not quite identical with
         // .sort_by(Ord::cmp)
@@ -2783,7 +3053,7 @@
     /// **Note:** This consumes the entire iterator, uses the
     /// [`slice::sort_by`] method and returns the result as a new
     /// iterator that owns its elements.
-    /// 
+    ///
     /// This sort is stable (i.e., does not reorder equal elements).
     ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
@@ -2805,8 +3075,9 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn sorted_by<F>(self, cmp: F) -> VecIntoIter<Self::Item>
-        where Self: Sized,
-              F: FnMut(&Self::Item, &Self::Item) -> Ordering,
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
         let mut v = Vec::from_iter(self);
         v.sort_by(cmp);
@@ -2818,7 +3089,7 @@
     /// **Note:** This consumes the entire iterator, uses the
     /// [`slice::sort_by_key`] method and returns the result as a new
     /// iterator that owns its elements.
-    /// 
+    ///
     /// This sort is stable (i.e., does not reorder equal elements).
     ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
@@ -2840,22 +3111,23 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn sorted_by_key<K, F>(self, f: F) -> VecIntoIter<Self::Item>
-        where Self: Sized,
-              K: Ord,
-              F: FnMut(&Self::Item) -> K,
+    where
+        Self: Sized,
+        K: Ord,
+        F: FnMut(&Self::Item) -> K,
     {
         let mut v = Vec::from_iter(self);
         v.sort_by_key(f);
         v.into_iter()
     }
 
-    /// Sort all iterator elements into a new iterator in ascending order. The key function is
-    /// called exactly once per key.
+    /// Sort all iterator elements into a new iterator in ascending order. The
+    /// key function is called exactly once per key.
     ///
     /// **Note:** This consumes the entire iterator, uses the
     /// [`slice::sort_by_cached_key`] method and returns the result as a new
     /// iterator that owns its elements.
-    /// 
+    ///
     /// This sort is stable (i.e., does not reorder equal elements).
     ///
     /// The sorted iterator, if directly collected to a `Vec`, is converted
@@ -2916,12 +3188,460 @@
     /// ```
     #[cfg(feature = "use_alloc")]
     fn k_smallest(self, k: usize) -> VecIntoIter<Self::Item>
-        where Self: Sized,
-              Self::Item: Ord
+    where
+        Self: Sized,
+        Self::Item: Ord,
     {
-        crate::k_smallest::k_smallest(self, k)
-            .into_sorted_vec()
-            .into_iter()
+        // The stdlib heap has optimised handling of "holes", which is not included in
+        // our heap implementation in k_smallest_general. While the difference
+        // is unlikely to have practical impact unless `Self::Item` is very large, this
+        // method uses the stdlib structure to maintain performance compared to
+        // previous versions of the crate.
+        use alloc::collections::BinaryHeap;
+
+        if k == 0 {
+            self.last();
+            return Vec::new().into_iter();
+        }
+        if k == 1 {
+            return self.min().into_iter().collect_vec().into_iter();
+        }
+
+        let mut iter = self.fuse();
+        let mut heap: BinaryHeap<_> = iter.by_ref().take(k).collect();
+
+        iter.for_each(|i| {
+            debug_assert_eq!(heap.len(), k);
+            // Equivalent to heap.push(min(i, heap.pop())) but more efficient.
+            // This should be done with a single `.peek_mut().unwrap()` but
+            //  `PeekMut` sifts-down unconditionally on Rust 1.46.0 and prior.
+            if *heap.peek().unwrap() > i {
+                *heap.peek_mut().unwrap() = i;
+            }
+        });
+
+        heap.into_sorted_vec().into_iter()
+    }
+
+    /// Sort the k smallest elements into a new iterator using the provided
+    /// comparison.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// This corresponds to `self.sorted_by(cmp).take(k)` in the same way that
+    /// [`k_smallest`](Itertools::k_smallest) corresponds to
+    /// `self.sorted().take(k)`, in both semantics and complexity.
+    ///
+    /// Particularly, a custom heap implementation ensures the comparison is not
+    /// cloned.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_smallest = numbers
+    ///     .into_iter()
+    ///     .k_smallest_by(5, |a, b| (a % 7).cmp(&(b % 7)).then(a.cmp(b)));
+    ///
+    /// itertools::assert_equal(five_smallest, vec![0, 7, 14, 1, 8]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_smallest_by<F>(self, k: usize, cmp: F) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
+    {
+        k_smallest::k_smallest_general(self, k, cmp).into_iter()
+    }
+
+    /// Return the elements producing the k smallest outputs of the provided
+    /// function.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// This corresponds to `self.sorted_by_key(key).take(k)` in the same way
+    /// that [`k_smallest`](Itertools::k_smallest) corresponds to
+    /// `self.sorted().take(k)`, in both semantics and complexity.
+    ///
+    /// Particularly, a custom heap implementation ensures the comparison is not
+    /// cloned.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_smallest = numbers
+    ///     .into_iter()
+    ///     .k_smallest_by_key(5, |n| (n % 7, *n));
+    ///
+    /// itertools::assert_equal(five_smallest, vec![0, 7, 14, 1, 8]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_smallest_by_key<F, K>(self, k: usize, key: F) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item) -> K,
+        K: Ord,
+    {
+        self.k_smallest_by(k, k_smallest::key_to_cmp(key))
+    }
+
+    /// Sort the k smallest elements into a new iterator, in ascending order,
+    /// relaxing the amount of memory required.
+    ///
+    /// **Note:** This consumes the entire iterator, and returns the result
+    /// as a new iterator that owns its elements.  If the input contains
+    /// less than k elements, the result is equivalent to `self.sorted()`.
+    ///
+    /// This is guaranteed to use `2 * k * sizeof(Self::Item) + O(1)` memory
+    /// and `O(n + k log k)` time, with `n` the number of elements in the input,
+    /// meaning it uses more memory than the minimum obtained by
+    /// [`k_smallest`](Itertools::k_smallest) but achieves linear time in
+    /// the number of elements.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// **Note:** This is functionally-equivalent to `self.sorted().take(k)`
+    /// but much more efficient.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_smallest = numbers
+    ///     .into_iter()
+    ///     .k_smallest_relaxed(5);
+    ///
+    /// itertools::assert_equal(five_smallest, 0..5);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_smallest_relaxed(self, k: usize) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        Self::Item: Ord,
+    {
+        self.k_smallest_relaxed_by(k, Ord::cmp)
+    }
+
+    /// Sort the k smallest elements into a new iterator using the provided
+    /// comparison, relaxing the amount of memory required.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// This corresponds to `self.sorted_by(cmp).take(k)` in the same way that
+    /// [`k_smallest_relaxed`](Itertools::k_smallest_relaxed) corresponds to
+    /// `self.sorted().take(k)`, in both semantics and complexity.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_smallest = numbers
+    ///     .into_iter()
+    ///     .k_smallest_relaxed_by(5, |a, b| (a % 7).cmp(&(b % 7)).then(a.cmp(b)));
+    ///
+    /// itertools::assert_equal(five_smallest, vec![0, 7, 14, 1, 8]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_smallest_relaxed_by<F>(self, k: usize, cmp: F) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
+    {
+        k_smallest::k_smallest_relaxed_general(self, k, cmp).into_iter()
+    }
+
+    /// Return the elements producing the k smallest outputs of the provided
+    /// function, relaxing the amount of memory required.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// This corresponds to `self.sorted_by_key(key).take(k)` in the same way
+    /// that [`k_smallest_relaxed`](Itertools::k_smallest_relaxed)
+    /// corresponds to `self.sorted().take(k)`, in both semantics and
+    /// complexity.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_smallest = numbers
+    ///     .into_iter()
+    ///     .k_smallest_relaxed_by_key(5, |n| (n % 7, *n));
+    ///
+    /// itertools::assert_equal(five_smallest, vec![0, 7, 14, 1, 8]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_smallest_relaxed_by_key<F, K>(self, k: usize, key: F) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item) -> K,
+        K: Ord,
+    {
+        self.k_smallest_relaxed_by(k, k_smallest::key_to_cmp(key))
+    }
+
+    /// Sort the k largest elements into a new iterator, in descending order.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// It is semantically equivalent to [`k_smallest`](Itertools::k_smallest)
+    /// with a reversed `Ord`.
+    /// However, this is implemented with a custom binary heap which does not
+    /// have the same performance characteristics for very large `Self::Item`.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_largest = numbers
+    ///     .into_iter()
+    ///     .k_largest(5);
+    ///
+    /// itertools::assert_equal(five_largest, vec![14, 13, 12, 11, 10]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_largest(self, k: usize) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        Self::Item: Ord,
+    {
+        self.k_largest_by(k, Self::Item::cmp)
+    }
+
+    /// Sort the k largest elements into a new iterator using the provided
+    /// comparison.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// Functionally equivalent to [`k_smallest_by`](Itertools::k_smallest_by)
+    /// with a reversed `Ord`.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_largest = numbers
+    ///     .into_iter()
+    ///     .k_largest_by(5, |a, b| (a % 7).cmp(&(b % 7)).then(a.cmp(b)));
+    ///
+    /// itertools::assert_equal(five_largest, vec![13, 6, 12, 5, 11]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_largest_by<F>(self, k: usize, mut cmp: F) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
+    {
+        self.k_smallest_by(k, move |a, b| cmp(b, a))
+    }
+
+    /// Return the elements producing the k largest outputs of the provided
+    /// function.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// Functionally equivalent to
+    /// [`k_smallest_by_key`](Itertools::k_smallest_by_key) with a reversed
+    /// `Ord`.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_largest = numbers
+    ///     .into_iter()
+    ///     .k_largest_by_key(5, |n| (n % 7, *n));
+    ///
+    /// itertools::assert_equal(five_largest, vec![13, 6, 12, 5, 11]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_largest_by_key<F, K>(self, k: usize, key: F) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item) -> K,
+        K: Ord,
+    {
+        self.k_largest_by(k, k_smallest::key_to_cmp(key))
+    }
+
+    /// Sort the k largest elements into a new iterator, in descending order,
+    /// relaxing the amount of memory required.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// It is semantically equivalent to
+    /// [`k_smallest_relaxed`](Itertools::k_smallest_relaxed)
+    /// with a reversed `Ord`.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_largest = numbers
+    ///     .into_iter()
+    ///     .k_largest_relaxed(5);
+    ///
+    /// itertools::assert_equal(five_largest, vec![14, 13, 12, 11, 10]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_largest_relaxed(self, k: usize) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        Self::Item: Ord,
+    {
+        self.k_largest_relaxed_by(k, Self::Item::cmp)
+    }
+
+    /// Sort the k largest elements into a new iterator using the provided
+    /// comparison, relaxing the amount of memory required.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// Functionally equivalent to
+    /// [`k_smallest_relaxed_by`](Itertools::k_smallest_relaxed_by)
+    /// with a reversed `Ord`.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_largest = numbers
+    ///     .into_iter()
+    ///     .k_largest_relaxed_by(5, |a, b| (a % 7).cmp(&(b % 7)).then(a.cmp(b)));
+    ///
+    /// itertools::assert_equal(five_largest, vec![13, 6, 12, 5, 11]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_largest_relaxed_by<F>(self, k: usize, mut cmp: F) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
+    {
+        self.k_smallest_relaxed_by(k, move |a, b| cmp(b, a))
+    }
+
+    /// Return the elements producing the k largest outputs of the provided
+    /// function, relaxing the amount of memory required.
+    ///
+    /// The sorted iterator, if directly collected to a `Vec`, is converted
+    /// without any extra copying or allocation cost.
+    ///
+    /// Functionally equivalent to
+    /// [`k_smallest_relaxed_by_key`](Itertools::k_smallest_relaxed_by_key)
+    /// with a reversed `Ord`.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// // A random permutation of 0..15
+    /// let numbers = vec![6, 9, 1, 14, 0, 4, 8, 7, 11, 2, 10, 3, 13, 12, 5];
+    ///
+    /// let five_largest = numbers
+    ///     .into_iter()
+    ///     .k_largest_relaxed_by_key(5, |n| (n % 7, *n));
+    ///
+    /// itertools::assert_equal(five_largest, vec![13, 6, 12, 5, 11]);
+    /// ```
+    #[cfg(feature = "use_alloc")]
+    fn k_largest_relaxed_by_key<F, K>(self, k: usize, key: F) -> VecIntoIter<Self::Item>
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item) -> K,
+        K: Ord,
+    {
+        self.k_largest_relaxed_by(k, k_smallest::key_to_cmp(key))
+    }
+
+    /// Consumes the iterator and return an iterator of the last `n` elements.
+    ///
+    /// The iterator, if directly collected to a `VecDeque`, is converted
+    /// without any extra copying or allocation cost.
+    /// If directly collected to a `Vec`, it may need some data movement
+    /// but no re-allocation.
+    ///
+    /// ```
+    /// use itertools::{assert_equal, Itertools};
+    ///
+    /// let v = vec![5, 9, 8, 4, 2, 12, 0];
+    /// assert_equal(v.iter().tail(3), &[2, 12, 0]);
+    /// assert_equal(v.iter().tail(10), &v);
+    ///
+    /// assert_equal(v.iter().tail(1), v.iter().last());
+    ///
+    /// assert_equal((0..100).tail(10), 90..100);
+    ///
+    /// assert_equal((0..100).filter(|x| x % 3 == 0).tail(10), (72..100).step_by(3));
+    /// ```
+    ///
+    /// For double ended iterators without side-effects, you might prefer
+    /// `.rev().take(n).rev()` to have a similar result (lazy and
+    /// non-allocating) without consuming the entire iterator.
+    #[cfg(feature = "use_alloc")]
+    fn tail(self, n: usize) -> VecDequeIntoIter<Self::Item>
+    where
+        Self: Sized,
+    {
+        match n {
+            0 => {
+                self.last();
+                VecDeque::new()
+            }
+            1 => self.last().into_iter().collect(),
+            _ => {
+                // Skip the starting part of the iterator if possible.
+                let (low, _) = self.size_hint();
+                let mut iter = self.fuse().skip(low.saturating_sub(n));
+                // TODO: If VecDeque has a more efficient method than
+                // `.pop_front();.push_back(val)` in the future then maybe revisit this.
+                let mut data: Vec<_> = iter.by_ref().take(n).collect();
+                // Update `data` cyclically.
+                let idx = iter.fold(0, |i, val| {
+                    debug_assert_eq!(data.len(), n);
+                    data[i] = val;
+                    if i + 1 == n {
+                        0
+                    } else {
+                        i + 1
+                    }
+                });
+                // Respect the insertion order, efficiently.
+                let mut data = VecDeque::from(data);
+                data.rotate_left(idx);
+                data
+            }
+        }
+        .into_iter()
     }
 
     /// Collect all iterator elements into one of two
@@ -2946,10 +3666,11 @@
     /// assert_eq!(failures, [false, true]);
     /// ```
     fn partition_map<A, B, F, L, R>(self, mut predicate: F) -> (A, B)
-        where Self: Sized,
-              F: FnMut(Self::Item) -> Either<L, R>,
-              A: Default + Extend<L>,
-              B: Default + Extend<R>,
+    where
+        Self: Sized,
+        F: FnMut(Self::Item) -> Either<L, R>,
+        A: Default + Extend<L>,
+        B: Default + Extend<R>,
     {
         let mut left = A::default();
         let mut right = B::default();
@@ -2978,10 +3699,10 @@
     /// assert_eq!(failures, [false, true]);
     /// ```
     fn partition_result<A, B, T, E>(self) -> (A, B)
-        where
-            Self: Iterator<Item = Result<T, E>> + Sized,
-            A: Default + Extend<T>,
-            B: Default + Extend<E>,
+    where
+        Self: Iterator<Item = Result<T, E>> + Sized,
+        A: Default + Extend<T>,
+        B: Default + Extend<E>,
     {
         self.partition_map(|r| match r {
             Ok(v) => Either::Left(v),
@@ -3007,16 +3728,19 @@
     /// ```
     #[cfg(feature = "use_std")]
     fn into_group_map<K, V>(self) -> HashMap<K, Vec<V>>
-        where Self: Iterator<Item=(K, V)> + Sized,
-              K: Hash + Eq,
+    where
+        Self: Iterator<Item = (K, V)> + Sized,
+        K: Hash + Eq,
     {
         group_map::into_group_map(self)
     }
 
-    /// Return an `Iterator` on a `HashMap`. Keys mapped to `Vec`s of values. The key is specified
-    /// in the closure.
+    /// Return a `HashMap` of keys mapped to `Vec`s of values. The key is
+    /// specified in the closure. The values are taken from the input
+    /// iterator.
     ///
-    /// Essentially a shorthand for `.into_grouping_map_by(f).collect::<Vec<_>>()`.
+    /// Essentially a shorthand for
+    /// `.into_grouping_map_by(f).collect::<Vec<_>>()`.
     ///
     /// ```
     /// use itertools::Itertools;
@@ -3026,7 +3750,7 @@
     /// let lookup: HashMap<u32,Vec<(u32, u32)>> =
     ///     data.clone().into_iter().into_group_map_by(|a| a.0);
     ///
-    /// assert_eq!(lookup[&0], vec![(0,10),(0,20)]);
+    /// assert_eq!(lookup[&0], vec![(0,10), (0,20)]);
     /// assert_eq!(lookup.get(&1), None);
     /// assert_eq!(lookup[&2], vec![(2,12), (2,42)]);
     /// assert_eq!(lookup[&3], vec![(3,13), (3,33)]);
@@ -3042,10 +3766,10 @@
     /// ```
     #[cfg(feature = "use_std")]
     fn into_group_map_by<K, V, F>(self, f: F) -> HashMap<K, Vec<V>>
-        where
-            Self: Iterator<Item=V> + Sized,
-            K: Hash + Eq,
-            F: Fn(&V) -> K,
+    where
+        Self: Iterator<Item = V> + Sized,
+        K: Hash + Eq,
+        F: FnMut(&V) -> K,
     {
         group_map::into_group_map_by(self, f)
     }
@@ -3061,8 +3785,9 @@
     /// on what operations are available.
     #[cfg(feature = "use_std")]
     fn into_grouping_map<K, V>(self) -> GroupingMap<Self>
-        where Self: Iterator<Item=(K, V)> + Sized,
-              K: Hash + Eq,
+    where
+        Self: Iterator<Item = (K, V)> + Sized,
+        K: Hash + Eq,
     {
         grouping_map::new(self)
     }
@@ -3070,18 +3795,20 @@
     /// Constructs a `GroupingMap` to be used later with one of the efficient
     /// group-and-fold operations it allows to perform.
     ///
-    /// The values from this iterator will be used as values for the folding operation
-    /// while the keys will be obtained from the values by calling `key_mapper`.
+    /// The values from this iterator will be used as values for the folding
+    /// operation while the keys will be obtained from the values by calling
+    /// `key_mapper`.
     ///
     /// See [`GroupingMap`] for more informations
     /// on what operations are available.
     #[cfg(feature = "use_std")]
     fn into_grouping_map_by<K, V, F>(self, key_mapper: F) -> GroupingMapBy<Self, F>
-        where Self: Iterator<Item=V> + Sized,
-              K: Hash + Eq,
-              F: FnMut(&V) -> K
+    where
+        Self: Iterator<Item = V> + Sized,
+        K: Hash + Eq,
+        F: FnMut(&V) -> K,
     {
-        grouping_map::new(grouping_map::MapForGrouping::new(self, key_mapper))
+        grouping_map::new(grouping_map::new_map_for_grouping(self, key_mapper))
     }
 
     /// Return all minimum elements of an iterator.
@@ -3106,9 +3833,11 @@
     ///
     /// The elements can be floats but no particular result is guaranteed
     /// if an element is NaN.
-    #[cfg(feature = "use_std")]
+    #[cfg(feature = "use_alloc")]
     fn min_set(self) -> Vec<Self::Item>
-        where Self: Sized, Self::Item: Ord
+    where
+        Self: Sized,
+        Self::Item: Ord,
     {
         extrema_set::min_set_impl(self, |_| (), |x, y, _, _| x.cmp(y))
     }
@@ -3137,15 +3866,13 @@
     ///
     /// The elements can be floats but no particular result is guaranteed
     /// if an element is NaN.
-    #[cfg(feature = "use_std")]
+    #[cfg(feature = "use_alloc")]
     fn min_set_by<F>(self, mut compare: F) -> Vec<Self::Item>
-        where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
-        extrema_set::min_set_impl(
-            self,
-            |_| (),
-            |x, y, _, _| compare(x, y)
-        )
+        extrema_set::min_set_impl(self, |_| (), |x, y, _, _| compare(x, y))
     }
 
     /// Return all minimum elements of an iterator, as determined by
@@ -3171,9 +3898,12 @@
     ///
     /// The elements can be floats but no particular result is guaranteed
     /// if an element is NaN.
-    #[cfg(feature = "use_std")]
+    #[cfg(feature = "use_alloc")]
     fn min_set_by_key<K, F>(self, key: F) -> Vec<Self::Item>
-        where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K
+    where
+        Self: Sized,
+        K: Ord,
+        F: FnMut(&Self::Item) -> K,
     {
         extrema_set::min_set_impl(self, key, |_, _, kx, ky| kx.cmp(ky))
     }
@@ -3200,9 +3930,11 @@
     ///
     /// The elements can be floats but no particular result is guaranteed
     /// if an element is NaN.
-    #[cfg(feature = "use_std")]
+    #[cfg(feature = "use_alloc")]
     fn max_set(self) -> Vec<Self::Item>
-        where Self: Sized, Self::Item: Ord
+    where
+        Self: Sized,
+        Self::Item: Ord,
     {
         extrema_set::max_set_impl(self, |_| (), |x, y, _, _| x.cmp(y))
     }
@@ -3231,15 +3963,13 @@
     ///
     /// The elements can be floats but no particular result is guaranteed
     /// if an element is NaN.
-    #[cfg(feature = "use_std")]
+    #[cfg(feature = "use_alloc")]
     fn max_set_by<F>(self, mut compare: F) -> Vec<Self::Item>
-        where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
-        extrema_set::max_set_impl(
-            self,
-            |_| (),
-            |x, y, _, _| compare(x, y)
-        )
+        extrema_set::max_set_impl(self, |_| (), |x, y, _, _| compare(x, y))
     }
 
     /// Return all maximum elements of an iterator, as determined by
@@ -3265,9 +3995,12 @@
     ///
     /// The elements can be floats but no particular result is guaranteed
     /// if an element is NaN.
-    #[cfg(feature = "use_std")]
+    #[cfg(feature = "use_alloc")]
     fn max_set_by_key<K, F>(self, key: F) -> Vec<Self::Item>
-        where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K
+    where
+        Self: Sized,
+        K: Ord,
+        F: FnMut(&Self::Item) -> K,
     {
         extrema_set::max_set_impl(self, key, |_, _, kx, ky| kx.cmp(ky))
     }
@@ -3278,9 +4011,9 @@
     ///
     /// - `NoElements` if the iterator is empty.
     /// - `OneElement(x)` if the iterator has exactly one element.
-    /// - `MinMax(x, y)` is returned otherwise, where `x <= y`. Two
-    ///    values are equal if and only if there is more than one
-    ///    element in the iterator and all elements are equal.
+    /// - `MinMax(x, y)` is returned otherwise, where `x <= y`. Two values are
+    ///   equal if and only if there is more than one element in the iterator
+    ///   and all elements are equal.
     ///
     /// On an iterator of length `n`, `minmax` does `1.5 * n` comparisons,
     /// and so is faster than calling `min` and `max` separately which does
@@ -3308,7 +4041,9 @@
     /// The elements can be floats but no particular result is guaranteed
     /// if an element is NaN.
     fn minmax(self) -> MinMaxResult<Self::Item>
-        where Self: Sized, Self::Item: PartialOrd
+    where
+        Self: Sized,
+        Self::Item: PartialOrd,
     {
         minmax::minmax_impl(self, |_| (), |x, y, _, _| x < y)
     }
@@ -3316,16 +4051,20 @@
     /// Return the minimum and maximum element of an iterator, as determined by
     /// the specified function.
     ///
-    /// The return value is a variant of [`MinMaxResult`] like for [`.minmax()`](Itertools::minmax).
+    /// The return value is a variant of [`MinMaxResult`] like for
+    /// [`.minmax()`](Itertools::minmax).
     ///
-    /// For the minimum, the first minimal element is returned.  For the maximum,
-    /// the last maximal element wins.  This matches the behavior of the standard
-    /// [`Iterator::min`] and [`Iterator::max`] methods.
+    /// For the minimum, the first minimal element is returned.  For the
+    /// maximum, the last maximal element wins.  This matches the behavior
+    /// of the standard [`Iterator::min`] and [`Iterator::max`] methods.
     ///
     /// The keys can be floats but no particular result is guaranteed
     /// if a key is NaN.
     fn minmax_by_key<K, F>(self, key: F) -> MinMaxResult<Self::Item>
-        where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K
+    where
+        Self: Sized,
+        K: PartialOrd,
+        F: FnMut(&Self::Item) -> K,
     {
         minmax::minmax_impl(self, key, |_, _, xk, yk| xk < yk)
     }
@@ -3333,19 +4072,18 @@
     /// Return the minimum and maximum element of an iterator, as determined by
     /// the specified comparison function.
     ///
-    /// The return value is a variant of [`MinMaxResult`] like for [`.minmax()`](Itertools::minmax).
+    /// The return value is a variant of [`MinMaxResult`] like for
+    /// [`.minmax()`](Itertools::minmax).
     ///
-    /// For the minimum, the first minimal element is returned.  For the maximum,
-    /// the last maximal element wins.  This matches the behavior of the standard
-    /// [`Iterator::min`] and [`Iterator::max`] methods.
+    /// For the minimum, the first minimal element is returned.  For the
+    /// maximum, the last maximal element wins.  This matches the behavior
+    /// of the standard [`Iterator::min`] and [`Iterator::max`] methods.
     fn minmax_by<F>(self, mut compare: F) -> MinMaxResult<Self::Item>
-        where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
-        minmax::minmax_impl(
-            self,
-            |_| (),
-            |x, y, _, _| Ordering::Less == compare(x, y)
-        )
+        minmax::minmax_impl(self, |_| (), |x, y, _, _| Ordering::Less == compare(x, y))
     }
 
     /// Return the position of the maximum element in the iterator.
@@ -3368,11 +4106,11 @@
     /// assert_eq!(a.iter().position_max(), Some(1));
     /// ```
     fn position_max(self) -> Option<usize>
-        where Self: Sized, Self::Item: Ord
+    where
+        Self: Sized,
+        Self::Item: Ord,
     {
-        self.enumerate()
-            .max_by(|x, y| Ord::cmp(&x.1, &y.1))
-            .map(|x| x.0)
+        self.enumerate().max_by(|x, y| Ord::cmp(&x.1, &y.1)).map(|x| x.0)
     }
 
     /// Return the position of the maximum element in the iterator, as
@@ -3396,11 +4134,12 @@
     /// assert_eq!(a.iter().position_max_by_key(|x| x.abs()), Some(3));
     /// ```
     fn position_max_by_key<K, F>(self, mut key: F) -> Option<usize>
-        where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K
+    where
+        Self: Sized,
+        K: Ord,
+        F: FnMut(&Self::Item) -> K,
     {
-        self.enumerate()
-            .max_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1)))
-            .map(|x| x.0)
+        self.enumerate().max_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1))).map(|x| x.0)
     }
 
     /// Return the position of the maximum element in the iterator, as
@@ -3424,11 +4163,11 @@
     /// assert_eq!(a.iter().position_max_by(|x, y| x.cmp(y)), Some(1));
     /// ```
     fn position_max_by<F>(self, mut compare: F) -> Option<usize>
-        where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
-        self.enumerate()
-            .max_by(|x, y| compare(&x.1, &y.1))
-            .map(|x| x.0)
+        self.enumerate().max_by(|x, y| compare(&x.1, &y.1)).map(|x| x.0)
     }
 
     /// Return the position of the minimum element in the iterator.
@@ -3451,11 +4190,11 @@
     /// assert_eq!(a.iter().position_min(), Some(2));
     /// ```
     fn position_min(self) -> Option<usize>
-        where Self: Sized, Self::Item: Ord
+    where
+        Self: Sized,
+        Self::Item: Ord,
     {
-        self.enumerate()
-            .min_by(|x, y| Ord::cmp(&x.1, &y.1))
-            .map(|x| x.0)
+        self.enumerate().min_by(|x, y| Ord::cmp(&x.1, &y.1)).map(|x| x.0)
     }
 
     /// Return the position of the minimum element in the iterator, as
@@ -3479,11 +4218,12 @@
     /// assert_eq!(a.iter().position_min_by_key(|x| x.abs()), Some(0));
     /// ```
     fn position_min_by_key<K, F>(self, mut key: F) -> Option<usize>
-        where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K
+    where
+        Self: Sized,
+        K: Ord,
+        F: FnMut(&Self::Item) -> K,
     {
-        self.enumerate()
-            .min_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1)))
-            .map(|x| x.0)
+        self.enumerate().min_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1))).map(|x| x.0)
     }
 
     /// Return the position of the minimum element in the iterator, as
@@ -3507,11 +4247,11 @@
     /// assert_eq!(a.iter().position_min_by(|x, y| x.cmp(y)), Some(2));
     /// ```
     fn position_min_by<F>(self, mut compare: F) -> Option<usize>
-        where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
-        self.enumerate()
-            .min_by(|x, y| compare(&x.1, &y.1))
-            .map(|x| x.0)
+        self.enumerate().min_by(|x, y| compare(&x.1, &y.1)).map(|x| x.0)
     }
 
     /// Return the positions of the minimum and maximum elements in
@@ -3521,10 +4261,9 @@
     ///
     /// - `NoElements` if the iterator is empty.
     /// - `OneElement(xpos)` if the iterator has exactly one element.
-    /// - `MinMax(xpos, ypos)` is returned otherwise, where the
-    ///    element at `xpos` ≤ the element at `ypos`. While the
-    ///    referenced elements themselves may be equal, `xpos` cannot
-    ///    be equal to `ypos`.
+    /// - `MinMax(xpos, ypos)` is returned otherwise, where the element at
+    ///   `xpos` ≤ the element at `ypos`. While the referenced elements
+    ///   themselves may be equal, `xpos` cannot be equal to `ypos`.
     ///
     /// On an iterator of length `n`, `position_minmax` does `1.5 * n`
     /// comparisons, and so is faster than calling `position_min` and
@@ -3557,9 +4296,11 @@
     /// assert_eq!(a.iter().position_minmax(), MinMax(2, 1));
     /// ```
     fn position_minmax(self) -> MinMaxResult<usize>
-        where Self: Sized, Self::Item: PartialOrd
+    where
+        Self: Sized,
+        Self::Item: PartialOrd,
     {
-        use crate::MinMaxResult::{NoElements, OneElement, MinMax};
+        use crate::MinMaxResult::{MinMax, NoElements, OneElement};
         match minmax::minmax_impl(self.enumerate(), |_| (), |x, y, _, _| x.1 < y.1) {
             NoElements => NoElements,
             OneElement(x) => OneElement(x.0),
@@ -3602,9 +4343,12 @@
     ///
     /// [`position_minmax`]: Self::position_minmax
     fn position_minmax_by_key<K, F>(self, mut key: F) -> MinMaxResult<usize>
-        where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K
+    where
+        Self: Sized,
+        K: PartialOrd,
+        F: FnMut(&Self::Item) -> K,
     {
-        use crate::MinMaxResult::{NoElements, OneElement, MinMax};
+        use crate::MinMaxResult::{MinMax, NoElements, OneElement};
         match self.enumerate().minmax_by_key(|e| key(&e.1)) {
             NoElements => NoElements,
             OneElement(x) => OneElement(x.0),
@@ -3644,9 +4388,11 @@
     ///
     /// [`position_minmax`]: Self::position_minmax
     fn position_minmax_by<F>(self, mut compare: F) -> MinMaxResult<usize>
-        where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
+    where
+        Self: Sized,
+        F: FnMut(&Self::Item, &Self::Item) -> Ordering,
     {
-        use crate::MinMaxResult::{NoElements, OneElement, MinMax};
+        use crate::MinMaxResult::{MinMax, NoElements, OneElement};
         match self.enumerate().minmax_by(|x, y| compare(&x.1, &y.1)) {
             NoElements => NoElements,
             OneElement(x) => OneElement(x.0),
@@ -3654,13 +4400,14 @@
         }
     }
 
-    /// If the iterator yields exactly one element, that element will be returned, otherwise
-    /// an error will be returned containing an iterator that has the same output as the input
-    /// iterator.
+    /// If the iterator yields exactly one element, that element will be
+    /// returned, otherwise an error will be returned containing an iterator
+    /// that has the same output as the input iterator.
     ///
-    /// This provides an additional layer of validation over just calling `Iterator::next()`.
-    /// If your assumption that there should only be one element yielded is false this provides
-    /// the opportunity to detect and handle that, preventing errors at a distance.
+    /// This provides an additional layer of validation over just calling
+    /// `Iterator::next()`. If your assumption that there should only be one
+    /// element yielded is false this provides the opportunity to detect and
+    /// handle that, preventing errors at a distance.
     ///
     /// # Examples
     /// ```
@@ -3676,27 +4423,25 @@
         Self: Sized,
     {
         match self.next() {
-            Some(first) => {
-                match self.next() {
-                    Some(second) => {
-                        Err(ExactlyOneError::new(Some(Either::Left([first, second])), self))
-                    }
-                    None => {
-                        Ok(first)
-                    }
+            Some(first) => match self.next() {
+                Some(second) => {
+                    Err(ExactlyOneError::new(Some(Either::Left([first, second])), self))
                 }
-            }
+                None => Ok(first),
+            },
             None => Err(ExactlyOneError::new(None, self)),
         }
     }
 
-    /// If the iterator yields no elements, Ok(None) will be returned. If the iterator yields
-    /// exactly one element, that element will be returned, otherwise an error will be returned
-    /// containing an iterator that has the same output as the input iterator.
+    /// If the iterator yields no elements, `Ok(None)` will be returned. If the
+    /// iterator yields exactly one element, that element will be returned,
+    /// otherwise an error will be returned containing an iterator that has
+    /// the same output as the input iterator.
     ///
-    /// This provides an additional layer of validation over just calling `Iterator::next()`.
-    /// If your assumption that there should be at most one element yielded is false this provides
-    /// the opportunity to detect and handle that, preventing errors at a distance.
+    /// This provides an additional layer of validation over just calling
+    /// `Iterator::next()`. If your assumption that there should be at most
+    /// one element yielded is false this provides the opportunity to detect
+    /// and handle that, preventing errors at a distance.
     ///
     /// # Examples
     /// ```
@@ -3712,16 +4457,12 @@
         Self: Sized,
     {
         match self.next() {
-            Some(first) => {
-                match self.next() {
-                    Some(second) => {
-                        Err(ExactlyOneError::new(Some(Either::Left([first, second])), self))
-                    }
-                    None => {
-                        Ok(Some(first))
-                    }
+            Some(first) => match self.next() {
+                Some(second) => {
+                    Err(ExactlyOneError::new(Some(Either::Left([first, second])), self))
                 }
-            }
+                None => Ok(Some(first)),
+            },
             None => Ok(None),
         }
     }
@@ -3755,7 +4496,7 @@
     /// # Examples
     /// ```
     /// # use itertools::Itertools;
-    /// let counts = [1, 1, 1, 3, 3, 5].into_iter().counts();
+    /// let counts = [1, 1, 1, 3, 3, 5].iter().counts();
     /// assert_eq!(counts[&1], 3);
     /// assert_eq!(counts[&3], 2);
     /// assert_eq!(counts[&5], 1);
@@ -3781,6 +4522,7 @@
     /// # use itertools::Itertools;
     /// struct Character {
     ///   first_name: &'static str,
+    ///   # #[allow(dead_code)]
     ///   last_name:  &'static str,
     /// }
     ///
@@ -3816,8 +4558,8 @@
 
     /// Converts an iterator of tuples into a tuple of containers.
     ///
-    /// `unzip()` consumes an entire iterator of n-ary tuples, producing `n` collections, one for each
-    /// column.
+    /// It consumes an entire iterator of n-ary tuples, producing `n`
+    /// collections, one for each column.
     ///
     /// This function is, in some sense, the opposite of [`multizip`].
     ///
@@ -3840,9 +4582,33 @@
     {
         MultiUnzip::multiunzip(self)
     }
+
+    /// Returns the length of the iterator if one exists.
+    /// Otherwise return `self.size_hint()`.
+    ///
+    /// Fallible [`ExactSizeIterator::len`].
+    ///
+    /// Inherits guarantees and restrictions from [`Iterator::size_hint`].
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// assert_eq!([0; 10].iter().try_len(), Ok(10));
+    /// assert_eq!((10..15).try_len(), Ok(5));
+    /// assert_eq!((15..10).try_len(), Ok(0));
+    /// assert_eq!((10..).try_len(), Err((usize::MAX, None)));
+    /// assert_eq!((10..15).filter(|x| x % 2 == 0).try_len(), Err((0, Some(5))));
+    /// ```
+    fn try_len(&self) -> Result<usize, size_hint::SizeHint> {
+        let sh = self.size_hint();
+        match sh {
+            (lo, Some(hi)) if lo == hi => Ok(lo),
+            _ => Err(sh),
+        }
+    }
 }
 
-impl<T: ?Sized> Itertools for T where T: Iterator { }
+impl<T> Itertools for T where T: Iterator + ?Sized {}
 
 /// Return `true` if both iterables produce equal sequences
 /// (elements pairwise equal and sequences of the same length),
@@ -3855,9 +4621,10 @@
 /// assert!(!itertools::equal(&[0, 0], &[0, 0, 0]));
 /// ```
 pub fn equal<I, J>(a: I, b: J) -> bool
-    where I: IntoIterator,
-          J: IntoIterator,
-          I::Item: PartialEq<J::Item>
+where
+    I: IntoIterator,
+    J: IntoIterator,
+    I::Item: PartialEq<J::Item>,
 {
     a.into_iter().eq(b)
 }
@@ -3866,31 +4633,39 @@
 /// semantics as [`equal(a, b)`](equal).
 ///
 /// **Panics** on assertion failure with a message that shows the
-/// two iteration elements.
+/// two different elements and the iteration index.
 ///
-/// ```ignore
+/// ```should_panic
+/// # use itertools::assert_equal;
 /// assert_equal("exceed".split('c'), "excess".split('c'));
-/// // ^PANIC: panicked at 'Failed assertion Some("eed") == Some("ess") for iteration 1',
+/// // ^PANIC: panicked at 'Failed assertion Some("eed") == Some("ess") for iteration 1'.
 /// ```
+#[track_caller]
 pub fn assert_equal<I, J>(a: I, b: J)
-    where I: IntoIterator,
-          J: IntoIterator,
-          I::Item: fmt::Debug + PartialEq<J::Item>,
-          J::Item: fmt::Debug,
+where
+    I: IntoIterator,
+    J: IntoIterator,
+    I::Item: fmt::Debug + PartialEq<J::Item>,
+    J::Item: fmt::Debug,
 {
     let mut ia = a.into_iter();
     let mut ib = b.into_iter();
-    let mut i = 0;
+    let mut i: usize = 0;
     loop {
         match (ia.next(), ib.next()) {
             (None, None) => return,
             (a, b) => {
                 let equal = match (&a, &b) {
-                    (&Some(ref a), &Some(ref b)) => a == b,
+                    (Some(a), Some(b)) => a == b,
                     _ => false,
                 };
-                assert!(equal, "Failed assertion {a:?} == {b:?} for iteration {i}",
-                        i=i, a=a, b=b);
+                assert!(
+                    equal,
+                    "Failed assertion {a:?} == {b:?} for iteration {i}",
+                    i = i,
+                    a = a,
+                    b = b
+                );
                 i += 1;
             }
         }
@@ -3915,22 +4690,18 @@
 /// assert_eq!(split_index, 3);
 /// ```
 pub fn partition<'a, A: 'a, I, F>(iter: I, mut pred: F) -> usize
-    where I: IntoIterator<Item = &'a mut A>,
-          I::IntoIter: DoubleEndedIterator,
-          F: FnMut(&A) -> bool
+where
+    I: IntoIterator<Item = &'a mut A>,
+    I::IntoIter: DoubleEndedIterator,
+    F: FnMut(&A) -> bool,
 {
     let mut split_index = 0;
     let mut iter = iter.into_iter();
-    'main: while let Some(front) = iter.next() {
+    while let Some(front) = iter.next() {
         if !pred(front) {
-            loop {
-                match iter.next_back() {
-                    Some(back) => if pred(back) {
-                        std::mem::swap(front, back);
-                        break;
-                    },
-                    None => break 'main,
-                }
+            match iter.rfind(|back| pred(back)) {
+                Some(back) => std::mem::swap(front, back),
+                None => break,
             }
         }
         split_index += 1;
@@ -3953,15 +4724,15 @@
     /// Return the value in the continue or done.
     pub fn into_inner(self) -> T {
         match self {
-            FoldWhile::Continue(x) | FoldWhile::Done(x) => x,
+            Self::Continue(x) | Self::Done(x) => x,
         }
     }
 
     /// Return true if `self` is `Done`, false if it is `Continue`.
     pub fn is_done(&self) -> bool {
         match *self {
-            FoldWhile::Continue(_) => false,
-            FoldWhile::Done(_) => true,
+            Self::Continue(_) => false,
+            Self::Done(_) => true,
         }
     }
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/merge_join.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/merge_join.rs
new file mode 100644
index 0000000..1d5dca56
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/merge_join.rs
@@ -0,0 +1,353 @@
+use std::cmp::Ordering;
+use std::fmt;
+use std::iter::{Fuse, FusedIterator};
+use std::marker::PhantomData;
+
+use either::Either;
+
+use super::adaptors::{put_back, PutBack};
+use crate::either_or_both::EitherOrBoth;
+use crate::size_hint::{self, SizeHint};
+#[cfg(doc)]
+use crate::Itertools;
+
+#[derive(Clone, Debug)]
+pub struct MergeLte;
+
+/// An iterator adaptor that merges the two base iterators in ascending order.
+/// If both base iterators are sorted (ascending), the result is sorted.
+///
+/// Iterator element type is `I::Item`.
+///
+/// See [`.merge()`](crate::Itertools::merge_by) for more information.
+pub type Merge<I, J> = MergeBy<I, J, MergeLte>;
+
+/// Create an iterator that merges elements in `i` and `j`.
+///
+/// [`IntoIterator`] enabled version of
+/// [`Itertools::merge`](crate::Itertools::merge).
+///
+/// ```
+/// use itertools::merge;
+///
+/// for elt in merge(&[1, 2, 3], &[2, 3, 4]) {
+///     /* loop body */
+///     # let _ = elt;
+/// }
+/// ```
+pub fn merge<I, J>(
+    i: I,
+    j: J,
+) -> Merge<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
+where
+    I: IntoIterator,
+    J: IntoIterator<Item = I::Item>,
+    I::Item: PartialOrd,
+{
+    merge_by_new(i, j, MergeLte)
+}
+
+/// An iterator adaptor that merges the two base iterators in ascending order.
+/// If both base iterators are sorted (ascending), the result is sorted.
+///
+/// Iterator element type is `I::Item`.
+///
+/// See [`.merge_by()`](crate::Itertools::merge_by) for more information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct MergeBy<I: Iterator, J: Iterator, F> {
+    left: PutBack<Fuse<I>>,
+    right: PutBack<Fuse<J>>,
+    cmp_fn: F,
+}
+
+/// Create a `MergeBy` iterator.
+pub fn merge_by_new<I, J, F>(a: I, b: J, cmp: F) -> MergeBy<I::IntoIter, J::IntoIter, F>
+where
+    I: IntoIterator,
+    J: IntoIterator<Item = I::Item>,
+{
+    MergeBy {
+        left: put_back(a.into_iter().fuse()),
+        right: put_back(b.into_iter().fuse()),
+        cmp_fn: cmp,
+    }
+}
+
+/// Return an iterator adaptor that merge-joins items from the two base
+/// iterators in ascending order.
+///
+/// [`IntoIterator`] enabled version of [`Itertools::merge_join_by`].
+pub fn merge_join_by<I, J, F, T>(
+    left: I,
+    right: J,
+    cmp_fn: F,
+) -> MergeJoinBy<I::IntoIter, J::IntoIter, F>
+where
+    I: IntoIterator,
+    J: IntoIterator,
+    F: FnMut(&I::Item, &J::Item) -> T,
+{
+    MergeBy {
+        left: put_back(left.into_iter().fuse()),
+        right: put_back(right.into_iter().fuse()),
+        cmp_fn: MergeFuncLR(cmp_fn, PhantomData),
+    }
+}
+
+/// An iterator adaptor that merge-joins items from the two base iterators in
+/// ascending order.
+///
+/// See [`.merge_join_by()`](crate::Itertools::merge_join_by) for more
+/// information.
+pub type MergeJoinBy<I, J, F> =
+    MergeBy<I, J, MergeFuncLR<F, <F as FuncLR<<I as Iterator>::Item, <J as Iterator>::Item>>::T>>;
+
+#[derive(Clone, Debug)]
+pub struct MergeFuncLR<F, T>(F, PhantomData<T>);
+
+pub trait FuncLR<L, R> {
+    type T;
+}
+
+impl<L, R, T, F: FnMut(&L, &R) -> T> FuncLR<L, R> for F {
+    type T = T;
+}
+
+pub trait OrderingOrBool<L, R> {
+    type MergeResult;
+    fn left(left: L) -> Self::MergeResult;
+    fn right(right: R) -> Self::MergeResult;
+    // "merge" never returns (Some(...), Some(...), ...) so Option<Either<I::Item,
+    // J::Item>> is appealing but it is always followed by two put_backs, so we
+    // think the compiler is smart enough to optimize it. Or we could move
+    // put_backs into "merge".
+    fn merge(&mut self, left: L, right: R) -> (Option<Either<L, R>>, Self::MergeResult);
+    fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint;
+}
+
+impl<L, R, F: FnMut(&L, &R) -> Ordering> OrderingOrBool<L, R> for MergeFuncLR<F, Ordering> {
+    type MergeResult = EitherOrBoth<L, R>;
+    fn left(left: L) -> Self::MergeResult {
+        EitherOrBoth::Left(left)
+    }
+    fn right(right: R) -> Self::MergeResult {
+        EitherOrBoth::Right(right)
+    }
+    fn merge(&mut self, left: L, right: R) -> (Option<Either<L, R>>, Self::MergeResult) {
+        match self.0(&left, &right) {
+            Ordering::Equal => (None, EitherOrBoth::Both(left, right)),
+            Ordering::Less => (Some(Either::Right(right)), EitherOrBoth::Left(left)),
+            Ordering::Greater => (Some(Either::Left(left)), EitherOrBoth::Right(right)),
+        }
+    }
+    fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint {
+        let (a_lower, a_upper) = left;
+        let (b_lower, b_upper) = right;
+        let lower = ::std::cmp::max(a_lower, b_lower);
+        let upper = match (a_upper, b_upper) {
+            (Some(x), Some(y)) => x.checked_add(y),
+            _ => None,
+        };
+        (lower, upper)
+    }
+}
+
+impl<L, R, F: FnMut(&L, &R) -> bool> OrderingOrBool<L, R> for MergeFuncLR<F, bool> {
+    type MergeResult = Either<L, R>;
+    fn left(left: L) -> Self::MergeResult {
+        Either::Left(left)
+    }
+    fn right(right: R) -> Self::MergeResult {
+        Either::Right(right)
+    }
+    fn merge(&mut self, left: L, right: R) -> (Option<Either<L, R>>, Self::MergeResult) {
+        if self.0(&left, &right) {
+            (Some(Either::Right(right)), Either::Left(left))
+        } else {
+            (Some(Either::Left(left)), Either::Right(right))
+        }
+    }
+    fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint {
+        // Not ExactSizeIterator because size may be larger than usize
+        size_hint::add(left, right)
+    }
+}
+
+impl<T, F: FnMut(&T, &T) -> bool> OrderingOrBool<T, T> for F {
+    type MergeResult = T;
+    fn left(left: T) -> Self::MergeResult {
+        left
+    }
+    fn right(right: T) -> Self::MergeResult {
+        right
+    }
+    fn merge(&mut self, left: T, right: T) -> (Option<Either<T, T>>, Self::MergeResult) {
+        if self(&left, &right) {
+            (Some(Either::Right(right)), left)
+        } else {
+            (Some(Either::Left(left)), right)
+        }
+    }
+    fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint {
+        // Not ExactSizeIterator because size may be larger than usize
+        size_hint::add(left, right)
+    }
+}
+
+impl<T: PartialOrd> OrderingOrBool<T, T> for MergeLte {
+    type MergeResult = T;
+    fn left(left: T) -> Self::MergeResult {
+        left
+    }
+    fn right(right: T) -> Self::MergeResult {
+        right
+    }
+    fn merge(&mut self, left: T, right: T) -> (Option<Either<T, T>>, Self::MergeResult) {
+        if left <= right {
+            (Some(Either::Right(right)), left)
+        } else {
+            (Some(Either::Left(left)), right)
+        }
+    }
+    fn size_hint(left: SizeHint, right: SizeHint) -> SizeHint {
+        // Not ExactSizeIterator because size may be larger than usize
+        size_hint::add(left, right)
+    }
+}
+
+impl<I, J, F> Clone for MergeBy<I, J, F>
+where
+    I: Iterator,
+    J: Iterator,
+    PutBack<Fuse<I>>: Clone,
+    PutBack<Fuse<J>>: Clone,
+    F: Clone,
+{
+    clone_fields!(left, right, cmp_fn);
+}
+
+impl<I, J, F> fmt::Debug for MergeBy<I, J, F>
+where
+    I: Iterator + fmt::Debug,
+    I::Item: fmt::Debug,
+    J: Iterator + fmt::Debug,
+    J::Item: fmt::Debug,
+{
+    debug_fmt_fields!(MergeBy, left, right);
+}
+
+impl<I, J, F> Iterator for MergeBy<I, J, F>
+where
+    I: Iterator,
+    J: Iterator,
+    F: OrderingOrBool<I::Item, J::Item>,
+{
+    type Item = F::MergeResult;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match (self.left.next(), self.right.next()) {
+            (None, None) => None,
+            (Some(left), None) => Some(F::left(left)),
+            (None, Some(right)) => Some(F::right(right)),
+            (Some(left), Some(right)) => {
+                let (not_next, next) = self.cmp_fn.merge(left, right);
+                match not_next {
+                    Some(Either::Left(l)) => {
+                        self.left.put_back(l);
+                    }
+                    Some(Either::Right(r)) => {
+                        self.right.put_back(r);
+                    }
+                    None => (),
+                }
+
+                Some(next)
+            }
+        }
+    }
+
+    fn fold<B, G>(mut self, init: B, mut f: G) -> B
+    where
+        Self: Sized,
+        G: FnMut(B, Self::Item) -> B,
+    {
+        let mut acc = init;
+        let mut left = self.left.next();
+        let mut right = self.right.next();
+
+        loop {
+            match (left, right) {
+                (Some(l), Some(r)) => match self.cmp_fn.merge(l, r) {
+                    (Some(Either::Right(r)), x) => {
+                        acc = f(acc, x);
+                        left = self.left.next();
+                        right = Some(r);
+                    }
+                    (Some(Either::Left(l)), x) => {
+                        acc = f(acc, x);
+                        left = Some(l);
+                        right = self.right.next();
+                    }
+                    (None, x) => {
+                        acc = f(acc, x);
+                        left = self.left.next();
+                        right = self.right.next();
+                    }
+                },
+                (Some(l), None) => {
+                    self.left.put_back(l);
+                    acc = self.left.fold(acc, |acc, x| f(acc, F::left(x)));
+                    break;
+                }
+                (None, Some(r)) => {
+                    self.right.put_back(r);
+                    acc = self.right.fold(acc, |acc, x| f(acc, F::right(x)));
+                    break;
+                }
+                (None, None) => {
+                    break;
+                }
+            }
+        }
+
+        acc
+    }
+
+    fn size_hint(&self) -> SizeHint {
+        F::size_hint(self.left.size_hint(), self.right.size_hint())
+    }
+
+    fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
+        loop {
+            if n == 0 {
+                break self.next();
+            }
+            n -= 1;
+            match (self.left.next(), self.right.next()) {
+                (None, None) => break None,
+                (Some(_left), None) => break self.left.nth(n).map(F::left),
+                (None, Some(_right)) => break self.right.nth(n).map(F::right),
+                (Some(left), Some(right)) => {
+                    let (not_next, _) = self.cmp_fn.merge(left, right);
+                    match not_next {
+                        Some(Either::Left(l)) => {
+                            self.left.put_back(l);
+                        }
+                        Some(Either::Right(r)) => {
+                            self.right.put_back(r);
+                        }
+                        None => (),
+                    }
+                }
+            }
+        }
+    }
+}
+
+impl<I, J, F> FusedIterator for MergeBy<I, J, F>
+where
+    I: Iterator,
+    J: Iterator,
+    F: OrderingOrBool<I::Item, J::Item>,
+{
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/minmax.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/minmax.rs
similarity index 69%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/minmax.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/minmax.rs
index 52b2f11..a4205044 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/minmax.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/minmax.rs
@@ -1,8 +1,7 @@
-
 /// `MinMaxResult` is an enum returned by `minmax`.
 ///
 /// See [`.minmax()`](crate::Itertools::minmax) for more detail.
-#[derive(Copy, Clone, PartialEq, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub enum MinMaxResult<T> {
     /// Empty iterator
     NoElements,
@@ -12,15 +11,15 @@
 
     /// More than one element in the iterator, the first element is not larger
     /// than the second
-    MinMax(T, T)
+    MinMax(T, T),
 }
 
 impl<T: Clone> MinMaxResult<T> {
-    /// `into_option` creates an `Option` of type `(T, T)`. The returned `Option`
-    /// has variant `None` if and only if the `MinMaxResult` has variant
-    /// `NoElements`. Otherwise `Some((x, y))` is returned where `x <= y`.
-    /// If the `MinMaxResult` has variant `OneElement(x)`, performing this
-    /// operation will make one clone of `x`.
+    /// `into_option` creates an `Option` of type `(T, T)`. The returned
+    /// `Option` has variant `None` if and only if the `MinMaxResult` has
+    /// variant `NoElements`. Otherwise `Some((x, y))` is returned where `x
+    /// <= y`. If the `MinMaxResult` has variant `OneElement(x)`, performing
+    /// this operation will make one clone of `x`.
     ///
     /// # Examples
     ///
@@ -36,34 +35,36 @@
     /// let r = MinMax(1, 2);
     /// assert_eq!(r.into_option(), Some((1, 2)));
     /// ```
-    pub fn into_option(self) -> Option<(T,T)> {
+    pub fn into_option(self) -> Option<(T, T)> {
         match self {
-            MinMaxResult::NoElements => None,
-            MinMaxResult::OneElement(x) => Some((x.clone(), x)),
-            MinMaxResult::MinMax(x, y) => Some((x, y))
+            Self::NoElements => None,
+            Self::OneElement(x) => Some((x.clone(), x)),
+            Self::MinMax(x, y) => Some((x, y)),
         }
     }
 }
 
 /// Implementation guts for `minmax` and `minmax_by_key`.
-pub fn minmax_impl<I, K, F, L>(mut it: I, mut key_for: F,
-                               mut lt: L) -> MinMaxResult<I::Item>
-    where I: Iterator,
-          F: FnMut(&I::Item) -> K,
-          L: FnMut(&I::Item, &I::Item, &K, &K) -> bool,
+pub fn minmax_impl<I, K, F, L>(mut it: I, mut key_for: F, mut lt: L) -> MinMaxResult<I::Item>
+where
+    I: Iterator,
+    F: FnMut(&I::Item) -> K,
+    L: FnMut(&I::Item, &I::Item, &K, &K) -> bool,
 {
     let (mut min, mut max, mut min_key, mut max_key) = match it.next() {
         None => return MinMaxResult::NoElements,
-        Some(x) => {
-            match it.next() {
-                None => return MinMaxResult::OneElement(x),
-                Some(y) => {
-                    let xk = key_for(&x);
-                    let yk = key_for(&y);
-                    if !lt(&y, &x, &yk, &xk) {(x, y, xk, yk)} else {(y, x, yk, xk)}
+        Some(x) => match it.next() {
+            None => return MinMaxResult::OneElement(x),
+            Some(y) => {
+                let xk = key_for(&x);
+                let yk = key_for(&y);
+                if !lt(&y, &x, &yk, &xk) {
+                    (x, y, xk, yk)
+                } else {
+                    (y, x, yk, xk)
                 }
             }
-        }
+        },
     };
 
     loop {
@@ -74,7 +75,7 @@
         // for 2 elements.
         let first = match it.next() {
             None => break,
-            Some(x) => x
+            Some(x) => x,
         };
         let second = match it.next() {
             None => {
@@ -86,7 +87,7 @@
                 }
                 break;
             }
-            Some(x) => x
+            Some(x) => x,
         };
         let first_key = key_for(&first);
         let second_key = key_for(&second);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/multipeek_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/multipeek_impl.rs
similarity index 71%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/multipeek_impl.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/multipeek_impl.rs
index 8b49c69..fff1bc2 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/multipeek_impl.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/multipeek_impl.rs
@@ -1,14 +1,16 @@
-use std::iter::Fuse;
-use alloc::collections::VecDeque;
 use crate::size_hint;
-use crate::PeekingNext;
 #[cfg(doc)]
 use crate::Itertools;
+use crate::PeekingNext;
+use alloc::collections::VecDeque;
+use std::iter::Fuse;
 
 /// See [`multipeek()`] for more information.
 #[derive(Clone, Debug)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct MultiPeek<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
     iter: Fuse<I>,
     buf: VecDeque<I::Item>,
@@ -20,17 +22,15 @@
 ///
 /// [`IntoIterator`] enabled version of [`Itertools::multipeek`].
 pub fn multipeek<I>(iterable: I) -> MultiPeek<I::IntoIter>
-    where I: IntoIterator
+where
+    I: IntoIterator,
 {
-    MultiPeek {
-        iter: iterable.into_iter().fuse(),
-        buf: VecDeque::new(),
-        index: 0,
-    }
+    MultiPeek { iter: iterable.into_iter().fuse(), buf: VecDeque::new(), index: 0 }
 }
 
 impl<I> MultiPeek<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
     /// Reset the peeking “cursor”
     pub fn reset_peek(&mut self) {
@@ -62,24 +62,31 @@
 }
 
 impl<I> PeekingNext for MultiPeek<I>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
-        where F: FnOnce(&Self::Item) -> bool
+    where
+        F: FnOnce(&Self::Item) -> bool,
     {
         if self.buf.is_empty() {
             if let Some(r) = self.peek() {
-                if !accept(r) { return None }
+                if !accept(r) {
+                    return None;
+                }
             }
-        } else if let Some(r) = self.buf.get(0) {
-            if !accept(r) { return None }
+        } else if let Some(r) = self.buf.front() {
+            if !accept(r) {
+                return None;
+            }
         }
         self.next()
     }
 }
 
 impl<I> Iterator for MultiPeek<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
     type Item = I::Item;
 
@@ -91,11 +98,15 @@
     fn size_hint(&self) -> (usize, Option<usize>) {
         size_hint::add_scalar(self.iter.size_hint(), self.buf.len())
     }
+
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        init = self.buf.into_iter().fold(init, &mut f);
+        self.iter.fold(init, f)
+    }
 }
 
 // Same size
-impl<I> ExactSizeIterator for MultiPeek<I>
-    where I: ExactSizeIterator
-{}
-
-
+impl<I> ExactSizeIterator for MultiPeek<I> where I: ExactSizeIterator {}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/next_array.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/next_array.rs
new file mode 100644
index 0000000..e6085b10
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/next_array.rs
@@ -0,0 +1,264 @@
+use core::mem::{self, MaybeUninit};
+
+/// An array of at most `N` elements.
+struct ArrayBuilder<T, const N: usize> {
+    /// The (possibly uninitialized) elements of the `ArrayBuilder`.
+    ///
+    /// # Safety
+    ///
+    /// The elements of `arr[..len]` are valid `T`s.
+    arr: [MaybeUninit<T>; N],
+
+    /// The number of leading elements of `arr` that are valid `T`s, len <= N.
+    len: usize,
+}
+
+impl<T, const N: usize> ArrayBuilder<T, N> {
+    /// Initializes a new, empty `ArrayBuilder`.
+    pub fn new() -> Self {
+        // SAFETY: The safety invariant of `arr` trivially holds for `len = 0`.
+        Self { arr: [(); N].map(|_| MaybeUninit::uninit()), len: 0 }
+    }
+
+    /// Pushes `value` onto the end of the array.
+    ///
+    /// # Panics
+    ///
+    /// This panics if `self.len >= N`.
+    #[inline(always)]
+    pub fn push(&mut self, value: T) {
+        // PANICS: This will panic if `self.len >= N`.
+        let place = &mut self.arr[self.len];
+        // SAFETY: The safety invariant of `self.arr` applies to elements at
+        // indices `0..self.len` — not to the element at `self.len`. Writing to
+        // the element at index `self.len` therefore does not violate the safety
+        // invariant of `self.arr`. Even if this line panics, we have not
+        // created any intermediate invalid state.
+        *place = MaybeUninit::new(value);
+        // Lemma: `self.len < N`. By invariant, `self.len <= N`. Above, we index
+        // into `self.arr`, which has size `N`, at index `self.len`. If `self.len == N`
+        // at that point, that index would be out-of-bounds, and the index
+        // operation would panic. Thus, `self.len != N`, and since `self.len <= N`,
+        // that means that `self.len < N`.
+        //
+        // PANICS: Since `self.len < N`, and since `N <= usize::MAX`,
+        // `self.len + 1 <= usize::MAX`, and so `self.len += 1` will not
+        // overflow. Overflow is the only panic condition of `+=`.
+        //
+        // SAFETY:
+        // - We are required to uphold the invariant that `self.len <= N`. Since, by the
+        //   preceding lemma, `self.len < N` at this point in the code, `self.len += 1`
+        //   results in `self.len <= N`.
+        // - We are required to uphold the invariant that `self.arr[..self.len]` are
+        //   valid instances of `T`. Since this invariant already held when this method
+        //   was called, and since we only increment `self.len` by 1 here, we only need
+        //   to prove that the element at `self.arr[self.len]` (using the value of
+        //   `self.len` before incrementing) is valid. Above, we construct `place` to
+        //   point to `self.arr[self.len]`, and then initialize `*place` to
+        //   `MaybeUninit::new(value)`, which is a valid `T` by construction.
+        self.len += 1;
+    }
+
+    /// Consumes the elements in the `ArrayBuilder` and returns them as an array
+    /// `[T; N]`.
+    ///
+    /// If `self.len() < N`, this returns `None`.
+    pub fn take(&mut self) -> Option<[T; N]> {
+        if self.len == N {
+            // SAFETY: Decreasing the value of `self.len` cannot violate the
+            // safety invariant on `self.arr`.
+            self.len = 0;
+
+            // SAFETY: Since `self.len` is 0, `self.arr` may safely contain
+            // uninitialized elements.
+            let arr = mem::replace(&mut self.arr, [(); N].map(|_| MaybeUninit::uninit()));
+
+            Some(arr.map(|v| {
+                // SAFETY: We know that all elements of `arr` are valid because
+                // we checked that `len == N`.
+                unsafe { v.assume_init() }
+            }))
+        } else {
+            None
+        }
+    }
+}
+
+impl<T, const N: usize> AsMut<[T]> for ArrayBuilder<T, N> {
+    fn as_mut(&mut self) -> &mut [T] {
+        let valid = &mut self.arr[..self.len];
+        // SAFETY: By invariant on `self.arr`, the elements of `self.arr` at
+        // indices `0..self.len` are in a valid state. Since `valid` references
+        // only these elements, the safety precondition of
+        // `slice_assume_init_mut` is satisfied.
+        unsafe { slice_assume_init_mut(valid) }
+    }
+}
+
+impl<T, const N: usize> Drop for ArrayBuilder<T, N> {
+    // We provide a non-trivial `Drop` impl, because the trivial impl would be a
+    // no-op; `MaybeUninit<T>` has no innate awareness of its own validity, and
+    // so it can only forget its contents. By leveraging the safety invariant of
+    // `self.arr`, we do know which elements of `self.arr` are valid, and can
+    // selectively run their destructors.
+    fn drop(&mut self) {
+        // SAFETY:
+        // - by invariant on `&mut [T]`, `self.as_mut()` is:
+        //   - valid for reads and writes
+        //   - properly aligned
+        //   - non-null
+        // - the dropped `T` are valid for dropping; they do not have any additional
+        //   library invariants that we've violated
+        // - no other pointers to `valid` exist (since we're in the context of `drop`)
+        unsafe { core::ptr::drop_in_place(self.as_mut()) }
+    }
+}
+
+/// Assuming all the elements are initialized, get a mutable slice to them.
+///
+/// # Safety
+///
+/// The caller guarantees that the elements `T` referenced by `slice` are in a
+/// valid state.
+unsafe fn slice_assume_init_mut<T>(slice: &mut [MaybeUninit<T>]) -> &mut [T] {
+    // SAFETY: Casting `&mut [MaybeUninit<T>]` to `&mut [T]` is sound, because
+    // `MaybeUninit<T>` is guaranteed to have the same size, alignment and ABI
+    // as `T`, and because the caller has guaranteed that `slice` is in the
+    // valid state.
+    unsafe { &mut *(slice as *mut [MaybeUninit<T>] as *mut [T]) }
+}
+
+/// Equivalent to `it.next_array()`.
+pub(crate) fn next_array<I, const N: usize>(it: &mut I) -> Option<[I::Item; N]>
+where
+    I: Iterator,
+{
+    let mut builder = ArrayBuilder::new();
+    for _ in 0..N {
+        builder.push(it.next()?);
+    }
+    builder.take()
+}
+
+#[cfg(test)]
+mod test {
+    use super::ArrayBuilder;
+
+    #[test]
+    fn zero_len_take() {
+        let mut builder = ArrayBuilder::<(), 0>::new();
+        let taken = builder.take();
+        assert_eq!(taken, Some([(); 0]));
+    }
+
+    #[test]
+    #[should_panic]
+    fn zero_len_push() {
+        let mut builder = ArrayBuilder::<(), 0>::new();
+        builder.push(());
+    }
+
+    #[test]
+    fn push_4() {
+        let mut builder = ArrayBuilder::<(), 4>::new();
+        assert_eq!(builder.take(), None);
+
+        builder.push(());
+        assert_eq!(builder.take(), None);
+
+        builder.push(());
+        assert_eq!(builder.take(), None);
+
+        builder.push(());
+        assert_eq!(builder.take(), None);
+
+        builder.push(());
+        assert_eq!(builder.take(), Some([(); 4]));
+    }
+
+    #[test]
+    fn tracked_drop() {
+        use std::panic::{catch_unwind, AssertUnwindSafe};
+        use std::sync::atomic::{AtomicU16, Ordering};
+
+        static DROPPED: AtomicU16 = AtomicU16::new(0);
+
+        #[derive(Debug, PartialEq)]
+        struct TrackedDrop;
+
+        impl Drop for TrackedDrop {
+            fn drop(&mut self) {
+                DROPPED.fetch_add(1, Ordering::Relaxed);
+            }
+        }
+
+        {
+            let builder = ArrayBuilder::<TrackedDrop, 0>::new();
+            assert_eq!(DROPPED.load(Ordering::Relaxed), 0);
+            drop(builder);
+            assert_eq!(DROPPED.load(Ordering::Relaxed), 0);
+        }
+
+        {
+            let mut builder = ArrayBuilder::<TrackedDrop, 2>::new();
+            builder.push(TrackedDrop);
+            assert_eq!(builder.take(), None);
+            assert_eq!(DROPPED.load(Ordering::Relaxed), 0);
+            drop(builder);
+            assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 1);
+        }
+
+        {
+            let mut builder = ArrayBuilder::<TrackedDrop, 2>::new();
+            builder.push(TrackedDrop);
+            builder.push(TrackedDrop);
+            assert!(matches!(builder.take(), Some(_)));
+            assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 2);
+            drop(builder);
+            assert_eq!(DROPPED.load(Ordering::Relaxed), 0);
+        }
+
+        {
+            let mut builder = ArrayBuilder::<TrackedDrop, 2>::new();
+
+            builder.push(TrackedDrop);
+            builder.push(TrackedDrop);
+
+            assert!(catch_unwind(AssertUnwindSafe(|| {
+                builder.push(TrackedDrop);
+            }))
+            .is_err());
+
+            assert_eq!(DROPPED.load(Ordering::Relaxed), 1);
+
+            drop(builder);
+
+            assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 3);
+        }
+
+        {
+            let mut builder = ArrayBuilder::<TrackedDrop, 2>::new();
+
+            builder.push(TrackedDrop);
+            builder.push(TrackedDrop);
+
+            assert!(catch_unwind(AssertUnwindSafe(|| {
+                builder.push(TrackedDrop);
+            }))
+            .is_err());
+
+            assert_eq!(DROPPED.load(Ordering::Relaxed), 1);
+
+            assert!(matches!(builder.take(), Some(_)));
+
+            assert_eq!(DROPPED.load(Ordering::Relaxed), 3);
+
+            builder.push(TrackedDrop);
+            builder.push(TrackedDrop);
+
+            assert!(matches!(builder.take(), Some(_)));
+
+            assert_eq!(DROPPED.swap(0, Ordering::Relaxed), 5);
+        }
+    }
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/pad_tail.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/pad_tail.rs
similarity index 67%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/pad_tail.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/pad_tail.rs
index 248a432..326cfafcb 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/pad_tail.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/pad_tail.rs
@@ -1,5 +1,5 @@
-use std::iter::{Fuse, FusedIterator};
 use crate::size_hint;
+use std::iter::{Fuse, FusedIterator};
 
 /// An iterator adaptor that pads a sequence to a minimum length by filling
 /// missing elements using a closure.
@@ -25,20 +25,17 @@
 
 /// Create a new `PadUsing` iterator.
 pub fn pad_using<I, F>(iter: I, min: usize, filler: F) -> PadUsing<I, F>
-    where I: Iterator,
-          F: FnMut(usize) -> I::Item
+where
+    I: Iterator,
+    F: FnMut(usize) -> I::Item,
 {
-    PadUsing {
-        iter: iter.fuse(),
-        min,
-        pos: 0,
-        filler,
-    }
+    PadUsing { iter: iter.fuse(), min, pos: 0, filler }
 }
 
 impl<I, F> Iterator for PadUsing<I, F>
-    where I: Iterator,
-          F: FnMut(usize) -> I::Item
+where
+    I: Iterator,
+    F: FnMut(usize) -> I::Item,
 {
     type Item = I::Item;
 
@@ -53,7 +50,7 @@
                 } else {
                     None
                 }
-            },
+            }
             e => {
                 self.pos += 1;
                 e
@@ -65,11 +62,24 @@
         let tail = self.min.saturating_sub(self.pos);
         size_hint::max(self.iter.size_hint(), (tail, Some(tail)))
     }
+
+    fn fold<B, G>(self, mut init: B, mut f: G) -> B
+    where
+        G: FnMut(B, Self::Item) -> B,
+    {
+        let mut pos = self.pos;
+        init = self.iter.fold(init, |acc, item| {
+            pos += 1;
+            f(acc, item)
+        });
+        (pos..self.min).map(self.filler).fold(init, f)
+    }
 }
 
 impl<I, F> DoubleEndedIterator for PadUsing<I, F>
-    where I: DoubleEndedIterator + ExactSizeIterator,
-          F: FnMut(usize) -> I::Item
+where
+    I: DoubleEndedIterator + ExactSizeIterator,
+    F: FnMut(usize) -> I::Item,
 {
     fn next_back(&mut self) -> Option<Self::Item> {
         if self.min == 0 {
@@ -82,15 +92,26 @@
             Some((self.filler)(self.min))
         }
     }
+
+    fn rfold<B, G>(self, mut init: B, mut f: G) -> B
+    where
+        G: FnMut(B, Self::Item) -> B,
+    {
+        init = (self.iter.len()..self.min).map(self.filler).rfold(init, &mut f);
+        self.iter.rfold(init, f)
+    }
 }
 
 impl<I, F> ExactSizeIterator for PadUsing<I, F>
-    where I: ExactSizeIterator,
-          F: FnMut(usize) -> I::Item
-{}
-
+where
+    I: ExactSizeIterator,
+    F: FnMut(usize) -> I::Item,
+{
+}
 
 impl<I, F> FusedIterator for PadUsing<I, F>
-    where I: FusedIterator,
-          F: FnMut(usize) -> I::Item
-{}
+where
+    I: FusedIterator,
+    F: FnMut(usize) -> I::Item,
+{
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peek_nth.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peek_nth.rs
new file mode 100644
index 0000000..e3f3f23
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peek_nth.rs
@@ -0,0 +1,176 @@
+use crate::size_hint;
+use crate::PeekingNext;
+use alloc::collections::VecDeque;
+use std::iter::Fuse;
+
+/// See [`peek_nth()`] for more information.
+#[derive(Clone, Debug)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct PeekNth<I>
+where
+    I: Iterator,
+{
+    iter: Fuse<I>,
+    buf: VecDeque<I::Item>,
+}
+
+/// A drop-in replacement for [`std::iter::Peekable`] which adds a `peek_nth`
+/// method allowing the user to `peek` at a value several iterations forward
+/// without advancing the base iterator.
+///
+/// This differs from `multipeek` in that subsequent calls to `peek` or
+/// `peek_nth` will always return the same value until `next` is called
+/// (making `reset_peek` unnecessary).
+pub fn peek_nth<I>(iterable: I) -> PeekNth<I::IntoIter>
+where
+    I: IntoIterator,
+{
+    PeekNth { iter: iterable.into_iter().fuse(), buf: VecDeque::new() }
+}
+
+impl<I> PeekNth<I>
+where
+    I: Iterator,
+{
+    /// Works exactly like the `peek` method in [`std::iter::Peekable`].
+    pub fn peek(&mut self) -> Option<&I::Item> {
+        self.peek_nth(0)
+    }
+
+    /// Works exactly like the `peek_mut` method in [`std::iter::Peekable`].
+    pub fn peek_mut(&mut self) -> Option<&mut I::Item> {
+        self.peek_nth_mut(0)
+    }
+
+    /// Returns a reference to the `nth` value without advancing the iterator.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// use itertools::peek_nth;
+    ///
+    /// let xs = vec![1, 2, 3];
+    /// let mut iter = peek_nth(xs.into_iter());
+    ///
+    /// assert_eq!(iter.peek_nth(0), Some(&1));
+    /// assert_eq!(iter.next(), Some(1));
+    ///
+    /// // The iterator does not advance even if we call `peek_nth` multiple times
+    /// assert_eq!(iter.peek_nth(0), Some(&2));
+    /// assert_eq!(iter.peek_nth(1), Some(&3));
+    /// assert_eq!(iter.next(), Some(2));
+    ///
+    /// // Calling `peek_nth` past the end of the iterator will return `None`
+    /// assert_eq!(iter.peek_nth(1), None);
+    /// ```
+    pub fn peek_nth(&mut self, n: usize) -> Option<&I::Item> {
+        let unbuffered_items = (n + 1).saturating_sub(self.buf.len());
+
+        self.buf.extend(self.iter.by_ref().take(unbuffered_items));
+
+        self.buf.get(n)
+    }
+
+    /// Returns a mutable reference to the `nth` value without advancing the
+    /// iterator.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// use itertools::peek_nth;
+    ///
+    /// let xs = vec![1, 2, 3, 4, 5];
+    /// let mut iter = peek_nth(xs.into_iter());
+    ///
+    /// assert_eq!(iter.peek_nth_mut(0), Some(&mut 1));
+    /// assert_eq!(iter.next(), Some(1));
+    ///
+    /// // The iterator does not advance even if we call `peek_nth_mut` multiple times
+    /// assert_eq!(iter.peek_nth_mut(0), Some(&mut 2));
+    /// assert_eq!(iter.peek_nth_mut(1), Some(&mut 3));
+    /// assert_eq!(iter.next(), Some(2));
+    ///
+    /// // Peek into the iterator and set the value behind the mutable reference.
+    /// if let Some(p) = iter.peek_nth_mut(1) {
+    ///     assert_eq!(*p, 4);
+    ///     *p = 9;
+    /// }
+    ///
+    /// // The value we put in reappears as the iterator continues.
+    /// assert_eq!(iter.next(), Some(3));
+    /// assert_eq!(iter.next(), Some(9));
+    ///
+    /// // Calling `peek_nth_mut` past the end of the iterator will return `None`
+    /// assert_eq!(iter.peek_nth_mut(1), None);
+    /// ```
+    pub fn peek_nth_mut(&mut self, n: usize) -> Option<&mut I::Item> {
+        let unbuffered_items = (n + 1).saturating_sub(self.buf.len());
+
+        self.buf.extend(self.iter.by_ref().take(unbuffered_items));
+
+        self.buf.get_mut(n)
+    }
+
+    /// Works exactly like the `next_if` method in [`std::iter::Peekable`].
+    pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option<I::Item> {
+        match self.next() {
+            Some(item) if func(&item) => Some(item),
+            Some(item) => {
+                self.buf.push_front(item);
+                None
+            }
+            _ => None,
+        }
+    }
+
+    /// Works exactly like the `next_if_eq` method in [`std::iter::Peekable`].
+    pub fn next_if_eq<T>(&mut self, expected: &T) -> Option<I::Item>
+    where
+        T: ?Sized,
+        I::Item: PartialEq<T>,
+    {
+        self.next_if(|next| next == expected)
+    }
+}
+
+impl<I> Iterator for PeekNth<I>
+where
+    I: Iterator,
+{
+    type Item = I::Item;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.buf.pop_front().or_else(|| self.iter.next())
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        size_hint::add_scalar(self.iter.size_hint(), self.buf.len())
+    }
+
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        init = self.buf.into_iter().fold(init, &mut f);
+        self.iter.fold(init, f)
+    }
+}
+
+impl<I> ExactSizeIterator for PeekNth<I> where I: ExactSizeIterator {}
+
+impl<I> PeekingNext for PeekNth<I>
+where
+    I: Iterator,
+{
+    fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
+    where
+        F: FnOnce(&Self::Item) -> bool,
+    {
+        self.peek().filter(|item| accept(item))?;
+        self.next()
+    }
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peeking_take_while.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peeking_take_while.rs
similarity index 74%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peeking_take_while.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peeking_take_while.rs
index 3a37228..f3259a9 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peeking_take_while.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peeking_take_while.rs
@@ -1,7 +1,8 @@
-use std::iter::Peekable;
 use crate::PutBack;
 #[cfg(feature = "use_alloc")]
 use crate::PutBackN;
+use crate::RepeatN;
+use std::iter::Peekable;
 
 /// An iterator that allows peeking at an element before deciding to accept it.
 ///
@@ -10,31 +11,36 @@
 ///
 /// This is implemented by peeking adaptors like peekable and put back,
 /// but also by a few iterators that can be peeked natively, like the slice’s
-/// by reference iterator (`std::slice::Iter`).
-pub trait PeekingNext : Iterator {
+/// by reference iterator ([`std::slice::Iter`]).
+pub trait PeekingNext: Iterator {
     /// Pass a reference to the next iterator element to the closure `accept`;
-    /// if `accept` returns true, return it as the next element,
-    /// else None.
+    /// if `accept` returns `true`, return it as the next element,
+    /// else `None`.
     fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
-        where Self: Sized,
-              F: FnOnce(&Self::Item) -> bool;
+    where
+        Self: Sized,
+        F: FnOnce(&Self::Item) -> bool;
 }
 
-impl<'a, I> PeekingNext for &'a mut I
-    where I: PeekingNext,
+impl<I> PeekingNext for &mut I
+where
+    I: PeekingNext,
 {
     fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
-        where F: FnOnce(&Self::Item) -> bool
+    where
+        F: FnOnce(&Self::Item) -> bool,
     {
         (*self).peeking_next(accept)
     }
 }
 
 impl<I> PeekingNext for Peekable<I>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
-        where F: FnOnce(&Self::Item) -> bool
+    where
+        F: FnOnce(&Self::Item) -> bool,
     {
         if let Some(r) = self.peek() {
             if !accept(r) {
@@ -46,10 +52,12 @@
 }
 
 impl<I> PeekingNext for PutBack<I>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
-        where F: FnOnce(&Self::Item) -> bool
+    where
+        F: FnOnce(&Self::Item) -> bool,
     {
         if let Some(r) = self.next() {
             if !accept(&r) {
@@ -65,10 +73,12 @@
 
 #[cfg(feature = "use_alloc")]
 impl<I> PeekingNext for PutBackN<I>
-    where I: Iterator,
+where
+    I: Iterator,
 {
     fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
-        where F: FnOnce(&Self::Item) -> bool
+    where
+        F: FnOnce(&Self::Item) -> bool,
     {
         if let Some(r) = self.next() {
             if !accept(&r) {
@@ -82,39 +92,51 @@
     }
 }
 
+impl<T: Clone> PeekingNext for RepeatN<T> {
+    fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
+    where
+        F: FnOnce(&Self::Item) -> bool,
+    {
+        let r = self.elt.as_ref()?;
+        if !accept(r) {
+            return None;
+        }
+        self.next()
+    }
+}
+
 /// An iterator adaptor that takes items while a closure returns `true`.
 ///
 /// See [`.peeking_take_while()`](crate::Itertools::peeking_take_while)
 /// for more information.
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct PeekingTakeWhile<'a, I: 'a, F>
-    where I: Iterator,
+pub struct PeekingTakeWhile<'a, I, F>
+where
+    I: Iterator + 'a,
 {
     iter: &'a mut I,
     f: F,
 }
 
-impl<'a, I: 'a, F> std::fmt::Debug for PeekingTakeWhile<'a, I, F>
+impl<'a, I, F> std::fmt::Debug for PeekingTakeWhile<'a, I, F>
 where
-    I: Iterator + std::fmt::Debug,
+    I: Iterator + std::fmt::Debug + 'a,
 {
     debug_fmt_fields!(PeekingTakeWhile, iter);
 }
 
 /// Create a `PeekingTakeWhile`
 pub fn peeking_take_while<I, F>(iter: &mut I, f: F) -> PeekingTakeWhile<I, F>
-    where I: Iterator,
+where
+    I: Iterator,
 {
-    PeekingTakeWhile {
-        iter,
-        f,
-    }
+    PeekingTakeWhile { iter, f }
 }
 
-impl<'a, I, F> Iterator for PeekingTakeWhile<'a, I, F>
-    where I: PeekingNext,
-          F: FnMut(&I::Item) -> bool,
-
+impl<I, F> Iterator for PeekingTakeWhile<'_, I, F>
+where
+    I: PeekingNext,
+    F: FnMut(&I::Item) -> bool,
 {
     type Item = I::Item;
     fn next(&mut self) -> Option<Self::Item> {
@@ -126,12 +148,14 @@
     }
 }
 
-impl<'a, I, F> PeekingNext for PeekingTakeWhile<'a, I, F>
-    where I: PeekingNext,
-          F: FnMut(&I::Item) -> bool,
+impl<I, F> PeekingNext for PeekingTakeWhile<'_, I, F>
+where
+    I: PeekingNext,
+    F: FnMut(&I::Item) -> bool,
 {
     fn peeking_next<G>(&mut self, g: G) -> Option<Self::Item>
-        where G: FnOnce(&Self::Item) -> bool,
+    where
+        G: FnOnce(&Self::Item) -> bool,
     {
         let f = &mut self.f;
         self.iter.peeking_next(|r| f(r) && g(r))
@@ -174,4 +198,4 @@
 
 // cloning a Rev has no extra overhead; peekable and put backs are never DEI.
 peeking_next_by_clone! { [I: Clone + PeekingNext + DoubleEndedIterator]
-                         ::std::iter::Rev<I> }
+::std::iter::Rev<I> }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/permutations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/permutations.rs
new file mode 100644
index 0000000..dffe829
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/permutations.rs
@@ -0,0 +1,174 @@
+use alloc::boxed::Box;
+use alloc::vec::Vec;
+use std::fmt;
+use std::iter::once;
+use std::iter::FusedIterator;
+
+use super::lazy_buffer::LazyBuffer;
+use crate::size_hint::{self, SizeHint};
+
+/// An iterator adaptor that iterates through all the `k`-permutations of the
+/// elements from an iterator.
+///
+/// See [`.permutations()`](crate::Itertools::permutations) for
+/// more information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct Permutations<I: Iterator> {
+    vals: LazyBuffer<I>,
+    state: PermutationState,
+}
+
+impl<I> Clone for Permutations<I>
+where
+    I: Clone + Iterator,
+    I::Item: Clone,
+{
+    clone_fields!(vals, state);
+}
+
+#[derive(Clone, Debug)]
+enum PermutationState {
+    /// No permutation generated yet.
+    Start { k: usize },
+    /// Values from the iterator are not fully loaded yet so `n` is still
+    /// unknown.
+    Buffered { k: usize, min_n: usize },
+    /// All values from the iterator are known so `n` is known.
+    Loaded { indices: Box<[usize]>, cycles: Box<[usize]> },
+    /// No permutation left to generate.
+    End,
+}
+
+impl<I> fmt::Debug for Permutations<I>
+where
+    I: Iterator + fmt::Debug,
+    I::Item: fmt::Debug,
+{
+    debug_fmt_fields!(Permutations, vals, state);
+}
+
+pub fn permutations<I: Iterator>(iter: I, k: usize) -> Permutations<I> {
+    Permutations { vals: LazyBuffer::new(iter), state: PermutationState::Start { k } }
+}
+
+impl<I> Iterator for Permutations<I>
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+    type Item = Vec<I::Item>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let Self { vals, state } = self;
+        match state {
+            PermutationState::Start { k: 0 } => {
+                *state = PermutationState::End;
+                Some(Vec::new())
+            }
+            &mut PermutationState::Start { k } => {
+                vals.prefill(k);
+                if vals.len() != k {
+                    *state = PermutationState::End;
+                    return None;
+                }
+                *state = PermutationState::Buffered { k, min_n: k };
+                Some(vals[0..k].to_vec())
+            }
+            PermutationState::Buffered { ref k, min_n } => {
+                if vals.get_next() {
+                    let item = (0..*k - 1).chain(once(*min_n)).map(|i| vals[i].clone()).collect();
+                    *min_n += 1;
+                    Some(item)
+                } else {
+                    let n = *min_n;
+                    let prev_iteration_count = n - *k + 1;
+                    let mut indices: Box<[_]> = (0..n).collect();
+                    let mut cycles: Box<[_]> = (n - k..n).rev().collect();
+                    // Advance the state to the correct point.
+                    for _ in 0..prev_iteration_count {
+                        if advance(&mut indices, &mut cycles) {
+                            *state = PermutationState::End;
+                            return None;
+                        }
+                    }
+                    let item = vals.get_at(&indices[0..*k]);
+                    *state = PermutationState::Loaded { indices, cycles };
+                    Some(item)
+                }
+            }
+            PermutationState::Loaded { indices, cycles } => {
+                if advance(indices, cycles) {
+                    *state = PermutationState::End;
+                    return None;
+                }
+                let k = cycles.len();
+                Some(vals.get_at(&indices[0..k]))
+            }
+            PermutationState::End => None,
+        }
+    }
+
+    fn count(self) -> usize {
+        let Self { vals, state } = self;
+        let n = vals.count();
+        state.size_hint_for(n).1.unwrap()
+    }
+
+    fn size_hint(&self) -> SizeHint {
+        let (mut low, mut upp) = self.vals.size_hint();
+        low = self.state.size_hint_for(low).0;
+        upp = upp.and_then(|n| self.state.size_hint_for(n).1);
+        (low, upp)
+    }
+}
+
+impl<I> FusedIterator for Permutations<I>
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+}
+
+fn advance(indices: &mut [usize], cycles: &mut [usize]) -> bool {
+    let n = indices.len();
+    let k = cycles.len();
+    // NOTE: if `cycles` are only zeros, then we reached the last permutation.
+    for i in (0..k).rev() {
+        if cycles[i] == 0 {
+            cycles[i] = n - i - 1;
+            indices[i..].rotate_left(1);
+        } else {
+            let swap_index = n - cycles[i];
+            indices.swap(i, swap_index);
+            cycles[i] -= 1;
+            return false;
+        }
+    }
+    true
+}
+
+impl PermutationState {
+    fn size_hint_for(&self, n: usize) -> SizeHint {
+        // At the beginning, there are `n!/(n-k)!` items to come.
+        let at_start = |n, k| {
+            debug_assert!(n >= k);
+            let total = (n - k + 1..=n).try_fold(1usize, |acc, i| acc.checked_mul(i));
+            (total.unwrap_or(usize::MAX), total)
+        };
+        match *self {
+            Self::Start { k } if n < k => (0, Some(0)),
+            Self::Start { k } => at_start(n, k),
+            Self::Buffered { k, min_n } => {
+                // Same as `Start` minus the previously generated items.
+                size_hint::sub_scalar(at_start(n, k), min_n - k + 1)
+            }
+            Self::Loaded { ref indices, ref cycles } => {
+                let count = cycles.iter().enumerate().try_fold(0usize, |acc, (i, &c)| {
+                    acc.checked_mul(indices.len() - i).and_then(|count| count.checked_add(c))
+                });
+                (count.unwrap_or(usize::MAX), count)
+            }
+            Self::End => (0, Some(0)),
+        }
+    }
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/powerset.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/powerset.rs
new file mode 100644
index 0000000..f9c75e95
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/powerset.rs
@@ -0,0 +1,131 @@
+use alloc::vec::Vec;
+use std::fmt;
+use std::iter::FusedIterator;
+
+use super::combinations::{combinations, Combinations};
+use crate::adaptors::checked_binomial;
+use crate::size_hint::{self, SizeHint};
+
+/// An iterator to iterate through the powerset of the elements from an
+/// iterator.
+///
+/// See [`.powerset()`](crate::Itertools::powerset) for more
+/// information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct Powerset<I: Iterator> {
+    combs: Combinations<I>,
+}
+
+impl<I> Clone for Powerset<I>
+where
+    I: Clone + Iterator,
+    I::Item: Clone,
+{
+    clone_fields!(combs);
+}
+
+impl<I> fmt::Debug for Powerset<I>
+where
+    I: Iterator + fmt::Debug,
+    I::Item: fmt::Debug,
+{
+    debug_fmt_fields!(Powerset, combs);
+}
+
+/// Create a new `Powerset` from a clonable iterator.
+pub fn powerset<I>(src: I) -> Powerset<I>
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+    Powerset { combs: combinations(src, 0) }
+}
+
+impl<I: Iterator> Powerset<I> {
+    /// Returns true if `k` has been incremented, false otherwise.
+    fn increment_k(&mut self) -> bool {
+        if self.combs.k() < self.combs.n() || self.combs.k() == 0 {
+            self.combs.reset(self.combs.k() + 1);
+            true
+        } else {
+            false
+        }
+    }
+}
+
+impl<I> Iterator for Powerset<I>
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+    type Item = Vec<I::Item>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if let Some(elt) = self.combs.next() {
+            Some(elt)
+        } else if self.increment_k() {
+            self.combs.next()
+        } else {
+            None
+        }
+    }
+
+    fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
+        loop {
+            match self.combs.try_nth(n) {
+                Ok(item) => return Some(item),
+                Err(steps) => {
+                    if !self.increment_k() {
+                        return None;
+                    }
+                    n -= steps;
+                }
+            }
+        }
+    }
+
+    fn size_hint(&self) -> SizeHint {
+        let k = self.combs.k();
+        // Total bounds for source iterator.
+        let (n_min, n_max) = self.combs.src().size_hint();
+        let low = remaining_for(n_min, k).unwrap_or(usize::MAX);
+        let upp = n_max.and_then(|n| remaining_for(n, k));
+        size_hint::add(self.combs.size_hint(), (low, upp))
+    }
+
+    fn count(self) -> usize {
+        let k = self.combs.k();
+        let (n, combs_count) = self.combs.n_and_count();
+        combs_count + remaining_for(n, k).unwrap()
+    }
+
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let mut it = self.combs;
+        if it.k() == 0 {
+            init = it.by_ref().fold(init, &mut f);
+            it.reset(1);
+        }
+        init = it.by_ref().fold(init, &mut f);
+        // n is now known for sure because k >= 1 and all k-combinations have been
+        // generated.
+        for k in it.k() + 1..=it.n() {
+            it.reset(k);
+            init = it.by_ref().fold(init, &mut f);
+        }
+        init
+    }
+}
+
+impl<I> FusedIterator for Powerset<I>
+where
+    I: Iterator,
+    I::Item: Clone,
+{
+}
+
+fn remaining_for(n: usize, k: usize) -> Option<usize> {
+    (k + 1..=n).try_fold(0usize, |sum, i| sum.checked_add(checked_binomial(n, i)?))
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/process_results_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/process_results_impl.rs
new file mode 100644
index 0000000..4478f217
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/process_results_impl.rs
@@ -0,0 +1,105 @@
+#[cfg(doc)]
+use crate::Itertools;
+
+/// An iterator that produces only the `T` values as long as the
+/// inner iterator produces `Ok(T)`.
+///
+/// Used by [`process_results`](crate::process_results), see its docs
+/// for more information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[derive(Debug)]
+pub struct ProcessResults<'a, I, E: 'a> {
+    error: &'a mut Result<(), E>,
+    iter: I,
+}
+
+impl<I, E> ProcessResults<'_, I, E> {
+    #[inline(always)]
+    fn next_body<T>(&mut self, item: Option<Result<T, E>>) -> Option<T> {
+        match item {
+            Some(Ok(x)) => Some(x),
+            Some(Err(e)) => {
+                *self.error = Err(e);
+                None
+            }
+            None => None,
+        }
+    }
+}
+
+impl<I, T, E> Iterator for ProcessResults<'_, I, E>
+where
+    I: Iterator<Item = Result<T, E>>,
+{
+    type Item = T;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let item = self.iter.next();
+        self.next_body(item)
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (0, self.iter.size_hint().1)
+    }
+
+    fn fold<B, F>(mut self, init: B, mut f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let error = self.error;
+        self.iter
+            .try_fold(init, |acc, opt| match opt {
+                Ok(x) => Ok(f(acc, x)),
+                Err(e) => {
+                    *error = Err(e);
+                    Err(acc)
+                }
+            })
+            .unwrap_or_else(|e| e)
+    }
+}
+
+impl<I, T, E> DoubleEndedIterator for ProcessResults<'_, I, E>
+where
+    I: Iterator<Item = Result<T, E>>,
+    I: DoubleEndedIterator,
+{
+    fn next_back(&mut self) -> Option<Self::Item> {
+        let item = self.iter.next_back();
+        self.next_body(item)
+    }
+
+    fn rfold<B, F>(mut self, init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let error = self.error;
+        self.iter
+            .try_rfold(init, |acc, opt| match opt {
+                Ok(x) => Ok(f(acc, x)),
+                Err(e) => {
+                    *error = Err(e);
+                    Err(acc)
+                }
+            })
+            .unwrap_or_else(|e| e)
+    }
+}
+
+/// “Lift” a function of the values of an iterator so that it can process
+/// an iterator of `Result` values instead.
+///
+/// [`IntoIterator`] enabled version of [`Itertools::process_results`].
+pub fn process_results<I, F, T, E, R>(iterable: I, processor: F) -> Result<R, E>
+where
+    I: IntoIterator<Item = Result<T, E>>,
+    F: FnOnce(ProcessResults<I::IntoIter, E>) -> R,
+{
+    let iter = iterable.into_iter();
+    let mut error = Ok(());
+
+    let result = processor(ProcessResults { error: &mut error, iter });
+
+    error.map(|_| result)
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/put_back_n_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/put_back_n_impl.rs
similarity index 75%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/put_back_n_impl.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/put_back_n_impl.rs
index 60ea8e6..f508f40 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/put_back_n_impl.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/put_back_n_impl.rs
@@ -7,6 +7,7 @@
 ///
 /// Iterator element type is `I::Item`.
 #[derive(Debug, Clone)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct PutBackN<I: Iterator> {
     top: Vec<I::Item>,
     iter: I,
@@ -17,16 +18,15 @@
 ///
 /// Iterator element type is `I::Item`.
 pub fn put_back_n<I>(iterable: I) -> PutBackN<I::IntoIter>
-    where I: IntoIterator
+where
+    I: IntoIterator,
 {
-    PutBackN {
-        top: Vec::new(),
-        iter: iterable.into_iter(),
-    }
+    PutBackN { top: Vec::new(), iter: iterable.into_iter() }
 }
 
 impl<I: Iterator> PutBackN<I> {
-    /// Puts x in front of the iterator.
+    /// Puts `x` in front of the iterator.
+    ///
     /// The values are yielded in order of the most recently put back
     /// values first.
     ///
@@ -57,5 +57,12 @@
     fn size_hint(&self) -> (usize, Option<usize>) {
         size_hint::add_scalar(self.iter.size_hint(), self.top.len())
     }
-}
 
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        init = self.top.into_iter().rfold(init, &mut f);
+        self.iter.fold(init, f)
+    }
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/rciter_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/rciter_impl.rs
similarity index 85%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/rciter_impl.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/rciter_impl.rs
index 7298350a..e709dbf0 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/rciter_impl.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/rciter_impl.rs
@@ -1,10 +1,10 @@
-
-use std::iter::{FusedIterator, IntoIterator};
 use alloc::rc::Rc;
 use std::cell::RefCell;
+use std::iter::{FusedIterator, IntoIterator};
 
 /// A wrapper for `Rc<RefCell<I>>`, that implements the `Iterator` trait.
 #[derive(Debug)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 pub struct RcIter<I> {
     /// The boxed iterator.
     pub rciter: Rc<RefCell<I>>,
@@ -15,9 +15,9 @@
 /// The returned `RcIter` can be cloned, and each clone will refer back to the
 /// same original iterator.
 ///
-/// `RcIter` allows doing interesting things like using `.zip()` on an iterator with
-/// itself, at the cost of runtime borrow checking which may have a performance
-/// penalty.
+/// `RcIter` allows doing interesting things like using `.zip()` on an iterator
+/// with itself, at the cost of runtime borrow checking which may have a
+/// performance penalty.
 ///
 /// Iterator element type is `Self::Item`.
 ///
@@ -45,7 +45,8 @@
 /// `.next()`, i.e. if it somehow participates in an “iterator knot”
 /// where it is an adaptor of itself.
 pub fn rciter<I>(iterable: I) -> RcIter<I::IntoIter>
-    where I: IntoIterator
+where
+    I: IntoIterator,
 {
     RcIter { rciter: Rc::new(RefCell::new(iterable.into_iter())) }
 }
@@ -55,7 +56,8 @@
 }
 
 impl<A, I> Iterator for RcIter<I>
-    where I: Iterator<Item = A>
+where
+    I: Iterator<Item = A>,
 {
     type Item = A;
     #[inline]
@@ -73,7 +75,8 @@
 }
 
 impl<I> DoubleEndedIterator for RcIter<I>
-    where I: DoubleEndedIterator
+where
+    I: DoubleEndedIterator,
 {
     #[inline]
     fn next_back(&mut self) -> Option<Self::Item> {
@@ -82,8 +85,9 @@
 }
 
 /// Return an iterator from `&RcIter<I>` (by simply cloning it).
-impl<'a, I> IntoIterator for &'a RcIter<I>
-    where I: Iterator
+impl<I> IntoIterator for &RcIter<I>
+where
+    I: Iterator,
 {
     type Item = I::Item;
     type IntoIter = RcIter<I>;
@@ -93,7 +97,4 @@
     }
 }
 
-
-impl<A, I> FusedIterator for RcIter<I>
-    where I: FusedIterator<Item = A>
-{}
+impl<A, I> FusedIterator for RcIter<I> where I: FusedIterator<Item = A> {}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/repeatn.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/repeatn.rs
new file mode 100644
index 0000000..b2037e15
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/repeatn.rs
@@ -0,0 +1,80 @@
+use std::iter::FusedIterator;
+
+/// An iterator that produces *n* repetitions of an element.
+///
+/// See [`repeat_n()`](crate::repeat_n) for more information.
+#[must_use = "iterators are lazy and do nothing unless consumed"]
+#[derive(Clone, Debug)]
+pub struct RepeatN<A> {
+    pub(crate) elt: Option<A>,
+    n: usize,
+}
+
+/// Create an iterator that produces `n` repetitions of `element`.
+pub fn repeat_n<A>(element: A, n: usize) -> RepeatN<A>
+where
+    A: Clone,
+{
+    if n == 0 {
+        RepeatN { elt: None, n }
+    } else {
+        RepeatN { elt: Some(element), n }
+    }
+}
+
+impl<A> Iterator for RepeatN<A>
+where
+    A: Clone,
+{
+    type Item = A;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.n > 1 {
+            self.n -= 1;
+            self.elt.as_ref().cloned()
+        } else {
+            self.n = 0;
+            self.elt.take()
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.n, Some(self.n))
+    }
+
+    fn fold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        match self {
+            Self { elt: Some(elt), n } => {
+                debug_assert!(n > 0);
+                init = (1..n).map(|_| elt.clone()).fold(init, &mut f);
+                f(init, elt)
+            }
+            _ => init,
+        }
+    }
+}
+
+impl<A> DoubleEndedIterator for RepeatN<A>
+where
+    A: Clone,
+{
+    #[inline]
+    fn next_back(&mut self) -> Option<Self::Item> {
+        self.next()
+    }
+
+    #[inline]
+    fn rfold<B, F>(self, init: B, f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        self.fold(init, f)
+    }
+}
+
+impl<A> ExactSizeIterator for RepeatN<A> where A: Clone {}
+
+impl<A> FusedIterator for RepeatN<A> where A: Clone {}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/size_hint.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/size_hint.rs
similarity index 72%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/size_hint.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/size_hint.rs
index 71ea1412..dbaaa77e 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/size_hint.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/size_hint.rs
@@ -1,9 +1,6 @@
 //! Arithmetic on `Iterator.size_hint()` values.
-//!
 
-use std::usize;
 use std::cmp;
-use std::u32;
 
 /// `SizeHint` is the return type of `Iterator::size_hint()`.
 pub type SizeHint = (usize, Option<usize>);
@@ -31,7 +28,6 @@
 
 /// Subtract `x` correctly from a `SizeHint`.
 #[inline]
-#[allow(dead_code)]
 pub fn sub_scalar(sh: SizeHint, x: usize) -> SizeHint {
     let (mut low, mut hi) = sh;
     low = low.saturating_sub(x);
@@ -39,22 +35,7 @@
     (low, hi)
 }
 
-
 /// Multiply `SizeHint` correctly
-///
-/// ```ignore
-/// use std::usize;
-/// use itertools::size_hint;
-///
-/// assert_eq!(size_hint::mul((3, Some(4)), (3, Some(4))),
-///            (9, Some(16)));
-///
-/// assert_eq!(size_hint::mul((3, Some(4)), (usize::MAX, None)),
-///            (usize::MAX, None));
-///
-/// assert_eq!(size_hint::mul((3, None), (0, Some(0))),
-///            (0, Some(0)));
-/// ```
 #[inline]
 pub fn mul(a: SizeHint, b: SizeHint) -> SizeHint {
     let low = a.0.saturating_mul(b.0);
@@ -75,20 +56,6 @@
     (low, hi)
 }
 
-/// Raise `base` correctly by a `SizeHint` exponent.
-#[inline]
-pub fn pow_scalar_base(base: usize, exp: SizeHint) -> SizeHint {
-    let exp_low = cmp::min(exp.0, u32::MAX as usize) as u32;
-    let low = base.saturating_pow(exp_low);
-
-    let hi = exp.1.and_then(|exp| {
-        let exp_hi = cmp::min(exp, u32::MAX as usize) as u32;
-        base.checked_pow(exp_hi)
-    });
-
-    (low, hi)
-}
-
 /// Return the maximum
 #[inline]
 pub fn max(a: SizeHint, b: SizeHint) -> SizeHint {
@@ -117,3 +84,10 @@
     };
     (lower, upper)
 }
+
+#[test]
+fn mul_size_hints() {
+    assert_eq!(mul((3, Some(4)), (3, Some(4))), (9, Some(16)));
+    assert_eq!(mul((3, Some(4)), (usize::MAX, None)), (usize::MAX, None));
+    assert_eq!(mul((3, None), (0, Some(0))), (0, Some(0)));
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/sources.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/sources.rs
similarity index 62%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/sources.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/sources.rs
index 3877ce3c..936458d 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/sources.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/sources.rs
@@ -5,62 +5,6 @@
 use std::fmt;
 use std::mem;
 
-/// See [`repeat_call`](crate::repeat_call) for more information.
-#[derive(Clone)]
-#[deprecated(note="Use std repeat_with() instead", since="0.8.0")]
-pub struct RepeatCall<F> {
-    f: F,
-}
-
-impl<F> fmt::Debug for RepeatCall<F>
-{
-    debug_fmt_fields!(RepeatCall, );
-}
-
-/// An iterator source that produces elements indefinitely by calling
-/// a given closure.
-///
-/// Iterator element type is the return type of the closure.
-///
-/// ```
-/// use itertools::repeat_call;
-/// use itertools::Itertools;
-/// use std::collections::BinaryHeap;
-///
-/// let mut heap = BinaryHeap::from(vec![2, 5, 3, 7, 8]);
-///
-/// // extract each element in sorted order
-/// for element in repeat_call(|| heap.pop()).while_some() {
-///     print!("{}", element);
-/// }
-///
-/// itertools::assert_equal(
-///     repeat_call(|| 1).take(5),
-///     vec![1, 1, 1, 1, 1]
-/// );
-/// ```
-#[deprecated(note="Use std repeat_with() instead", since="0.8.0")]
-pub fn repeat_call<F, A>(function: F) -> RepeatCall<F>
-    where F: FnMut() -> A
-{
-    RepeatCall { f: function }
-}
-
-impl<A, F> Iterator for RepeatCall<F>
-    where F: FnMut() -> A
-{
-    type Item = A;
-
-    #[inline]
-    fn next(&mut self) -> Option<Self::Item> {
-        Some((self.f)())
-    }
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        (usize::max_value(), None)
-    }
-}
-
 /// Creates a new unfold source with the specified closure as the "iterator
 /// function" and an initial state to eventually pass to the closure
 ///
@@ -97,17 +41,20 @@
 ///                         vec![1, 1, 2, 3, 5, 8, 13, 21]);
 /// assert_eq!(fibonacci.last(), Some(2_971_215_073))
 /// ```
+#[deprecated(
+    note = "Use [std::iter::from_fn](https://doc.rust-lang.org/std/iter/fn.from_fn.html) instead",
+    since = "0.13.0"
+)]
 pub fn unfold<A, St, F>(initial_state: St, f: F) -> Unfold<St, F>
-    where F: FnMut(&mut St) -> Option<A>
+where
+    F: FnMut(&mut St) -> Option<A>,
 {
-    Unfold {
-        f,
-        state: initial_state,
-    }
+    Unfold { f, state: initial_state }
 }
 
 impl<St, F> fmt::Debug for Unfold<St, F>
-    where St: fmt::Debug,
+where
+    St: fmt::Debug,
 {
     debug_fmt_fields!(Unfold, state);
 }
@@ -115,6 +62,10 @@
 /// See [`unfold`](crate::unfold) for more information.
 #[derive(Clone)]
 #[must_use = "iterators are lazy and do nothing unless consumed"]
+#[deprecated(
+    note = "Use [std::iter::FromFn](https://doc.rust-lang.org/std/iter/struct.FromFn.html) instead",
+    since = "0.13.0"
+)]
 pub struct Unfold<St, F> {
     f: F,
     /// Internal state that will be passed to the closure on the next iteration
@@ -122,7 +73,8 @@
 }
 
 impl<A, St, F> Iterator for Unfold<St, F>
-    where F: FnMut(&mut St) -> Option<A>
+where
+    F: FnMut(&mut St) -> Option<A>,
 {
     type Item = A;
 
@@ -144,13 +96,15 @@
 }
 
 impl<St, F> fmt::Debug for Iterate<St, F>
-    where St: fmt::Debug,
+where
+    St: fmt::Debug,
 {
     debug_fmt_fields!(Iterate, state);
 }
 
 impl<St, F> Iterator for Iterate<St, F>
-    where F: FnMut(&St) -> St
+where
+    F: FnMut(&St) -> St,
 {
     type Item = St;
 
@@ -162,22 +116,34 @@
 
     #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
-        (usize::max_value(), None)
+        (usize::MAX, None)
     }
 }
 
-/// Creates a new iterator that infinitely applies function to value and yields results.
+/// Creates a new iterator that infinitely applies function to value and yields
+/// results.
 ///
 /// ```
 /// use itertools::iterate;
 ///
-/// itertools::assert_equal(iterate(1, |&i| i * 3).take(5), vec![1, 3, 9, 27, 81]);
+/// itertools::assert_equal(iterate(1, |i| i % 3 + 1).take(5), vec![1, 2, 3, 1, 2]);
 /// ```
+///
+/// **Panics** if compute the next value does.
+///
+/// ```should_panic
+/// # use itertools::iterate;
+/// let mut it = iterate(25u32, |x| x - 10).take_while(|&x| x > 10);
+/// assert_eq!(it.next(), Some(25)); // `Iterate` holds 15.
+/// assert_eq!(it.next(), Some(15)); // `Iterate` holds 5.
+/// it.next(); // `5 - 10` overflows.
+/// ```
+///
+/// You can alternatively use [`core::iter::successors`] as it better describes
+/// a finite iterator.
 pub fn iterate<St, F>(initial_value: St, f: F) -> Iterate<St, F>
-    where F: FnMut(&St) -> St
+where
+    F: FnMut(&St) -> St,
 {
-    Iterate {
-        state: initial_value,
-        f,
-    }
+    Iterate { state: initial_value, f }
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/take_while_inclusive.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/take_while_inclusive.rs
new file mode 100644
index 0000000..abd5c1d
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/take_while_inclusive.rs
@@ -0,0 +1,92 @@
+use core::iter::FusedIterator;
+use std::fmt;
+
+/// An iterator adaptor that consumes elements while the given predicate is
+/// `true`, including the element for which the predicate first returned
+/// `false`.
+///
+/// See [`.take_while_inclusive()`](crate::Itertools::take_while_inclusive)
+/// for more information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[derive(Clone)]
+pub struct TakeWhileInclusive<I, F> {
+    iter: I,
+    predicate: F,
+    done: bool,
+}
+
+impl<I, F> TakeWhileInclusive<I, F>
+where
+    I: Iterator,
+    F: FnMut(&I::Item) -> bool,
+{
+    /// Create a new [`TakeWhileInclusive`] from an iterator and a predicate.
+    pub(crate) fn new(iter: I, predicate: F) -> Self {
+        Self { iter, predicate, done: false }
+    }
+}
+
+impl<I, F> fmt::Debug for TakeWhileInclusive<I, F>
+where
+    I: Iterator + fmt::Debug,
+{
+    debug_fmt_fields!(TakeWhileInclusive, iter, done);
+}
+
+impl<I, F> Iterator for TakeWhileInclusive<I, F>
+where
+    I: Iterator,
+    F: FnMut(&I::Item) -> bool,
+{
+    type Item = I::Item;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.done {
+            None
+        } else {
+            self.iter.next().map(|item| {
+                if !(self.predicate)(&item) {
+                    self.done = true;
+                }
+                item
+            })
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        if self.done {
+            (0, Some(0))
+        } else {
+            (0, self.iter.size_hint().1)
+        }
+    }
+
+    fn fold<B, Fold>(mut self, init: B, mut f: Fold) -> B
+    where
+        Fold: FnMut(B, Self::Item) -> B,
+    {
+        if self.done {
+            init
+        } else {
+            let predicate = &mut self.predicate;
+            self.iter
+                .try_fold(init, |mut acc, item| {
+                    let is_ok = predicate(&item);
+                    acc = f(acc, item);
+                    if is_ok {
+                        Ok(acc)
+                    } else {
+                        Err(acc)
+                    }
+                })
+                .unwrap_or_else(|err| err)
+        }
+    }
+}
+
+impl<I, F> FusedIterator for TakeWhileInclusive<I, F>
+where
+    I: Iterator,
+    F: FnMut(&I::Item) -> bool,
+{
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tee.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tee.rs
similarity index 83%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tee.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tee.rs
index ea47529..f2659104 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tee.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tee.rs
@@ -1,8 +1,8 @@
 use super::size_hint;
 
-use std::cell::RefCell;
 use alloc::collections::VecDeque;
 use alloc::rc::Rc;
+use std::cell::RefCell;
 
 /// Common buffer object for the two tee halves
 #[derive(Debug)]
@@ -19,24 +19,27 @@
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
 #[derive(Debug)]
 pub struct Tee<I>
-    where I: Iterator
+where
+    I: Iterator,
 {
     rcbuffer: Rc<RefCell<TeeBuffer<I::Item, I>>>,
     id: bool,
 }
 
 pub fn new<I>(iter: I) -> (Tee<I>, Tee<I>)
-    where I: Iterator
+where
+    I: Iterator,
 {
-    let buffer = TeeBuffer{backlog: VecDeque::new(), iter, owner: false};
-    let t1 = Tee{rcbuffer: Rc::new(RefCell::new(buffer)), id: true};
-    let t2 = Tee{rcbuffer: t1.rcbuffer.clone(), id: false};
+    let buffer = TeeBuffer { backlog: VecDeque::new(), iter, owner: false };
+    let t1 = Tee { rcbuffer: Rc::new(RefCell::new(buffer)), id: true };
+    let t2 = Tee { rcbuffer: t1.rcbuffer.clone(), id: false };
     (t1, t2)
 }
 
 impl<I> Iterator for Tee<I>
-    where I: Iterator,
-          I::Item: Clone
+where
+    I: Iterator,
+    I::Item: Clone,
 {
     type Item = I::Item;
     fn next(&mut self) -> Option<Self::Item> {
@@ -73,6 +76,8 @@
 }
 
 impl<I> ExactSizeIterator for Tee<I>
-    where I: ExactSizeIterator,
-          I::Item: Clone
-{}
+where
+    I: ExactSizeIterator,
+    I::Item: Clone,
+{
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tuple_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tuple_impl.rs
new file mode 100644
index 0000000..faed07f
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tuple_impl.rs
@@ -0,0 +1,398 @@
+//! Some iterator that produces tuples
+
+use std::iter::Cycle;
+use std::iter::Fuse;
+use std::iter::FusedIterator;
+
+use crate::size_hint;
+
+// `HomogeneousTuple` is a public facade for `TupleCollect`, allowing
+// tuple-related methods to be used by clients in generic contexts, while
+// hiding the implementation details of `TupleCollect`.
+// See https://github.com/rust-itertools/itertools/issues/387
+
+/// Implemented for homogeneous tuples of size up to 12.
+pub trait HomogeneousTuple: TupleCollect {}
+
+impl<T: TupleCollect> HomogeneousTuple for T {}
+
+/// An iterator over a incomplete tuple.
+///
+/// See [`.tuples()`](crate::Itertools::tuples) and
+/// [`Tuples::into_buffer()`].
+#[derive(Clone, Debug)]
+pub struct TupleBuffer<T>
+where
+    T: HomogeneousTuple,
+{
+    cur: usize,
+    buf: T::Buffer,
+}
+
+impl<T> TupleBuffer<T>
+where
+    T: HomogeneousTuple,
+{
+    fn new(buf: T::Buffer) -> Self {
+        Self { cur: 0, buf }
+    }
+}
+
+impl<T> Iterator for TupleBuffer<T>
+where
+    T: HomogeneousTuple,
+{
+    type Item = T::Item;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let s = self.buf.as_mut();
+        if let Some(ref mut item) = s.get_mut(self.cur) {
+            self.cur += 1;
+            item.take()
+        } else {
+            None
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let buffer = &self.buf.as_ref()[self.cur..];
+        let len = if buffer.is_empty() {
+            0
+        } else {
+            buffer.iter().position(|x| x.is_none()).unwrap_or(buffer.len())
+        };
+        (len, Some(len))
+    }
+}
+
+impl<T> ExactSizeIterator for TupleBuffer<T> where T: HomogeneousTuple {}
+
+/// An iterator that groups the items in tuples of a specific size.
+///
+/// See [`.tuples()`](crate::Itertools::tuples) for more information.
+#[derive(Clone, Debug)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct Tuples<I, T>
+where
+    I: Iterator<Item = T::Item>,
+    T: HomogeneousTuple,
+{
+    iter: Fuse<I>,
+    buf: T::Buffer,
+}
+
+/// Create a new tuples iterator.
+pub fn tuples<I, T>(iter: I) -> Tuples<I, T>
+where
+    I: Iterator<Item = T::Item>,
+    T: HomogeneousTuple,
+{
+    Tuples { iter: iter.fuse(), buf: Default::default() }
+}
+
+impl<I, T> Iterator for Tuples<I, T>
+where
+    I: Iterator<Item = T::Item>,
+    T: HomogeneousTuple,
+{
+    type Item = T;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        T::collect_from_iter(&mut self.iter, &mut self.buf)
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        // The number of elts we've drawn from the underlying iterator, but have
+        // not yet produced as a tuple.
+        let buffered = T::buffer_len(&self.buf);
+        // To that, we must add the size estimates of the underlying iterator.
+        let (unbuffered_lo, unbuffered_hi) = self.iter.size_hint();
+        // The total low estimate is the sum of the already-buffered elements,
+        // plus the low estimate of remaining unbuffered elements, divided by
+        // the tuple size.
+        let total_lo = add_then_div(unbuffered_lo, buffered, T::num_items()).unwrap_or(usize::MAX);
+        // And likewise for the total high estimate, but using the high estimate
+        // of the remaining unbuffered elements.
+        let total_hi = unbuffered_hi.and_then(|hi| add_then_div(hi, buffered, T::num_items()));
+        (total_lo, total_hi)
+    }
+}
+
+/// `(n + a) / d` avoiding overflow when possible, returns `None` if it
+/// overflows.
+fn add_then_div(n: usize, a: usize, d: usize) -> Option<usize> {
+    debug_assert_ne!(d, 0);
+    (n / d).checked_add(a / d)?.checked_add((n % d + a % d) / d)
+}
+
+impl<I, T> ExactSizeIterator for Tuples<I, T>
+where
+    I: ExactSizeIterator<Item = T::Item>,
+    T: HomogeneousTuple,
+{
+}
+
+impl<I, T> Tuples<I, T>
+where
+    I: Iterator<Item = T::Item>,
+    T: HomogeneousTuple,
+{
+    /// Return a buffer with the produced items that was not enough to be
+    /// grouped in a tuple.
+    ///
+    /// ```
+    /// use itertools::Itertools;
+    ///
+    /// let mut iter = (0..5).tuples();
+    /// assert_eq!(Some((0, 1, 2)), iter.next());
+    /// assert_eq!(None, iter.next());
+    /// itertools::assert_equal(vec![3, 4], iter.into_buffer());
+    /// ```
+    pub fn into_buffer(self) -> TupleBuffer<T> {
+        TupleBuffer::new(self.buf)
+    }
+}
+
+/// An iterator over all contiguous windows that produces tuples of a specific
+/// size.
+///
+/// See [`.tuple_windows()`](crate::Itertools::tuple_windows) for more
+/// information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[derive(Clone, Debug)]
+pub struct TupleWindows<I, T>
+where
+    I: Iterator<Item = T::Item>,
+    T: HomogeneousTuple,
+{
+    iter: I,
+    last: Option<T>,
+}
+
+/// Create a new tuple windows iterator.
+pub fn tuple_windows<I, T>(iter: I) -> TupleWindows<I, T>
+where
+    I: Iterator<Item = T::Item>,
+    T: HomogeneousTuple,
+    T::Item: Clone,
+{
+    TupleWindows { last: None, iter }
+}
+
+impl<I, T> Iterator for TupleWindows<I, T>
+where
+    I: Iterator<Item = T::Item>,
+    T: HomogeneousTuple + Clone,
+    T::Item: Clone,
+{
+    type Item = T;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if T::num_items() == 1 {
+            return T::collect_from_iter_no_buf(&mut self.iter);
+        }
+        if let Some(new) = self.iter.next() {
+            if let Some(ref mut last) = self.last {
+                last.left_shift_push(new);
+                Some(last.clone())
+            } else {
+                use std::iter::once;
+                let iter = once(new).chain(&mut self.iter);
+                self.last = T::collect_from_iter_no_buf(iter);
+                self.last.clone()
+            }
+        } else {
+            None
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let mut sh = self.iter.size_hint();
+        // Adjust the size hint at the beginning
+        // OR when `num_items == 1` (but it does not change the size hint).
+        if self.last.is_none() {
+            sh = size_hint::sub_scalar(sh, T::num_items() - 1);
+        }
+        sh
+    }
+}
+
+impl<I, T> ExactSizeIterator for TupleWindows<I, T>
+where
+    I: ExactSizeIterator<Item = T::Item>,
+    T: HomogeneousTuple + Clone,
+    T::Item: Clone,
+{
+}
+
+impl<I, T> FusedIterator for TupleWindows<I, T>
+where
+    I: FusedIterator<Item = T::Item>,
+    T: HomogeneousTuple + Clone,
+    T::Item: Clone,
+{
+}
+
+/// An iterator over all windows, wrapping back to the first elements when the
+/// window would otherwise exceed the length of the iterator, producing tuples
+/// of a specific size.
+///
+/// See [`.circular_tuple_windows()`](crate::Itertools::circular_tuple_windows)
+/// for more information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[derive(Debug, Clone)]
+pub struct CircularTupleWindows<I, T>
+where
+    I: Iterator<Item = T::Item> + Clone,
+    T: TupleCollect + Clone,
+{
+    iter: TupleWindows<Cycle<I>, T>,
+    len: usize,
+}
+
+pub fn circular_tuple_windows<I, T>(iter: I) -> CircularTupleWindows<I, T>
+where
+    I: Iterator<Item = T::Item> + Clone + ExactSizeIterator,
+    T: TupleCollect + Clone,
+    T::Item: Clone,
+{
+    let len = iter.len();
+    let iter = tuple_windows(iter.cycle());
+
+    CircularTupleWindows { iter, len }
+}
+
+impl<I, T> Iterator for CircularTupleWindows<I, T>
+where
+    I: Iterator<Item = T::Item> + Clone,
+    T: TupleCollect + Clone,
+    T::Item: Clone,
+{
+    type Item = T;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.len != 0 {
+            self.len -= 1;
+            self.iter.next()
+        } else {
+            None
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.len, Some(self.len))
+    }
+}
+
+impl<I, T> ExactSizeIterator for CircularTupleWindows<I, T>
+where
+    I: Iterator<Item = T::Item> + Clone,
+    T: TupleCollect + Clone,
+    T::Item: Clone,
+{
+}
+
+impl<I, T> FusedIterator for CircularTupleWindows<I, T>
+where
+    I: Iterator<Item = T::Item> + Clone,
+    T: TupleCollect + Clone,
+    T::Item: Clone,
+{
+}
+
+pub trait TupleCollect: Sized {
+    type Item;
+    type Buffer: Default + AsRef<[Option<Self::Item>]> + AsMut<[Option<Self::Item>]>;
+
+    fn buffer_len(buf: &Self::Buffer) -> usize {
+        let s = buf.as_ref();
+        s.iter().position(Option::is_none).unwrap_or(s.len())
+    }
+
+    fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self>
+    where
+        I: IntoIterator<Item = Self::Item>;
+
+    fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self>
+    where
+        I: IntoIterator<Item = Self::Item>;
+
+    fn num_items() -> usize;
+
+    fn left_shift_push(&mut self, item: Self::Item);
+}
+
+macro_rules! rev_for_each_ident{
+    ($m:ident, ) => {};
+    ($m:ident, $i0:ident, $($i:ident,)*) => {
+        rev_for_each_ident!($m, $($i,)*);
+        $m!($i0);
+    };
+}
+
+macro_rules! impl_tuple_collect {
+    ($dummy:ident,) => {}; // stop
+    ($dummy:ident, $($Y:ident,)*) => (
+        impl_tuple_collect!($($Y,)*);
+        impl<A> TupleCollect for ($(ignore_ident!($Y, A),)*) {
+            type Item = A;
+            type Buffer = [Option<A>; count_ident!($($Y)*) - 1];
+
+            #[allow(unused_assignments, unused_mut)]
+            fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self>
+                where I: IntoIterator<Item = A>
+            {
+                let mut iter = iter.into_iter();
+                $(
+                    let mut $Y = None;
+                )*
+
+                loop {
+                    $(
+                        $Y = iter.next();
+                        if $Y.is_none() {
+                            break
+                        }
+                    )*
+                    return Some(($($Y.unwrap()),*,))
+                }
+
+                let mut i = 0;
+                let mut s = buf.as_mut();
+                $(
+                    if i < s.len() {
+                        s[i] = $Y;
+                        i += 1;
+                    }
+                )*
+                return None;
+            }
+
+            fn collect_from_iter_no_buf<I>(iter: I) -> Option<Self>
+                where I: IntoIterator<Item = A>
+            {
+                let mut iter = iter.into_iter();
+
+                Some(($(
+                    { let $Y = iter.next()?; $Y },
+                )*))
+            }
+
+            fn num_items() -> usize {
+                count_ident!($($Y)*)
+            }
+
+            fn left_shift_push(&mut self, mut item: A) {
+                use std::mem::replace;
+
+                let &mut ($(ref mut $Y),*,) = self;
+                macro_rules! replace_item{($i:ident) => {
+                    item = replace($i, item);
+                }}
+                rev_for_each_ident!(replace_item, $($Y,)*);
+                drop(item);
+            }
+        }
+    )
+}
+impl_tuple_collect!(dummy, a, b, c, d, e, f, g, h, i, j, k, l,);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unique_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unique_impl.rs
similarity index 62%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unique_impl.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unique_impl.rs
index 4e81e78..64f4d97 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unique_impl.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unique_impl.rs
@@ -1,7 +1,7 @@
-use std::collections::HashMap;
 use std::collections::hash_map::Entry;
-use std::hash::Hash;
+use std::collections::HashMap;
 use std::fmt;
+use std::hash::Hash;
 use std::iter::FusedIterator;
 
 /// An iterator adapter to filter out duplicate elements.
@@ -19,29 +19,29 @@
 }
 
 impl<I, V, F> fmt::Debug for UniqueBy<I, V, F>
-    where I: Iterator + fmt::Debug,
-          V: fmt::Debug + Hash + Eq,
+where
+    I: Iterator + fmt::Debug,
+    V: fmt::Debug + Hash + Eq,
 {
     debug_fmt_fields!(UniqueBy, iter, used);
 }
 
 /// Create a new `UniqueBy` iterator.
 pub fn unique_by<I, V, F>(iter: I, f: F) -> UniqueBy<I, V, F>
-    where V: Eq + Hash,
-          F: FnMut(&I::Item) -> V,
-          I: Iterator,
+where
+    V: Eq + Hash,
+    F: FnMut(&I::Item) -> V,
+    I: Iterator,
 {
-    UniqueBy {
-        iter,
-        used: HashMap::new(),
-        f,
-    }
+    UniqueBy { iter, used: HashMap::new(), f }
 }
 
-// count the number of new unique keys in iterable (`used` is the set already seen)
+// count the number of new unique keys in iterable (`used` is the set already
+// seen)
 fn count_new_keys<I, K>(mut used: HashMap<K, ()>, iterable: I) -> usize
-    where I: IntoIterator<Item=K>,
-          K: Hash + Eq,
+where
+    I: IntoIterator<Item = K>,
+    K: Hash + Eq,
 {
     let iter = iterable.into_iter();
     let current_used = used.len();
@@ -50,20 +50,16 @@
 }
 
 impl<I, V, F> Iterator for UniqueBy<I, V, F>
-    where I: Iterator,
-          V: Eq + Hash,
-          F: FnMut(&I::Item) -> V
+where
+    I: Iterator,
+    V: Eq + Hash,
+    F: FnMut(&I::Item) -> V,
 {
     type Item = I::Item;
 
     fn next(&mut self) -> Option<Self::Item> {
-        while let Some(v) = self.iter.next() {
-            let key = (self.f)(&v);
-            if self.used.insert(key, ()).is_none() {
-                return Some(v);
-            }
-        }
-        None
+        let Self { iter, used, f } = self;
+        iter.find(|v| used.insert(f(v), ()).is_none())
     }
 
     #[inline]
@@ -79,42 +75,42 @@
 }
 
 impl<I, V, F> DoubleEndedIterator for UniqueBy<I, V, F>
-    where I: DoubleEndedIterator,
-          V: Eq + Hash,
-          F: FnMut(&I::Item) -> V
+where
+    I: DoubleEndedIterator,
+    V: Eq + Hash,
+    F: FnMut(&I::Item) -> V,
 {
     fn next_back(&mut self) -> Option<Self::Item> {
-        while let Some(v) = self.iter.next_back() {
-            let key = (self.f)(&v);
-            if self.used.insert(key, ()).is_none() {
-                return Some(v);
-            }
-        }
-        None
+        let Self { iter, used, f } = self;
+        iter.rfind(|v| used.insert(f(v), ()).is_none())
     }
 }
 
 impl<I, V, F> FusedIterator for UniqueBy<I, V, F>
-    where I: FusedIterator,
-          V: Eq + Hash,
-          F: FnMut(&I::Item) -> V
-{}
+where
+    I: FusedIterator,
+    V: Eq + Hash,
+    F: FnMut(&I::Item) -> V,
+{
+}
 
 impl<I> Iterator for Unique<I>
-    where I: Iterator,
-          I::Item: Eq + Hash + Clone
+where
+    I: Iterator,
+    I::Item: Eq + Hash + Clone,
 {
     type Item = I::Item;
 
     fn next(&mut self) -> Option<Self::Item> {
-        while let Some(v) = self.iter.iter.next() {
-            if let Entry::Vacant(entry) = self.iter.used.entry(v) {
+        let UniqueBy { iter, used, .. } = &mut self.iter;
+        iter.find_map(|v| {
+            if let Entry::Vacant(entry) = used.entry(v) {
                 let elt = entry.key().clone();
                 entry.insert(());
                 return Some(elt);
             }
-        }
-        None
+            None
+        })
     }
 
     #[inline]
@@ -129,51 +125,55 @@
 }
 
 impl<I> DoubleEndedIterator for Unique<I>
-    where I: DoubleEndedIterator,
-          I::Item: Eq + Hash + Clone
+where
+    I: DoubleEndedIterator,
+    I::Item: Eq + Hash + Clone,
 {
     fn next_back(&mut self) -> Option<Self::Item> {
-        while let Some(v) = self.iter.iter.next_back() {
-            if let Entry::Vacant(entry) = self.iter.used.entry(v) {
+        let UniqueBy { iter, used, .. } = &mut self.iter;
+        iter.rev().find_map(|v| {
+            if let Entry::Vacant(entry) = used.entry(v) {
                 let elt = entry.key().clone();
                 entry.insert(());
                 return Some(elt);
             }
-        }
-        None
+            None
+        })
     }
 }
 
 impl<I> FusedIterator for Unique<I>
-    where I: FusedIterator,
-          I::Item: Eq + Hash + Clone
-{}
+where
+    I: FusedIterator,
+    I::Item: Eq + Hash + Clone,
+{
+}
 
 /// An iterator adapter to filter out duplicate elements.
 ///
 /// See [`.unique()`](crate::Itertools::unique) for more information.
 #[derive(Clone)]
 #[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
-pub struct Unique<I: Iterator> {
+pub struct Unique<I>
+where
+    I: Iterator,
+    I::Item: Eq + Hash + Clone,
+{
     iter: UniqueBy<I, I::Item, ()>,
 }
 
 impl<I> fmt::Debug for Unique<I>
-    where I: Iterator + fmt::Debug,
-          I::Item: Hash + Eq + fmt::Debug,
+where
+    I: Iterator + fmt::Debug,
+    I::Item: Hash + Eq + fmt::Debug + Clone,
 {
     debug_fmt_fields!(Unique, iter);
 }
 
 pub fn unique<I>(iter: I) -> Unique<I>
-    where I: Iterator,
-          I::Item: Eq + Hash,
+where
+    I: Iterator,
+    I::Item: Eq + Hash + Clone,
 {
-    Unique {
-        iter: UniqueBy {
-            iter,
-            used: HashMap::new(),
-            f: (),
-        }
-    }
+    Unique { iter: UniqueBy { iter, used: HashMap::new(), f: () } }
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unziptuple.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unziptuple.rs
similarity index 96%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unziptuple.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unziptuple.rs
index 7af29ec4..5df899e 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unziptuple.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unziptuple.rs
@@ -1,7 +1,7 @@
 /// Converts an iterator of tuples into a tuple of containers.
 ///
-/// `unzip()` consumes an entire iterator of n-ary tuples, producing `n` collections, one for each
-/// column.
+/// `multiunzip()` consumes an entire iterator of n-ary tuples, producing `n`
+/// collections, one for each column.
 ///
 /// This function is, in some sense, the opposite of [`multizip`].
 ///
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/with_position.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/with_position.rs
new file mode 100644
index 0000000..9f9453a
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/with_position.rs
@@ -0,0 +1,123 @@
+use std::fmt;
+use std::iter::{Fuse, FusedIterator, Peekable};
+
+/// An iterator adaptor that wraps each element in an [`Position`].
+///
+/// Iterator element type is `(Position, I::Item)`.
+///
+/// See [`.with_position()`](crate::Itertools::with_position) for more
+/// information.
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct WithPosition<I>
+where
+    I: Iterator,
+{
+    handled_first: bool,
+    peekable: Peekable<Fuse<I>>,
+}
+
+impl<I> fmt::Debug for WithPosition<I>
+where
+    I: Iterator,
+    Peekable<Fuse<I>>: fmt::Debug,
+{
+    debug_fmt_fields!(WithPosition, handled_first, peekable);
+}
+
+impl<I> Clone for WithPosition<I>
+where
+    I: Clone + Iterator,
+    I::Item: Clone,
+{
+    clone_fields!(handled_first, peekable);
+}
+
+/// Create a new `WithPosition` iterator.
+pub fn with_position<I>(iter: I) -> WithPosition<I>
+where
+    I: Iterator,
+{
+    WithPosition { handled_first: false, peekable: iter.fuse().peekable() }
+}
+
+/// The first component of the value yielded by `WithPosition`.
+/// Indicates the position of this element in the iterator results.
+///
+/// See [`.with_position()`](crate::Itertools::with_position) for more
+/// information.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum Position {
+    /// This is the first element.
+    First,
+    /// This is neither the first nor the last element.
+    Middle,
+    /// This is the last element.
+    Last,
+    /// This is the only element.
+    Only,
+}
+
+impl<I: Iterator> Iterator for WithPosition<I> {
+    type Item = (Position, I::Item);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match self.peekable.next() {
+            Some(item) => {
+                if !self.handled_first {
+                    // Haven't seen the first item yet, and there is one to give.
+                    self.handled_first = true;
+                    // Peek to see if this is also the last item,
+                    // in which case tag it as `Only`.
+                    match self.peekable.peek() {
+                        Some(_) => Some((Position::First, item)),
+                        None => Some((Position::Only, item)),
+                    }
+                } else {
+                    // Have seen the first item, and there's something left.
+                    // Peek to see if this is the last item.
+                    match self.peekable.peek() {
+                        Some(_) => Some((Position::Middle, item)),
+                        None => Some((Position::Last, item)),
+                    }
+                }
+            }
+            // Iterator is finished.
+            None => None,
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.peekable.size_hint()
+    }
+
+    fn fold<B, F>(mut self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        if let Some(mut head) = self.peekable.next() {
+            if !self.handled_first {
+                // The current head is `First` or `Only`,
+                // it depends if there is another item or not.
+                match self.peekable.next() {
+                    Some(second) => {
+                        let first = std::mem::replace(&mut head, second);
+                        init = f(init, (Position::First, first));
+                    }
+                    None => return f(init, (Position::Only, head)),
+                }
+            }
+            // Have seen the first item, and there's something left.
+            init = self.peekable.fold(init, |acc, mut item| {
+                std::mem::swap(&mut head, &mut item);
+                f(acc, (Position::Middle, item))
+            });
+            // The "head" is now the last item.
+            init = f(init, (Position::Last, head));
+        }
+        init
+    }
+}
+
+impl<I> ExactSizeIterator for WithPosition<I> where I: ExactSizeIterator {}
+
+impl<I: Iterator> FusedIterator for WithPosition<I> {}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_eq_impl.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_eq_impl.rs
similarity index 62%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_eq_impl.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_eq_impl.rs
index a079b32..f4a16d7 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_eq_impl.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_eq_impl.rs
@@ -1,6 +1,7 @@
 use super::size_hint;
 
 /// An iterator which iterates two other iterators simultaneously
+/// and panic if they have different lengths.
 ///
 /// See [`.zip_eq()`](crate::Itertools::zip_eq) for more information.
 #[derive(Clone, Debug)]
@@ -10,11 +11,10 @@
     b: J,
 }
 
-/// Iterate `i` and `j` in lock step.
+/// Zips two iterators but **panics** if they are not of the same length.
 ///
-/// **Panics** if the iterators are not of the same length.
-///
-/// [`IntoIterator`] enabled version of [`Itertools::zip_eq`](crate::Itertools::zip_eq).
+/// [`IntoIterator`] enabled version of
+/// [`Itertools::zip_eq`](crate::Itertools::zip_eq).
 ///
 /// ```
 /// use itertools::zip_eq;
@@ -22,21 +22,21 @@
 /// let data = [1, 2, 3, 4, 5];
 /// for (a, b) in zip_eq(&data[..data.len() - 1], &data[1..]) {
 ///     /* loop body */
+///     # let _ = (a, b);
 /// }
 /// ```
 pub fn zip_eq<I, J>(i: I, j: J) -> ZipEq<I::IntoIter, J::IntoIter>
-    where I: IntoIterator,
-          J: IntoIterator
+where
+    I: IntoIterator,
+    J: IntoIterator,
 {
-    ZipEq {
-        a: i.into_iter(),
-        b: j.into_iter(),
-    }
+    ZipEq { a: i.into_iter(), b: j.into_iter() }
 }
 
 impl<I, J> Iterator for ZipEq<I, J>
-    where I: Iterator,
-          J: Iterator
+where
+    I: Iterator,
+    J: Iterator,
 {
     type Item = (I::Item, J::Item);
 
@@ -44,8 +44,9 @@
         match (self.a.next(), self.b.next()) {
             (None, None) => None,
             (Some(a), Some(b)) => Some((a, b)),
-            (None, Some(_)) | (Some(_), None) =>
-            panic!("itertools: .zip_eq() reached end of one iterator before the other")
+            (None, Some(_)) | (Some(_), None) => {
+                panic!("itertools: .zip_eq() reached end of one iterator before the other")
+            }
         }
     }
 
@@ -55,6 +56,8 @@
 }
 
 impl<I, J> ExactSizeIterator for ZipEq<I, J>
-    where I: ExactSizeIterator,
-          J: ExactSizeIterator
-{}
+where
+    I: ExactSizeIterator,
+    J: ExactSizeIterator,
+{
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_longest.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_longest.rs
new file mode 100644
index 0000000..565eed1
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_longest.rs
@@ -0,0 +1,126 @@
+use super::size_hint;
+use std::cmp::Ordering::{Equal, Greater, Less};
+use std::iter::{Fuse, FusedIterator};
+
+use crate::either_or_both::EitherOrBoth;
+
+// ZipLongest originally written by SimonSapin,
+// and dedicated to itertools https://github.com/rust-lang/rust/pull/19283
+
+/// An iterator which iterates two other iterators simultaneously
+/// and wraps the elements in [`EitherOrBoth`].
+///
+/// This iterator is *fused*.
+///
+/// See [`.zip_longest()`](crate::Itertools::zip_longest) for more information.
+#[derive(Clone, Debug)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct ZipLongest<T, U> {
+    a: Fuse<T>,
+    b: Fuse<U>,
+}
+
+/// Create a new `ZipLongest` iterator.
+pub fn zip_longest<T, U>(a: T, b: U) -> ZipLongest<T, U>
+where
+    T: Iterator,
+    U: Iterator,
+{
+    ZipLongest { a: a.fuse(), b: b.fuse() }
+}
+
+impl<T, U> Iterator for ZipLongest<T, U>
+where
+    T: Iterator,
+    U: Iterator,
+{
+    type Item = EitherOrBoth<T::Item, U::Item>;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        match (self.a.next(), self.b.next()) {
+            (None, None) => None,
+            (Some(a), None) => Some(EitherOrBoth::Left(a)),
+            (None, Some(b)) => Some(EitherOrBoth::Right(b)),
+            (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)),
+        }
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        size_hint::max(self.a.size_hint(), self.b.size_hint())
+    }
+
+    #[inline]
+    fn fold<B, F>(self, init: B, mut f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let Self { mut a, mut b } = self;
+        let res = a.try_fold(init, |init, a| match b.next() {
+            Some(b) => Ok(f(init, EitherOrBoth::Both(a, b))),
+            None => Err(f(init, EitherOrBoth::Left(a))),
+        });
+        match res {
+            Ok(acc) => b.map(EitherOrBoth::Right).fold(acc, f),
+            Err(acc) => a.map(EitherOrBoth::Left).fold(acc, f),
+        }
+    }
+}
+
+impl<T, U> DoubleEndedIterator for ZipLongest<T, U>
+where
+    T: DoubleEndedIterator + ExactSizeIterator,
+    U: DoubleEndedIterator + ExactSizeIterator,
+{
+    #[inline]
+    fn next_back(&mut self) -> Option<Self::Item> {
+        match self.a.len().cmp(&self.b.len()) {
+            Equal => match (self.a.next_back(), self.b.next_back()) {
+                (None, None) => None,
+                (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)),
+                // These can only happen if .len() is inconsistent with .next_back()
+                (Some(a), None) => Some(EitherOrBoth::Left(a)),
+                (None, Some(b)) => Some(EitherOrBoth::Right(b)),
+            },
+            Greater => self.a.next_back().map(EitherOrBoth::Left),
+            Less => self.b.next_back().map(EitherOrBoth::Right),
+        }
+    }
+
+    fn rfold<B, F>(self, mut init: B, mut f: F) -> B
+    where
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let Self { mut a, mut b } = self;
+        let a_len = a.len();
+        let b_len = b.len();
+        match a_len.cmp(&b_len) {
+            Equal => {}
+            Greater => {
+                init =
+                    a.by_ref().rev().take(a_len - b_len).map(EitherOrBoth::Left).fold(init, &mut f)
+            }
+            Less => {
+                init =
+                    b.by_ref().rev().take(b_len - a_len).map(EitherOrBoth::Right).fold(init, &mut f)
+            }
+        }
+        a.rfold(init, |acc, item_a| f(acc, EitherOrBoth::Both(item_a, b.next_back().unwrap())))
+    }
+}
+
+impl<T, U> ExactSizeIterator for ZipLongest<T, U>
+where
+    T: ExactSizeIterator,
+    U: ExactSizeIterator,
+{
+}
+
+impl<T, U> FusedIterator for ZipLongest<T, U>
+where
+    T: Iterator,
+    U: Iterator,
+{
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/ziptuple.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/ziptuple.rs
similarity index 83%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/ziptuple.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/ziptuple.rs
index 6d3a584..fcd9ec1f 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/ziptuple.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/ziptuple.rs
@@ -7,21 +7,23 @@
     t: T,
 }
 
-/// An iterator that generalizes *.zip()* and allows running multiple iterators in lockstep.
+/// An iterator that generalizes `.zip()` and allows running multiple iterators
+/// in lockstep.
 ///
-/// The iterator `Zip<(I, J, ..., M)>` is formed from a tuple of iterators (or values that
-/// implement [`IntoIterator`]) and yields elements
+/// The iterator `Zip<(I, J, ..., M)>` is formed from a tuple of iterators (or
+/// values that implement [`IntoIterator`]) and yields elements
 /// until any of the subiterators yields `None`.
 ///
-/// The iterator element type is a tuple like like `(A, B, ..., E)` where `A` to `E` are the
-/// element types of the subiterator.
+/// The iterator element type is a tuple like like `(A, B, ..., E)` where `A` to
+/// `E` are the element types of the subiterator.
 ///
-/// **Note:** The result of this macro is a value of a named type (`Zip<(I, J,
-/// ..)>` of each component iterator `I, J, ...`) if each component iterator is
-/// nameable.
+/// **Note:** The result of this function is a value of a named type (`Zip<(I,
+/// J, ..)>` of each component iterator `I, J, ...`) if each component iterator
+/// is nameable.
 ///
-/// Prefer [`izip!()`] over `multizip` for the performance benefits of using the
-/// standard library `.zip()`. Prefer `multizip` if a nameable type is needed.
+/// Prefer [`izip!()`](crate::izip) over `multizip` for the performance benefits
+/// of using the standard library `.zip()`. Prefer `multizip` if a nameable type
+/// is needed.
 ///
 /// ```
 /// use itertools::multizip;
@@ -36,10 +38,9 @@
 ///
 /// assert_eq!(results, [0 + 3, 10 + 7, 29, 36]);
 /// ```
-/// [`izip!()`]: crate::izip
 pub fn multizip<T, U>(t: U) -> Zip<T>
-    where Zip<T>: From<U>,
-          Zip<T>: Iterator,
+where
+    Zip<T>: From<U> + Iterator,
 {
     Zip::from(t)
 }
@@ -82,7 +83,7 @@
 
             fn size_hint(&self) -> (usize, Option<usize>)
             {
-                let sh = (::std::usize::MAX, None);
+                let sh = (usize::MAX, None);
                 let ($(ref $B,)*) = self.t;
                 $(
                     let sh = size_hint::min($B.size_hint(), sh);
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/adaptors_no_collect.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/adaptors_no_collect.rs
similarity index 93%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/adaptors_no_collect.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/adaptors_no_collect.rs
index 103db23..68ee20e 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/adaptors_no_collect.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/adaptors_no_collect.rs
@@ -22,7 +22,9 @@
 }
 
 fn no_collect_test<A, T>(to_adaptor: T)
-    where A: Iterator, T: Fn(PanickingCounter) -> A
+where
+    A: Iterator,
+    T: Fn(PanickingCounter) -> A,
 {
     let counter = PanickingCounter { curr: 0, max: 10_000 };
     let adaptor = to_adaptor(counter);
@@ -43,4 +45,4 @@
 #[test]
 fn combinations_with_replacement_no_collect() {
     no_collect_test(|iter| iter.combinations_with_replacement(5))
-}
\ No newline at end of file
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/flatten_ok.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/flatten_ok.rs
new file mode 100644
index 0000000..12bab60
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/flatten_ok.rs
@@ -0,0 +1,49 @@
+use itertools::{assert_equal, Itertools};
+use std::{ops::Range, vec::IntoIter};
+
+fn mix_data() -> IntoIter<Result<Range<i32>, bool>> {
+    vec![Ok(0..2), Err(false), Ok(2..4), Err(true), Ok(4..6)].into_iter()
+}
+
+fn ok_data() -> IntoIter<Result<Range<i32>, bool>> {
+    vec![Ok(0..2), Ok(2..4), Ok(4..6)].into_iter()
+}
+
+#[test]
+fn flatten_ok_mixed_expected_forward() {
+    assert_equal(
+        mix_data().flatten_ok(),
+        vec![Ok(0), Ok(1), Err(false), Ok(2), Ok(3), Err(true), Ok(4), Ok(5)],
+    );
+}
+
+#[test]
+fn flatten_ok_mixed_expected_reverse() {
+    assert_equal(
+        mix_data().flatten_ok().rev(),
+        vec![Ok(5), Ok(4), Err(true), Ok(3), Ok(2), Err(false), Ok(1), Ok(0)],
+    );
+}
+
+#[test]
+fn flatten_ok_collect_mixed_forward() {
+    assert_eq!(mix_data().flatten_ok().collect::<Result<Vec<_>, _>>(), Err(false));
+}
+
+#[test]
+fn flatten_ok_collect_mixed_reverse() {
+    assert_eq!(mix_data().flatten_ok().rev().collect::<Result<Vec<_>, _>>(), Err(true));
+}
+
+#[test]
+fn flatten_ok_collect_ok_forward() {
+    assert_eq!(ok_data().flatten_ok().collect::<Result<Vec<_>, _>>(), Ok((0..6).collect()));
+}
+
+#[test]
+fn flatten_ok_collect_ok_reverse() {
+    assert_eq!(
+        ok_data().flatten_ok().rev().collect::<Result<Vec<_>, _>>(),
+        Ok((0..6).rev().collect())
+    );
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/laziness.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/laziness.rs
new file mode 100644
index 0000000..c559d33
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/laziness.rs
@@ -0,0 +1,283 @@
+#![allow(unstable_name_collisions)]
+
+use itertools::Itertools;
+
+#[derive(Debug, Clone)]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
+struct Panicking;
+
+impl Iterator for Panicking {
+    type Item = u8;
+
+    fn next(&mut self) -> Option<u8> {
+        panic!("iterator adaptor is not lazy")
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (0, Some(0))
+    }
+}
+
+impl ExactSizeIterator for Panicking {}
+
+/// ## Usage example
+/// ```compile_fail
+/// must_use_tests! {
+///     name {
+///         Panicking.name(); // Add `let _ =` only if required (encountered error).
+///     }
+///     // ...
+/// }
+/// ```
+///
+/// **TODO:** test missing `must_use` attributes better, maybe with a new lint.
+macro_rules! must_use_tests {
+    ($($(#[$attr:meta])* $name:ident $body:block)*) => {
+        $(
+            /// `#[deny(unused_must_use)]` should force us to ignore the resulting iterators
+            /// by adding `let _ = ...;` on every iterator.
+            /// If it does not, then a `must_use` attribute is missing on the associated struct.
+            ///
+            /// However, it's only helpful if we don't add `let _ =` before seeing if there is an error or not.
+            /// And it does not protect us against removed `must_use` attributes.
+            /// There is no simple way to test this yet.
+            #[deny(unused_must_use)]
+            #[test]
+            $(#[$attr])*
+            fn $name() $body
+        )*
+    };
+}
+
+must_use_tests! {
+    // Itertools trait:
+    interleave {
+        let _ = Panicking.interleave(Panicking);
+    }
+    interleave_shortest {
+        let _ = Panicking.interleave_shortest(Panicking);
+    }
+    intersperse {
+        let _ = Panicking.intersperse(0);
+    }
+    intersperse_with {
+        let _ = Panicking.intersperse_with(|| 0);
+    }
+    get {
+        let _ = Panicking.get(1..4);
+        let _ = Panicking.get(1..=4);
+        let _ = Panicking.get(1..);
+        let _ = Panicking.get(..4);
+        let _ = Panicking.get(..=4);
+        let _ = Panicking.get(..);
+    }
+    zip_longest {
+        let _ = Panicking.zip_longest(Panicking);
+    }
+    zip_eq {
+        let _ = Panicking.zip_eq(Panicking);
+    }
+    batching {
+        let _ = Panicking.batching(Iterator::next);
+    }
+    chunk_by {
+        // ChunkBy
+        let _ = Panicking.chunk_by(|x| *x);
+        // Groups
+        let _ = Panicking.chunk_by(|x| *x).into_iter();
+    }
+    chunks {
+        // IntoChunks
+        let _ = Panicking.chunks(1);
+        let _ = Panicking.chunks(2);
+        // Chunks
+        let _ = Panicking.chunks(1).into_iter();
+        let _ = Panicking.chunks(2).into_iter();
+    }
+    tuple_windows {
+        let _ = Panicking.tuple_windows::<(_,)>();
+        let _ = Panicking.tuple_windows::<(_, _)>();
+        let _ = Panicking.tuple_windows::<(_, _, _)>();
+    }
+    circular_tuple_windows {
+        let _ = Panicking.circular_tuple_windows::<(_,)>();
+        let _ = Panicking.circular_tuple_windows::<(_, _)>();
+        let _ = Panicking.circular_tuple_windows::<(_, _, _)>();
+    }
+    tuples {
+        let _ = Panicking.tuples::<(_,)>();
+        let _ = Panicking.tuples::<(_, _)>();
+        let _ = Panicking.tuples::<(_, _, _)>();
+    }
+    tee {
+        let _ = Panicking.tee();
+    }
+    map_into {
+        let _ = Panicking.map_into::<u16>();
+    }
+    map_ok {
+        let _ = Panicking.map(Ok::<u8, ()>).map_ok(|x| x + 1);
+    }
+    filter_ok {
+        let _ = Panicking.map(Ok::<u8, ()>).filter_ok(|x| x % 2 == 0);
+    }
+    filter_map_ok {
+        let _ = Panicking.map(Ok::<u8, ()>).filter_map_ok(|x| {
+            if x % 2 == 0 {
+                Some(x + 1)
+            } else {
+                None
+            }
+        });
+    }
+    flatten_ok {
+        let _ = Panicking.map(|x| Ok::<_, ()>([x])).flatten_ok();
+    }
+    merge {
+        let _ = Panicking.merge(Panicking);
+    }
+    merge_by {
+        let _ = Panicking.merge_by(Panicking, |_, _| true);
+    }
+    merge_join_by {
+        let _ = Panicking.merge_join_by(Panicking, |_, _| true);
+        let _ = Panicking.merge_join_by(Panicking, Ord::cmp);
+    }
+    #[should_panic]
+    kmerge {
+        let _ = Panicking.map(|_| Panicking).kmerge();
+    }
+    #[should_panic]
+    kmerge_by {
+        let _ = Panicking.map(|_| Panicking).kmerge_by(|_, _| true);
+    }
+    cartesian_product {
+        let _ = Panicking.cartesian_product(Panicking);
+    }
+    multi_cartesian_product {
+        let _ = vec![Panicking, Panicking, Panicking].into_iter().multi_cartesian_product();
+    }
+    coalesce {
+        let _ = Panicking.coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) });
+    }
+    dedup {
+        let _ = Panicking.dedup();
+    }
+    dedup_by {
+        let _ = Panicking.dedup_by(|_, _| true);
+    }
+    dedup_with_count {
+        let _ = Panicking.dedup_with_count();
+    }
+    dedup_by_with_count {
+        let _ = Panicking.dedup_by_with_count(|_, _| true);
+    }
+    duplicates {
+        let _ = Panicking.duplicates();
+    }
+    duplicates_by {
+        let _ = Panicking.duplicates_by(|x| *x);
+    }
+    unique {
+        let _ = Panicking.unique();
+    }
+    unique_by {
+        let _ = Panicking.unique_by(|x| *x);
+    }
+    peeking_take_while {
+        let _ = Panicking.peekable().peeking_take_while(|x| x % 2 == 0);
+    }
+    take_while_ref {
+        let _ = Panicking.take_while_ref(|x| x % 2 == 0);
+    }
+    take_while_inclusive {
+        let _ = Panicking.take_while_inclusive(|x| x % 2 == 0);
+    }
+    while_some {
+        let _ = Panicking.map(Some).while_some();
+    }
+    tuple_combinations1 {
+        let _ = Panicking.tuple_combinations::<(_,)>();
+    }
+    #[should_panic]
+    tuple_combinations2 {
+        let _ = Panicking.tuple_combinations::<(_, _)>();
+    }
+    #[should_panic]
+    tuple_combinations3 {
+        let _ = Panicking.tuple_combinations::<(_, _, _)>();
+    }
+    combinations {
+        let _ = Panicking.combinations(0);
+        let _ = Panicking.combinations(1);
+        let _ = Panicking.combinations(2);
+    }
+    combinations_with_replacement {
+        let _ = Panicking.combinations_with_replacement(0);
+        let _ = Panicking.combinations_with_replacement(1);
+        let _ = Panicking.combinations_with_replacement(2);
+    }
+    permutations {
+        let _ = Panicking.permutations(0);
+        let _ = Panicking.permutations(1);
+        let _ = Panicking.permutations(2);
+    }
+    powerset {
+        let _ = Panicking.powerset();
+    }
+    pad_using {
+        let _ = Panicking.pad_using(25, |_| 10);
+    }
+    with_position {
+        let _ = Panicking.with_position();
+    }
+    positions {
+        let _ = Panicking.positions(|v| v % 2 == 0);
+    }
+    update {
+        let _ = Panicking.update(|n| *n += 1);
+    }
+    multipeek {
+        let _ = Panicking.multipeek();
+    }
+    // Not iterator themselves but still lazy.
+    into_grouping_map {
+        let _ = Panicking.map(|x| (x, x + 1)).into_grouping_map();
+    }
+    into_grouping_map_by {
+        let _ = Panicking.into_grouping_map_by(|x| *x);
+    }
+    // Macros:
+    iproduct {
+        let _ = itertools::iproduct!(Panicking);
+        let _ = itertools::iproduct!(Panicking, Panicking);
+        let _ = itertools::iproduct!(Panicking, Panicking, Panicking);
+    }
+    izip {
+        let _ = itertools::izip!(Panicking);
+        let _ = itertools::izip!(Panicking, Panicking);
+        let _ = itertools::izip!(Panicking, Panicking, Panicking);
+    }
+    chain {
+        let _ = itertools::chain!(Panicking);
+        let _ = itertools::chain!(Panicking, Panicking);
+        let _ = itertools::chain!(Panicking, Panicking, Panicking);
+    }
+    // Free functions:
+    multizip {
+        let _ = itertools::multizip((Panicking, Panicking));
+    }
+    put_back {
+        let _ = itertools::put_back(Panicking);
+        let _ = itertools::put_back(Panicking).with_value(15);
+    }
+    peek_nth {
+        let _ = itertools::peek_nth(Panicking);
+    }
+    put_back_n {
+        let _ = itertools::put_back_n(Panicking);
+    }
+    rciter {
+        let _ = itertools::rciter(Panicking);
+    }
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/macros_hygiene.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/macros_hygiene.rs
new file mode 100644
index 0000000..e6e89555
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/macros_hygiene.rs
@@ -0,0 +1,27 @@
+mod alloc {}
+mod core {}
+mod either {}
+mod std {}
+
+#[test]
+fn iproduct_hygiene() {
+    let _ = itertools::iproduct!();
+    let _ = itertools::iproduct!(0..6);
+    let _ = itertools::iproduct!(0..6, 0..9);
+    let _ = itertools::iproduct!(0..6, 0..9, 0..12);
+}
+
+#[test]
+fn izip_hygiene() {
+    let _ = itertools::izip!(0..6);
+    let _ = itertools::izip!(0..6, 0..9);
+    let _ = itertools::izip!(0..6, 0..9, 0..12);
+}
+
+#[test]
+fn chain_hygiene() {
+    let _: ::std::iter::Empty<i32> = itertools::chain!();
+    let _ = itertools::chain!(0..6);
+    let _ = itertools::chain!(0..6, 0..9);
+    let _ = itertools::chain!(0..6, 0..9, 0..12);
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/merge_join.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/merge_join.rs
new file mode 100644
index 0000000..2fa0975
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/merge_join.rs
@@ -0,0 +1,95 @@
+use itertools::free::merge_join_by;
+use itertools::EitherOrBoth;
+
+#[test]
+fn empty() {
+    let left: Vec<u32> = vec![];
+    let right: Vec<u32> = vec![];
+    let expected_result: Vec<EitherOrBoth<u32>> = vec![];
+    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
+    assert_eq!(expected_result, actual_result);
+}
+
+#[test]
+fn left_only() {
+    let left: Vec<u32> = vec![1, 2, 3];
+    let right: Vec<u32> = vec![];
+    let expected_result: Vec<EitherOrBoth<u32>> =
+        vec![EitherOrBoth::Left(1), EitherOrBoth::Left(2), EitherOrBoth::Left(3)];
+    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
+    assert_eq!(expected_result, actual_result);
+}
+
+#[test]
+fn right_only() {
+    let left: Vec<u32> = vec![];
+    let right: Vec<u32> = vec![1, 2, 3];
+    let expected_result: Vec<EitherOrBoth<u32>> =
+        vec![EitherOrBoth::Right(1), EitherOrBoth::Right(2), EitherOrBoth::Right(3)];
+    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
+    assert_eq!(expected_result, actual_result);
+}
+
+#[test]
+fn first_left_then_right() {
+    let left: Vec<u32> = vec![1, 2, 3];
+    let right: Vec<u32> = vec![4, 5, 6];
+    let expected_result: Vec<EitherOrBoth<u32>> = vec![
+        EitherOrBoth::Left(1),
+        EitherOrBoth::Left(2),
+        EitherOrBoth::Left(3),
+        EitherOrBoth::Right(4),
+        EitherOrBoth::Right(5),
+        EitherOrBoth::Right(6),
+    ];
+    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
+    assert_eq!(expected_result, actual_result);
+}
+
+#[test]
+fn first_right_then_left() {
+    let left: Vec<u32> = vec![4, 5, 6];
+    let right: Vec<u32> = vec![1, 2, 3];
+    let expected_result: Vec<EitherOrBoth<u32>> = vec![
+        EitherOrBoth::Right(1),
+        EitherOrBoth::Right(2),
+        EitherOrBoth::Right(3),
+        EitherOrBoth::Left(4),
+        EitherOrBoth::Left(5),
+        EitherOrBoth::Left(6),
+    ];
+    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
+    assert_eq!(expected_result, actual_result);
+}
+
+#[test]
+fn interspersed_left_and_right() {
+    let left: Vec<u32> = vec![1, 3, 5];
+    let right: Vec<u32> = vec![2, 4, 6];
+    let expected_result: Vec<EitherOrBoth<u32>> = vec![
+        EitherOrBoth::Left(1),
+        EitherOrBoth::Right(2),
+        EitherOrBoth::Left(3),
+        EitherOrBoth::Right(4),
+        EitherOrBoth::Left(5),
+        EitherOrBoth::Right(6),
+    ];
+    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
+    assert_eq!(expected_result, actual_result);
+}
+
+#[test]
+fn overlapping_left_and_right() {
+    let left: Vec<u32> = vec![1, 3, 4, 6];
+    let right: Vec<u32> = vec![2, 3, 4, 5];
+    let expected_result: Vec<EitherOrBoth<u32>> = vec![
+        EitherOrBoth::Left(1),
+        EitherOrBoth::Right(2),
+        EitherOrBoth::Both(3, 3),
+        EitherOrBoth::Both(4, 4),
+        EitherOrBoth::Right(5),
+        EitherOrBoth::Left(6),
+    ];
+    let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)).collect::<Vec<_>>();
+    assert_eq!(expected_result, actual_result);
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/peeking_take_while.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/peeking_take_while.rs
similarity index 86%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/peeking_take_while.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/peeking_take_while.rs
index 5be97271..e9d6935 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/peeking_take_while.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/peeking_take_while.rs
@@ -52,18 +52,12 @@
 #[test]
 fn peeking_take_while_nested() {
     let mut xs = (0..10).peekable();
-    let ys: Vec<_> = xs
-        .peeking_take_while(|x| *x < 6)
-        .peeking_take_while(|x| *x != 3)
-        .collect();
+    let ys: Vec<_> = xs.peeking_take_while(|x| *x < 6).peeking_take_while(|x| *x != 3).collect();
     assert_eq!(ys, vec![0, 1, 2]);
     assert_eq!(xs.next(), Some(3));
 
     let mut xs = (4..10).peekable();
-    let ys: Vec<_> = xs
-        .peeking_take_while(|x| *x != 3)
-        .peeking_take_while(|x| *x < 6)
-        .collect();
+    let ys: Vec<_> = xs.peeking_take_while(|x| *x != 3).peeking_take_while(|x| *x < 6).collect();
     assert_eq!(ys, vec![4, 5]);
     assert_eq!(xs.next(), Some(6));
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/quick.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/quick.rs
similarity index 82%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/quick.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/quick.rs
index c19af6c1..0177f122 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/quick.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/quick.rs
@@ -2,35 +2,28 @@
 //! and adaptors.
 //!
 //! In particular we test the tedious size_hint and exact size correctness.
+//!
+//! **NOTE:** Due to performance limitations, these tests are not run with miri!
+//! They cannot be relied upon to discover soundness issues.
 
+#![cfg(not(miri))]
+#![allow(deprecated, unstable_name_collisions)]
+
+use itertools::free::{
+    cloned, enumerate, multipeek, peek_nth, put_back, put_back_n, rciter, zip, zip_eq,
+};
+use itertools::Itertools;
+use itertools::{iproduct, izip, multizip, EitherOrBoth};
 use quickcheck as qc;
+use std::cmp::{max, min, Ordering};
+use std::collections::{HashMap, HashSet};
 use std::default::Default;
 use std::num::Wrapping;
 use std::ops::Range;
-use std::cmp::{max, min, Ordering};
-use std::collections::{HashMap, HashSet};
-use itertools::Itertools;
-use itertools::{
-    multizip,
-    EitherOrBoth,
-    iproduct,
-    izip,
-};
-use itertools::free::{
-    cloned,
-    enumerate,
-    multipeek,
-    peek_nth,
-    put_back,
-    put_back_n,
-    rciter,
-    zip,
-    zip_eq,
-};
 
-use rand::Rng;
-use rand::seq::SliceRandom;
 use quickcheck::TestResult;
+use rand::seq::SliceRandom;
+use rand::Rng;
 
 /// Trait for size hint modifier types
 trait HintKind: Copy + Send + qc::Arbitrary {
@@ -49,7 +42,7 @@
 
 impl qc::Arbitrary for Exact {
     fn arbitrary<G: qc::Gen>(_: &mut G) -> Self {
-        Exact {}
+        Self {}
     }
 }
 
@@ -66,8 +59,10 @@
 impl HintKind for Inexact {
     fn loosen_bounds(&self, org_hint: (usize, Option<usize>)) -> (usize, Option<usize>) {
         let (org_lower, org_upper) = org_hint;
-        (org_lower.saturating_sub(self.underestimate),
-         org_upper.and_then(move |x| x.checked_add(self.overestimate)))
+        (
+            org_lower.saturating_sub(self.underestimate),
+            org_upper.and_then(move |x| x.checked_add(self.overestimate)),
+        )
     }
 }
 
@@ -76,27 +71,22 @@
         let ue_value = usize::arbitrary(g);
         let oe_value = usize::arbitrary(g);
         // Compensate for quickcheck using extreme values too rarely
-        let ue_choices = &[0, ue_value, usize::max_value()];
-        let oe_choices = &[0, oe_value, usize::max_value()];
-        Inexact {
+        let ue_choices = &[0, ue_value, usize::MAX];
+        let oe_choices = &[0, oe_value, usize::MAX];
+        Self {
             underestimate: *ue_choices.choose(g).unwrap(),
             overestimate: *oe_choices.choose(g).unwrap(),
         }
     }
 
-    fn shrink(&self) -> Box<dyn Iterator<Item=Self>> {
+    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
         let underestimate_value = self.underestimate;
         let overestimate_value = self.overestimate;
-        Box::new(
-            underestimate_value.shrink().flat_map(move |ue_value|
-                overestimate_value.shrink().map(move |oe_value|
-                    Inexact {
-                        underestimate: ue_value,
-                        overestimate: oe_value,
-                    }
-                )
-            )
-        )
+        Box::new(underestimate_value.shrink().flat_map(move |ue_value| {
+            overestimate_value
+                .shrink()
+                .map(move |oe_value| Self { underestimate: ue_value, overestimate: oe_value })
+        }))
     }
 }
 
@@ -116,75 +106,75 @@
     hint_kind: SK,
 }
 
-impl<T, HK> Iter<T, HK> where HK: HintKind
+impl<T, HK> Iter<T, HK>
+where
+    HK: HintKind,
 {
     fn new(it: Range<T>, hint_kind: HK) -> Self {
-        Iter {
-            iterator: it,
-            fuse_flag: 0,
-            hint_kind,
-        }
+        Self { iterator: it, fuse_flag: 0, hint_kind }
     }
 }
 
 impl<T, HK> Iterator for Iter<T, HK>
-    where Range<T>: Iterator,
-          <Range<T> as Iterator>::Item: Default,
-          HK: HintKind,
+where
+    Range<T>: Iterator,
+    <Range<T> as Iterator>::Item: Default,
+    HK: HintKind,
 {
     type Item = <Range<T> as Iterator>::Item;
 
-    fn next(&mut self) -> Option<Self::Item>
-    {
+    fn next(&mut self) -> Option<Self::Item> {
         let elt = self.iterator.next();
         if elt.is_none() {
             self.fuse_flag += 1;
             // check fuse flag
             if self.fuse_flag == 2 {
-                return Some(Default::default())
+                return Some(Default::default());
             }
         }
         elt
     }
 
-    fn size_hint(&self) -> (usize, Option<usize>)
-    {
+    fn size_hint(&self) -> (usize, Option<usize>) {
         let org_hint = self.iterator.size_hint();
         self.hint_kind.loosen_bounds(org_hint)
     }
 }
 
 impl<T, HK> DoubleEndedIterator for Iter<T, HK>
-    where Range<T>: DoubleEndedIterator,
-          <Range<T> as Iterator>::Item: Default,
-          HK: HintKind
+where
+    Range<T>: DoubleEndedIterator,
+    <Range<T> as Iterator>::Item: Default,
+    HK: HintKind,
 {
-    fn next_back(&mut self) -> Option<Self::Item> { self.iterator.next_back() }
+    fn next_back(&mut self) -> Option<Self::Item> {
+        self.iterator.next_back()
+    }
 }
 
-impl<T> ExactSizeIterator for Iter<T, Exact> where Range<T>: ExactSizeIterator,
+impl<T> ExactSizeIterator for Iter<T, Exact>
+where
+    Range<T>: ExactSizeIterator,
     <Range<T> as Iterator>::Item: Default,
-{ }
+{
+}
 
 impl<T, HK> qc::Arbitrary for Iter<T, HK>
-    where T: qc::Arbitrary,
-          HK: HintKind,
+where
+    T: qc::Arbitrary,
+    HK: HintKind,
 {
-    fn arbitrary<G: qc::Gen>(g: &mut G) -> Self
-    {
-        Iter::new(T::arbitrary(g)..T::arbitrary(g), HK::arbitrary(g))
+    fn arbitrary<G: qc::Gen>(g: &mut G) -> Self {
+        Self::new(T::arbitrary(g)..T::arbitrary(g), HK::arbitrary(g))
     }
 
-    fn shrink(&self) -> Box<dyn Iterator<Item=Iter<T, HK>>>
-    {
+    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
         let r = self.iterator.clone();
         let hint_kind = self.hint_kind;
         Box::new(
-            r.start.shrink().flat_map(move |a|
-                r.end.shrink().map(move |b|
-                    Iter::new(a.clone()..b, hint_kind)
-                )
-            )
+            r.start
+                .shrink()
+                .flat_map(move |a| r.end.shrink().map(move |b| Self::new(a.clone()..b, hint_kind))),
         )
     }
 }
@@ -201,7 +191,10 @@
     hint_kind: HK,
 }
 
-impl<HK> Iterator for ShiftRange<HK> where HK: HintKind {
+impl<HK> Iterator for ShiftRange<HK>
+where
+    HK: HintKind,
+{
     type Item = Iter<i32, HK>;
 
     fn next(&mut self) -> Option<Self::Item> {
@@ -219,10 +212,11 @@
     }
 }
 
-impl ExactSizeIterator for ShiftRange<Exact> { }
+impl ExactSizeIterator for ShiftRange<Exact> {}
 
 impl<HK> qc::Arbitrary for ShiftRange<HK>
-    where HK: HintKind
+where
+    HK: HintKind,
 {
     fn arbitrary<G: qc::Gen>(g: &mut G) -> Self {
         const MAX_STARTING_RANGE_DIFF: i32 = 32;
@@ -236,21 +230,14 @@
         let iter_count = g.gen_range(0, MAX_ITER_COUNT + 1);
         let hint_kind = qc::Arbitrary::arbitrary(g);
 
-        ShiftRange {
-            range_start,
-            range_end,
-            start_step,
-            end_step,
-            iter_count,
-            hint_kind,
-        }
+        Self { range_start, range_end, start_step, end_step, iter_count, hint_kind }
     }
 }
 
 fn correct_count<I, F>(get_it: F) -> bool
 where
     I: Iterator,
-    F: Fn() -> I
+    F: Fn() -> I,
 {
     let mut counts = vec![get_it().count()];
 
@@ -258,7 +245,6 @@
         let mut it = get_it();
 
         for _ in 0..(counts.len() - 1) {
-            #[allow(clippy::manual_assert)]
             if it.next().is_none() {
                 panic!("Iterator shouldn't be finished, may not be deterministic");
             }
@@ -276,7 +262,10 @@
     for (i, returned_count) in counts.into_iter().enumerate() {
         let actual_count = total_actual_count - i;
         if actual_count != returned_count {
-            println!("Total iterations: {} True count: {} returned count: {}", i, actual_count, returned_count);
+            println!(
+                "Total iterations: {} True count: {} returned count: {}",
+                i, actual_count, returned_count
+            );
 
             return false;
         }
@@ -299,12 +288,10 @@
     // check all the size hints
     for &(low, hi) in &hints {
         true_count -= 1;
-        if low > true_count ||
-            (hi.is_some() && hi.unwrap() < true_count)
-        {
+        if low > true_count || (hi.is_some() && hi.unwrap() < true_count) {
             println!("True size: {:?}, size hint: {:?}", true_count, (low, hi));
             //println!("All hints: {:?}", hints);
-            return false
+            return false;
         }
     }
     true
@@ -313,13 +300,19 @@
 fn exact_size<I: ExactSizeIterator>(mut it: I) -> bool {
     // check every iteration
     let (mut low, mut hi) = it.size_hint();
-    if Some(low) != hi { return false; }
+    if Some(low) != hi {
+        return false;
+    }
     while let Some(_) = it.next() {
         let (xlow, xhi) = it.size_hint();
-        if low != xlow + 1 { return false; }
+        if low != xlow + 1 {
+            return false;
+        }
         low = xlow;
         hi = xhi;
-        if Some(low) != hi { return false; }
+        if Some(low) != hi {
+            return false;
+        }
     }
     let (low, hi) = it.size_hint();
     low == 0 && hi == Some(0)
@@ -329,13 +322,19 @@
 fn exact_size_for_this<I: Iterator>(mut it: I) -> bool {
     // check every iteration
     let (mut low, mut hi) = it.size_hint();
-    if Some(low) != hi { return false; }
+    if Some(low) != hi {
+        return false;
+    }
     while let Some(_) = it.next() {
         let (xlow, xhi) = it.size_hint();
-        if low != xlow + 1 { return false; }
+        if low != xlow + 1 {
+            return false;
+        }
         low = xlow;
         hi = xhi;
-        if Some(low) != hi { return false; }
+        if Some(low) != hi {
+            return false;
+        }
     }
     let (low, hi) = it.size_hint();
     low == 0 && hi == Some(0)
@@ -442,43 +441,10 @@
         assert_eq!(answer.into_iter().last(), a.multi_cartesian_product().last());
     }
 
-    #[allow(deprecated)]
-    fn size_step(a: Iter<i16, Exact>, s: usize) -> bool {
-        let mut s = s;
-        if s == 0 {
-            s += 1; // never zero
-        }
-        let filt = a.clone().dedup();
-        correct_size_hint(filt.step(s)) &&
-            exact_size(a.step(s))
-    }
-
-    #[allow(deprecated)]
-    fn equal_step(a: Iter<i16>, s: usize) -> bool {
-        let mut s = s;
-        if s == 0 {
-            s += 1; // never zero
-        }
-        let mut i = 0;
-        itertools::equal(a.clone().step(s), a.filter(|_| {
-            let keep = i % s == 0;
-            i += 1;
-            keep
-        }))
-    }
-
-    #[allow(deprecated)]
-    fn equal_step_vec(a: Vec<i16>, s: usize) -> bool {
-        let mut s = s;
-        if s == 0 {
-            s += 1; // never zero
-        }
-        let mut i = 0;
-        itertools::equal(a.iter().step(s), a.iter().filter(|_| {
-            let keep = i % s == 0;
-            i += 1;
-            keep
-        }))
+    fn correct_empty_multi_product() -> () {
+        let empty = Vec::<std::vec::IntoIter<i32>>::new().into_iter().multi_cartesian_product();
+        assert!(correct_size_hint(empty.clone()));
+        itertools::assert_equal(empty, std::iter::once(Vec::new()))
     }
 
     fn size_multipeek(a: Iter<u16, Exact>, s: u8) -> bool {
@@ -596,6 +562,20 @@
         let b = &b[..len];
         itertools::equal(zip_eq(a, b), zip(a, b))
     }
+
+    #[should_panic]
+    fn zip_eq_panics(a: Vec<u8>, b: Vec<u8>) -> TestResult {
+        if a.len() == b.len() { return TestResult::discard(); }
+        zip_eq(a.iter(), b.iter()).for_each(|_| {});
+        TestResult::passed() // won't come here
+    }
+
+    fn equal_positions(a: Vec<i32>) -> bool {
+        let with_pos = a.iter().positions(|v| v % 2 == 0);
+        let without = a.iter().enumerate().filter(|(_, v)| *v % 2 == 0).map(|(i, _)| i);
+        itertools::equal(with_pos.clone(), without.clone())
+            && itertools::equal(with_pos.rev(), without.rev())
+    }
     fn size_zip_longest(a: Iter<i16, Exact>, b: Iter<i16, Exact>) -> bool {
         let filt = a.clone().dedup();
         let filt2 = b.clone().dedup();
@@ -751,6 +731,56 @@
 }
 
 quickcheck! {
+    fn correct_peek_nth(mut a: Vec<u16>) -> () {
+        let mut it = peek_nth(a.clone());
+        for start_pos in 0..a.len() + 2 {
+            for real_idx in start_pos..a.len() + 2 {
+                let peek_idx = real_idx - start_pos;
+                assert_eq!(it.peek_nth(peek_idx), a.get(real_idx));
+                assert_eq!(it.peek_nth_mut(peek_idx), a.get_mut(real_idx));
+            }
+            assert_eq!(it.next(), a.get(start_pos).copied());
+        }
+    }
+
+    fn peek_nth_mut_replace(a: Vec<u16>, b: Vec<u16>) -> () {
+        let mut it = peek_nth(a.iter());
+        for (i, m) in b.iter().enumerate().take(a.len().min(b.len())) {
+            *it.peek_nth_mut(i).unwrap() = m;
+        }
+        for (i, m) in a.iter().enumerate() {
+             assert_eq!(it.next().unwrap(), b.get(i).unwrap_or(m));
+        }
+        assert_eq!(it.next(), None);
+        assert_eq!(it.next(), None);
+    }
+
+    fn peek_nth_next_if(a: Vec<u8>) -> () {
+        let mut it = peek_nth(a.clone());
+        for (idx, mut value) in a.iter().copied().enumerate() {
+            let should_be_none = it.next_if(|x| x != &value);
+            assert_eq!(should_be_none, None);
+            if value % 5 == 0 {
+                // Sometimes, peek up to 3 further.
+                let n = value as usize % 3;
+                let nth = it.peek_nth(n);
+                assert_eq!(nth, a.get(idx + n));
+            } else if value % 5 == 1 {
+                // Sometimes, peek next element mutably.
+                if let Some(v) = it.peek_mut() {
+                    *v = v.wrapping_sub(1);
+                    let should_be_none = it.next_if_eq(&value);
+                    assert_eq!(should_be_none, None);
+                    value = value.wrapping_sub(1);
+                }
+            }
+            let eq = it.next_if_eq(&value);
+            assert_eq!(eq, Some(value));
+        }
+    }
+}
+
+quickcheck! {
     fn dedup_via_coalesce(a: Vec<i32>) -> bool {
         let mut b = a.clone();
         b.dedup();
@@ -811,9 +841,8 @@
 quickcheck! {
     fn size_put_back(a: Vec<u8>, x: Option<u8>) -> bool {
         let mut it = put_back(a.into_iter());
-        match x {
-            Some(t) => it.put_back(t),
-            None => {}
+        if let Some(t) = x {
+            it.put_back(t);
         }
         correct_size_hint(it)
     }
@@ -895,8 +924,31 @@
 }
 
 quickcheck! {
-    fn size_combinations(it: Iter<i16>) -> bool {
-        correct_size_hint(it.tuple_combinations::<(_, _)>())
+    fn size_combinations(a: Iter<i16>) -> bool {
+        let it = a.clone().tuple_combinations::<(_, _)>();
+        correct_size_hint(it.clone()) && it.count() == binomial(a.count(), 2)
+    }
+
+    fn exact_size_combinations_1(a: Vec<u8>) -> bool {
+        let it = a.iter().tuple_combinations::<(_,)>();
+        exact_size_for_this(it.clone()) && it.count() == binomial(a.len(), 1)
+    }
+    fn exact_size_combinations_2(a: Vec<u8>) -> bool {
+        let it = a.iter().tuple_combinations::<(_, _)>();
+        exact_size_for_this(it.clone()) && it.count() == binomial(a.len(), 2)
+    }
+    fn exact_size_combinations_3(mut a: Vec<u8>) -> bool {
+        a.truncate(15);
+        let it = a.iter().tuple_combinations::<(_, _, _)>();
+        exact_size_for_this(it.clone()) && it.count() == binomial(a.len(), 3)
+    }
+}
+
+fn binomial(n: usize, k: usize) -> usize {
+    if k > n {
+        0
+    } else {
+        (n - k + 1..=n).product::<usize>() / (1..=k).product::<usize>()
     }
 }
 
@@ -912,7 +964,7 @@
                 }
             }
         }
-        cmb.next() == None
+        cmb.next().is_none()
     }
 }
 
@@ -961,58 +1013,58 @@
 }
 
 quickcheck! {
-    fn fuzz_group_by_lazy_1(it: Iter<u8>) -> bool {
+    fn fuzz_chunk_by_lazy_1(it: Iter<u8>) -> bool {
         let jt = it.clone();
-        let groups = it.group_by(|k| *k);
-        itertools::equal(jt, groups.into_iter().flat_map(|(_, x)| x))
+        let chunks = it.chunk_by(|k| *k);
+        itertools::equal(jt, chunks.into_iter().flat_map(|(_, x)| x))
     }
 }
 
 quickcheck! {
-    fn fuzz_group_by_lazy_2(data: Vec<u8>) -> bool {
-        let groups = data.iter().group_by(|k| *k / 10);
-        let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x));
+    fn fuzz_chunk_by_lazy_2(data: Vec<u8>) -> bool {
+        let chunks = data.iter().chunk_by(|k| *k / 10);
+        let res = itertools::equal(data.iter(), chunks.into_iter().flat_map(|(_, x)| x));
         res
     }
 }
 
 quickcheck! {
-    fn fuzz_group_by_lazy_3(data: Vec<u8>) -> bool {
-        let grouper = data.iter().group_by(|k| *k / 10);
-        let groups = grouper.into_iter().collect_vec();
-        let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x));
+    fn fuzz_chunk_by_lazy_3(data: Vec<u8>) -> bool {
+        let grouper = data.iter().chunk_by(|k| *k / 10);
+        let chunks = grouper.into_iter().collect_vec();
+        let res = itertools::equal(data.iter(), chunks.into_iter().flat_map(|(_, x)| x));
         res
     }
 }
 
 quickcheck! {
-    fn fuzz_group_by_lazy_duo(data: Vec<u8>, order: Vec<(bool, bool)>) -> bool {
-        let grouper = data.iter().group_by(|k| *k / 3);
-        let mut groups1 = grouper.into_iter();
-        let mut groups2 = grouper.into_iter();
+    fn fuzz_chunk_by_lazy_duo(data: Vec<u8>, order: Vec<(bool, bool)>) -> bool {
+        let grouper = data.iter().chunk_by(|k| *k / 3);
+        let mut chunks1 = grouper.into_iter();
+        let mut chunks2 = grouper.into_iter();
         let mut elts = Vec::<&u8>::new();
-        let mut old_groups = Vec::new();
+        let mut old_chunks = Vec::new();
 
         let tup1 = |(_, b)| b;
         for &(ord, consume_now) in &order {
-            let iter = &mut [&mut groups1, &mut groups2][ord as usize];
+            let iter = &mut [&mut chunks1, &mut chunks2][ord as usize];
             match iter.next() {
                 Some((_, gr)) => if consume_now {
-                    for og in old_groups.drain(..) {
+                    for og in old_chunks.drain(..) {
                         elts.extend(og);
                     }
                     elts.extend(gr);
                 } else {
-                    old_groups.push(gr);
+                    old_chunks.push(gr);
                 },
                 None => break,
             }
         }
-        for og in old_groups.drain(..) {
+        for og in old_chunks.drain(..) {
             elts.extend(og);
         }
-        for gr in groups1.map(&tup1) { elts.extend(gr); }
-        for gr in groups2.map(&tup1) { elts.extend(gr); }
+        for gr in chunks1.map(&tup1) { elts.extend(gr); }
+        for gr in chunks2.map(&tup1) { elts.extend(gr); }
         itertools::assert_equal(&data, elts);
         true
     }
@@ -1111,6 +1163,10 @@
         true
     }
 
+    fn circular_tuple_windows_exact_size(a: Vec<u8>) -> bool {
+        exact_size(a.iter().circular_tuple_windows::<(_, _, _, _)>())
+    }
+
     fn equal_tuple_windows_1(a: Vec<u8>) -> bool {
         let x = a.windows(1).map(|s| (&s[0], ));
         let y = a.iter().tuple_windows::<(_,)>();
@@ -1135,6 +1191,14 @@
         itertools::equal(x, y)
     }
 
+    fn tuple_windows_exact_size_1(a: Vec<u8>) -> bool {
+        exact_size(a.iter().tuple_windows::<(_,)>())
+    }
+
+    fn tuple_windows_exact_size_4(a: Vec<u8>) -> bool {
+        exact_size(a.iter().tuple_windows::<(_, _, _, _)>())
+    }
+
     fn equal_tuples_1(a: Vec<u8>) -> bool {
         let x = a.chunks(1).map(|s| (&s[0], ));
         let y = a.iter().tuples::<(_,)>();
@@ -1166,6 +1230,18 @@
         assert_eq!(buffer.len(), a.len() % 4);
         exact_size(buffer)
     }
+
+    fn tuples_size_hint_inexact(a: Iter<u8>) -> bool {
+        correct_size_hint(a.clone().tuples::<(_,)>())
+            && correct_size_hint(a.clone().tuples::<(_, _)>())
+            && correct_size_hint(a.tuples::<(_, _, _, _)>())
+    }
+
+    fn tuples_size_hint_exact(a: Iter<u8, Exact>) -> bool {
+        exact_size(a.clone().tuples::<(_,)>())
+            && exact_size(a.clone().tuples::<(_, _)>())
+            && exact_size(a.tuples::<(_, _, _, _)>())
+    }
 }
 
 // with_position
@@ -1197,14 +1273,14 @@
 #[derive(Clone, Debug, PartialEq, Eq)]
 struct Val(u32, u32);
 
-impl PartialOrd<Val> for Val {
-    fn partial_cmp(&self, other: &Val) -> Option<Ordering> {
-        self.0.partial_cmp(&other.0)
+impl PartialOrd<Self> for Val {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
     }
 }
 
 impl Ord for Val {
-    fn cmp(&self, other: &Val) -> Ordering {
+    fn cmp(&self, other: &Self) -> Ordering {
         self.0.cmp(&other.0)
     }
 }
@@ -1212,10 +1288,10 @@
 impl qc::Arbitrary for Val {
     fn arbitrary<G: qc::Gen>(g: &mut G) -> Self {
         let (x, y) = <(u32, u32)>::arbitrary(g);
-        Val(x, y)
+        Self(x, y)
     }
     fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
-        Box::new((self.0, self.1).shrink().map(|(x, y)| Val(x, y)))
+        Box::new((self.0, self.1).shrink().map(|(x, y)| Self(x, y)))
     }
 }
 
@@ -1257,13 +1333,12 @@
 }
 
 quickcheck! {
-    #[allow(deprecated)]
-    fn tree_fold1_f64(mut a: Vec<f64>) -> TestResult {
+    fn tree_reduce_f64(mut a: Vec<f64>) -> TestResult {
         fn collapse_adjacent<F>(x: Vec<f64>, mut f: F) -> Vec<f64>
             where F: FnMut(f64, f64) -> f64
         {
             let mut out = Vec::new();
-            for i in (0..x.len()).step(2) {
+            for i in (0..x.len()).step_by(2) {
                 if i == x.len()-1 {
                     out.push(x[i])
                 } else {
@@ -1277,7 +1352,7 @@
             return TestResult::discard();
         }
 
-        let actual = a.iter().cloned().tree_fold1(f64::atan2);
+        let actual = a.iter().cloned().tree_reduce(f64::atan2);
 
         while a.len() > 1 {
             a = collapse_adjacent(a, f64::atan2);
@@ -1302,7 +1377,7 @@
     fn at_most_one_i32(a: Vec<i32>) -> TestResult {
         let ret = a.iter().cloned().at_most_one();
         match a.len() {
-            0 => TestResult::from_bool(ret.unwrap() == None),
+            0 => TestResult::from_bool(ret.unwrap().is_none()),
             1 => TestResult::from_bool(ret.unwrap() == Some(a[0])),
             _ => TestResult::from_bool(ret.unwrap_err().eq(a.iter().cloned())),
         }
@@ -1332,7 +1407,7 @@
                     Some(acc.unwrap_or(0) + val)
                 }
             });
-        
+
         let group_map_lookup = a.iter()
             .map(|&b| b as u64)
             .map(|i| (i % modulo, i))
@@ -1352,7 +1427,7 @@
 
         for m in 0..modulo {
             assert_eq!(
-                lookup.get(&m).copied(), 
+                lookup.get(&m).copied(),
                 a.iter()
                     .map(|&b| b as u64)
                     .filter(|&val| val % modulo == m)
@@ -1367,6 +1442,35 @@
         }
     }
 
+    fn correct_grouping_map_by_fold_with_modulo_key(a: Vec<u8>, modulo: u8) -> () {
+        #[derive(Debug, Default, PartialEq)]
+        struct Accumulator {
+            acc: u64,
+        }
+
+        let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0`
+        let lookup = a.iter().map(|&b| b as u64) // Avoid overflows
+            .into_grouping_map_by(|i| i % modulo)
+            .fold_with(|_key, _val| Default::default(), |Accumulator { acc }, &key, val| {
+                assert!(val % modulo == key);
+                let acc = acc + val;
+                Accumulator { acc }
+            });
+
+        let group_map_lookup = a.iter()
+            .map(|&b| b as u64)
+            .map(|i| (i % modulo, i))
+            .into_group_map()
+            .into_iter()
+            .map(|(key, vals)| (key, vals.into_iter().sum())).map(|(key, acc)| (key,Accumulator { acc }))
+            .collect::<HashMap<_,_>>();
+        assert_eq!(lookup, group_map_lookup);
+
+        for (&key, &Accumulator { acc: sum }) in lookup.iter() {
+            assert_eq!(sum, a.iter().map(|&b| b as u64).filter(|&val| val % modulo == key).sum::<u64>());
+        }
+    }
+
     fn correct_grouping_map_by_fold_modulo_key(a: Vec<u8>, modulo: u8) -> () {
         let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0`
         let lookup = a.iter().map(|&b| b as u64) // Avoid overflows
@@ -1390,22 +1494,21 @@
         }
     }
 
-    fn correct_grouping_map_by_fold_first_modulo_key(a: Vec<u8>, modulo: u8) -> () {
+    fn correct_grouping_map_by_reduce_modulo_key(a: Vec<u8>, modulo: u8) -> () {
         let modulo = if modulo == 0 { 1 } else { modulo } as u64; // Avoid `% 0`
         let lookup = a.iter().map(|&b| b as u64) // Avoid overflows
             .into_grouping_map_by(|i| i % modulo)
-            .fold_first(|acc, &key, val| {
+            .reduce(|acc, &key, val| {
                 assert!(val % modulo == key);
                 acc + val
             });
 
-        // TODO: Swap `fold1` with stdlib's `fold_first` when it's stabilized
         let group_map_lookup = a.iter()
             .map(|&b| b as u64)
             .map(|i| (i % modulo, i))
             .into_group_map()
             .into_iter()
-            .map(|(key, vals)| (key, vals.into_iter().fold1(|acc, val| acc + val).unwrap()))
+            .map(|(key, vals)| (key, vals.into_iter().reduce(|acc, val| acc + val).unwrap()))
             .collect::<HashMap<_,_>>();
         assert_eq!(lookup, group_map_lookup);
 
@@ -1472,7 +1575,7 @@
             assert_eq!(Some(max), a.iter().copied().filter(|&val| val % modulo == key).max_by_key(|&val| val));
         }
     }
-    
+
     fn correct_grouping_map_by_min_modulo_key(a: Vec<u8>, modulo: u8) -> () {
         let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0`
         let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).min();
@@ -1523,7 +1626,7 @@
             assert_eq!(Some(min), a.iter().copied().filter(|&val| val % modulo == key).min_by_key(|&val| val));
         }
     }
-    
+
     fn correct_grouping_map_by_minmax_modulo_key(a: Vec<u8>, modulo: u8) -> () {
         let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0`
         let lookup = a.iter().copied().into_grouping_map_by(|i| i % modulo).minmax();
@@ -1636,7 +1739,7 @@
             .min_by(|_, _, _| Ordering::Equal);
 
         assert_eq!(lookup[&0], 0);
-        
+
         let lookup = (0..=10)
             .into_grouping_map_by(|_| 0)
             .minmax_by(|_, _, _| Ordering::Equal);
@@ -1694,12 +1797,10 @@
     }
 }
 
-
-fn is_fused<I: Iterator>(mut it: I) -> bool
-{
+fn is_fused<I: Iterator>(mut it: I) -> bool {
     for _ in it.by_ref() {}
-    for _ in 0..10{
-        if it.next().is_some(){
+    for _ in 0..10 {
+        if it.next().is_some() {
             return false;
         }
     }
@@ -1740,7 +1841,7 @@
         !is_fused(a.clone().interleave_shortest(b.clone())) &&
         is_fused(a.fuse().interleave_shortest(b.fuse()))
     }
-    
+
     fn fused_product(a: Iter<i16>, b: Iter<i16>) -> bool
     {
         is_fused(a.fuse().cartesian_product(b.fuse()))
@@ -1846,4 +1947,11 @@
             result_set.is_empty()
         }
     }
+
+    fn tail(v: Vec<i32>, n: u8) -> bool {
+        let n = n as usize;
+        let result = &v[v.len().saturating_sub(n)..];
+        itertools::equal(v.iter().tail(n), result)
+            && itertools::equal(v.iter().filter(|_| true).tail(n), result)
+    }
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/specializations.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/specializations.rs
new file mode 100644
index 0000000..6408985d
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/specializations.rs
@@ -0,0 +1,605 @@
+//! Test specializations of methods with default impls match the behavior of the
+//! default impls.
+//!
+//! **NOTE:** Due to performance limitations, these tests are not run with miri!
+//! They cannot be relied upon to discover soundness issues.
+
+#![cfg(not(miri))]
+#![allow(unstable_name_collisions)]
+
+use itertools::Itertools;
+use quickcheck::Arbitrary;
+use quickcheck::{quickcheck, TestResult};
+use rand::Rng;
+use std::fmt::Debug;
+
+struct Unspecialized<I>(I);
+
+impl<I> Iterator for Unspecialized<I>
+where
+    I: Iterator,
+{
+    type Item = I::Item;
+
+    #[inline(always)]
+    fn next(&mut self) -> Option<Self::Item> {
+        self.0.next()
+    }
+}
+
+impl<I> DoubleEndedIterator for Unspecialized<I>
+where
+    I: DoubleEndedIterator,
+{
+    #[inline(always)]
+    fn next_back(&mut self) -> Option<Self::Item> {
+        self.0.next_back()
+    }
+}
+
+fn test_specializations<I>(it: &I)
+where
+    I::Item: Eq + Debug + Clone,
+    I: Iterator + Clone,
+{
+    macro_rules! check_specialized {
+        ($src:expr, |$it:pat| $closure:expr) => {
+            // Many iterators special-case the first elements, so we test specializations
+            // for iterators that have already been advanced.
+            let mut src = $src.clone();
+            for _ in 0..5 {
+                let $it = src.clone();
+                let v1 = $closure;
+                let $it = Unspecialized(src.clone());
+                let v2 = $closure;
+                assert_eq!(v1, v2);
+                src.next();
+            }
+        };
+    }
+    check_specialized!(it, |i| i.count());
+    check_specialized!(it, |i| i.last());
+    check_specialized!(it, |i| i.collect::<Vec<_>>());
+    check_specialized!(it, |i| {
+        let mut parameters_from_fold = vec![];
+        let fold_result = i.fold(vec![], |mut acc, v: I::Item| {
+            parameters_from_fold.push((acc.clone(), v.clone()));
+            acc.push(v);
+            acc
+        });
+        (parameters_from_fold, fold_result)
+    });
+    check_specialized!(it, |mut i| {
+        let mut parameters_from_all = vec![];
+        let first = i.next();
+        let all_result = i.all(|x| {
+            parameters_from_all.push(x.clone());
+            Some(x) == first
+        });
+        (parameters_from_all, all_result)
+    });
+    let size = it.clone().count();
+    for n in 0..size + 2 {
+        check_specialized!(it, |mut i| i.nth(n));
+    }
+    // size_hint is a bit harder to check
+    let mut it_sh = it.clone();
+    for n in 0..size + 2 {
+        let len = it_sh.clone().count();
+        let (min, max) = it_sh.size_hint();
+        assert_eq!(size - n.min(size), len);
+        assert!(min <= len);
+        if let Some(max) = max {
+            assert!(len <= max);
+        }
+        it_sh.next();
+    }
+}
+
+fn test_double_ended_specializations<I>(it: &I)
+where
+    I::Item: Eq + Debug + Clone,
+    I: DoubleEndedIterator + Clone,
+{
+    macro_rules! check_specialized {
+        ($src:expr, |$it:pat| $closure:expr) => {
+            // Many iterators special-case the first elements, so we test specializations
+            // for iterators that have already been advanced.
+            let mut src = $src.clone();
+            for step in 0..8 {
+                let $it = src.clone();
+                let v1 = $closure;
+                let $it = Unspecialized(src.clone());
+                let v2 = $closure;
+                assert_eq!(v1, v2);
+                if step % 2 == 0 {
+                    src.next();
+                } else {
+                    src.next_back();
+                }
+            }
+        };
+    }
+    check_specialized!(it, |i| {
+        let mut parameters_from_rfold = vec![];
+        let rfold_result = i.rfold(vec![], |mut acc, v: I::Item| {
+            parameters_from_rfold.push((acc.clone(), v.clone()));
+            acc.push(v);
+            acc
+        });
+        (parameters_from_rfold, rfold_result)
+    });
+    let size = it.clone().count();
+    for n in 0..size + 2 {
+        check_specialized!(it, |mut i| i.nth_back(n));
+    }
+}
+
+quickcheck! {
+    fn interleave(v: Vec<u8>, w: Vec<u8>) -> () {
+        test_specializations(&v.iter().interleave(w.iter()));
+    }
+
+    fn interleave_shortest(v: Vec<u8>, w: Vec<u8>) -> () {
+        test_specializations(&v.iter().interleave_shortest(w.iter()));
+    }
+
+    fn batching(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().batching(Iterator::next));
+    }
+
+    fn tuple_windows(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().tuple_windows::<(_,)>());
+        test_specializations(&v.iter().tuple_windows::<(_, _)>());
+        test_specializations(&v.iter().tuple_windows::<(_, _, _)>());
+    }
+
+    fn circular_tuple_windows(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().circular_tuple_windows::<(_,)>());
+        test_specializations(&v.iter().circular_tuple_windows::<(_, _)>());
+        test_specializations(&v.iter().circular_tuple_windows::<(_, _, _)>());
+    }
+
+    fn tuples(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().tuples::<(_,)>());
+        test_specializations(&v.iter().tuples::<(_, _)>());
+        test_specializations(&v.iter().tuples::<(_, _, _)>());
+    }
+
+    fn cartesian_product(a: Vec<u8>, b: Vec<u8>) -> TestResult {
+        if a.len() * b.len() > 100 {
+            return TestResult::discard();
+        }
+        test_specializations(&a.iter().cartesian_product(&b));
+        TestResult::passed()
+    }
+
+    fn multi_cartesian_product(a: Vec<u8>, b: Vec<u8>, c: Vec<u8>) -> TestResult {
+        if a.len() * b.len() * c.len() > 100 {
+            return TestResult::discard();
+        }
+        test_specializations(&vec![a, b, c].into_iter().multi_cartesian_product());
+        TestResult::passed()
+    }
+
+    fn coalesce(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().coalesce(|x, y| if x == y { Ok(x) } else { Err((x, y)) }))
+    }
+
+    fn dedup(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().dedup())
+    }
+
+    fn dedup_by(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().dedup_by(PartialOrd::ge))
+    }
+
+    fn dedup_with_count(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().dedup_with_count())
+    }
+
+    fn dedup_by_with_count(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().dedup_by_with_count(PartialOrd::ge))
+    }
+
+    fn duplicates(v: Vec<u8>) -> () {
+        let it = v.iter().duplicates();
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn duplicates_by(v: Vec<u8>) -> () {
+        let it = v.iter().duplicates_by(|x| *x % 10);
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn unique(v: Vec<u8>) -> () {
+        let it = v.iter().unique();
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn unique_by(v: Vec<u8>) -> () {
+        let it = v.iter().unique_by(|x| *x % 50);
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn take_while_inclusive(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().copied().take_while_inclusive(|&x| x < 100));
+    }
+
+    fn while_some(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().map(|&x| if x < 100 { Some(2 * x) } else { None }).while_some());
+    }
+
+    fn pad_using(v: Vec<u8>) -> () {
+        use std::convert::TryFrom;
+        let it = v.iter().copied().pad_using(10, |i| u8::try_from(5 * i).unwrap_or(u8::MAX));
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn with_position(v: Vec<u8>) -> () {
+        test_specializations(&v.iter().with_position());
+    }
+
+    fn positions(v: Vec<u8>) -> () {
+        let it = v.iter().positions(|x| x % 5 == 0);
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn update(v: Vec<u8>) -> () {
+        let it = v.iter().copied().update(|x| *x = x.wrapping_mul(7));
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn tuple_combinations(v: Vec<u8>) -> TestResult {
+        if v.len() > 10 {
+            return TestResult::discard();
+        }
+        test_specializations(&v.iter().tuple_combinations::<(_,)>());
+        test_specializations(&v.iter().tuple_combinations::<(_, _)>());
+        test_specializations(&v.iter().tuple_combinations::<(_, _, _)>());
+        TestResult::passed()
+    }
+
+    fn intersperse(v: Vec<u8>) -> () {
+        test_specializations(&v.into_iter().intersperse(0));
+    }
+
+    fn intersperse_with(v: Vec<u8>) -> () {
+        test_specializations(&v.into_iter().intersperse_with(|| 0));
+    }
+
+    fn array_combinations(v: Vec<u8>) -> TestResult {
+        if v.len() > 10 {
+            return TestResult::discard();
+        }
+        test_specializations(&v.iter().array_combinations::<1>());
+        test_specializations(&v.iter().array_combinations::<2>());
+        test_specializations(&v.iter().array_combinations::<3>());
+        TestResult::passed()
+    }
+
+    fn combinations(a: Vec<u8>, n: u8) -> TestResult {
+        if n > 3 || a.len() > 8 {
+            return TestResult::discard();
+        }
+        test_specializations(&a.iter().combinations(n as usize));
+        TestResult::passed()
+    }
+
+    fn combinations_with_replacement(a: Vec<u8>, n: u8) -> TestResult {
+        if n > 3 || a.len() > 7 {
+            return TestResult::discard();
+        }
+        test_specializations(&a.iter().combinations_with_replacement(n as usize));
+        TestResult::passed()
+    }
+
+    fn permutations(a: Vec<u8>, n: u8) -> TestResult {
+        if n > 3 || a.len() > 8 {
+            return TestResult::discard();
+        }
+        test_specializations(&a.iter().permutations(n as usize));
+        TestResult::passed()
+    }
+
+    fn powerset(a: Vec<u8>) -> TestResult {
+        if a.len() > 6 {
+            return TestResult::discard();
+        }
+        test_specializations(&a.iter().powerset());
+        TestResult::passed()
+    }
+
+    fn zip_longest(a: Vec<u8>, b: Vec<u8>) -> () {
+        let it = a.into_iter().zip_longest(b);
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn zip_eq(a: Vec<u8>) -> () {
+        test_specializations(&a.iter().zip_eq(a.iter().rev()))
+    }
+
+    fn multizip(a: Vec<u8>) -> () {
+        let it = itertools::multizip((a.iter(), a.iter().rev(), a.iter().take(50)));
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn izip(a: Vec<u8>, b: Vec<u8>) -> () {
+        test_specializations(&itertools::izip!(b.iter(), a, b.iter().rev()));
+    }
+
+    fn iproduct(a: Vec<u8>, b: Vec<u8>, c: Vec<u8>) -> TestResult {
+        if a.len() * b.len() * c.len() > 200 {
+            return TestResult::discard();
+        }
+        test_specializations(&itertools::iproduct!(a, b.iter(), c));
+        TestResult::passed()
+    }
+
+    fn repeat_n(element: i8, n: u8) -> () {
+        let it = itertools::repeat_n(element, n as usize);
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn exactly_one_error(v: Vec<u8>) -> TestResult {
+        // Use `at_most_one` would be similar.
+        match v.iter().exactly_one() {
+            Ok(_) => TestResult::discard(),
+            Err(it) => {
+                test_specializations(&it);
+                TestResult::passed()
+            }
+        }
+    }
+}
+
+quickcheck! {
+    fn put_back_qc(test_vec: Vec<i32>) -> () {
+        test_specializations(&itertools::put_back(test_vec.iter()));
+        let mut pb = itertools::put_back(test_vec.into_iter());
+        pb.put_back(1);
+        test_specializations(&pb);
+    }
+
+    fn put_back_n(v: Vec<u8>, n: u8) -> () {
+        let mut it = itertools::put_back_n(v);
+        for k in 0..n {
+            it.put_back(k);
+        }
+        test_specializations(&it);
+    }
+
+    fn multipeek(v: Vec<u8>, n: u8) -> () {
+        let mut it = v.into_iter().multipeek();
+        for _ in 0..n {
+            it.peek();
+        }
+        test_specializations(&it);
+    }
+
+    fn peek_nth_with_peek(v: Vec<u8>, n: u8) -> () {
+        let mut it = itertools::peek_nth(v);
+        for _ in 0..n {
+            it.peek();
+        }
+        test_specializations(&it);
+    }
+
+    fn peek_nth_with_peek_nth(v: Vec<u8>, n: u8) -> () {
+        let mut it = itertools::peek_nth(v);
+        it.peek_nth(n as usize);
+        test_specializations(&it);
+    }
+
+    fn peek_nth_with_peek_mut(v: Vec<u8>, n: u8) -> () {
+        let mut it = itertools::peek_nth(v);
+        for _ in 0..n {
+            if let Some(x) = it.peek_mut() {
+                *x = x.wrapping_add(50);
+            }
+        }
+        test_specializations(&it);
+    }
+
+    fn peek_nth_with_peek_nth_mut(v: Vec<u8>, n: u8) -> () {
+        let mut it = itertools::peek_nth(v);
+        if let Some(x) = it.peek_nth_mut(n as usize) {
+            *x = x.wrapping_add(50);
+        }
+        test_specializations(&it);
+    }
+}
+
+quickcheck! {
+    fn merge(a: Vec<u8>, b: Vec<u8>) -> () {
+        test_specializations(&a.into_iter().merge(b))
+    }
+
+    fn merge_by(a: Vec<u8>, b: Vec<u8>) -> () {
+        test_specializations(&a.into_iter().merge_by(b, PartialOrd::ge))
+    }
+
+    fn merge_join_by_ordering(i1: Vec<u8>, i2: Vec<u8>) -> () {
+        test_specializations(&i1.into_iter().merge_join_by(i2, Ord::cmp));
+    }
+
+    fn merge_join_by_bool(i1: Vec<u8>, i2: Vec<u8>) -> () {
+        test_specializations(&i1.into_iter().merge_join_by(i2, PartialOrd::ge));
+    }
+
+    fn kmerge(a: Vec<i8>, b: Vec<i8>, c: Vec<i8>) -> () {
+        test_specializations(&vec![a, b, c]
+            .into_iter()
+            .map(|v| v.into_iter().sorted())
+            .kmerge());
+    }
+
+    fn kmerge_by(a: Vec<i8>, b: Vec<i8>, c: Vec<i8>) -> () {
+        test_specializations(&vec![a, b, c]
+            .into_iter()
+            .map(|v| v.into_iter().sorted_by_key(|a| a.abs()))
+            .kmerge_by(|a, b| a.abs() < b.abs()));
+    }
+}
+
+quickcheck! {
+    fn map_into(v: Vec<u8>) -> () {
+        let it = v.into_iter().map_into::<u32>();
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn map_ok(v: Vec<Result<u8, char>>) -> () {
+        let it = v.into_iter().map_ok(|u| u.checked_add(1));
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn filter_ok(v: Vec<Result<u8, char>>) -> () {
+        let it = v.into_iter().filter_ok(|&i| i < 20);
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    fn filter_map_ok(v: Vec<Result<u8, char>>) -> () {
+        let it = v.into_iter().filter_map_ok(|i| if i < 20 { Some(i * 2) } else { None });
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+
+    // `SmallIter2<u8>` because `Vec<u8>` is too slow and we get bad coverage from a singleton like Option<u8>
+    fn flatten_ok(v: Vec<Result<SmallIter2<u8>, char>>) -> () {
+        let it = v.into_iter().flatten_ok();
+        test_specializations(&it);
+        test_double_ended_specializations(&it);
+    }
+}
+
+quickcheck! {
+    // TODO Replace this function by a normal call to test_specializations
+    fn process_results(v: Vec<Result<u8, u8>>) -> () {
+        helper(v.iter().copied());
+        helper(v.iter().copied().filter(Result::is_ok));
+
+        fn helper(it: impl DoubleEndedIterator<Item = Result<u8, u8>> + Clone) {
+            macro_rules! check_results_specialized {
+                ($src:expr, |$it:pat| $closure:expr) => {
+                    assert_eq!(
+                        itertools::process_results($src.clone(), |$it| $closure),
+                        itertools::process_results($src.clone(), |i| {
+                            let $it = Unspecialized(i);
+                            $closure
+                        }),
+                    )
+                }
+            }
+
+            check_results_specialized!(it, |i| i.count());
+            check_results_specialized!(it, |i| i.last());
+            check_results_specialized!(it, |i| i.collect::<Vec<_>>());
+            check_results_specialized!(it, |i| i.rev().collect::<Vec<_>>());
+            check_results_specialized!(it, |i| {
+                let mut parameters_from_fold = vec![];
+                let fold_result = i.fold(vec![], |mut acc, v| {
+                    parameters_from_fold.push((acc.clone(), v));
+                    acc.push(v);
+                    acc
+                });
+                (parameters_from_fold, fold_result)
+            });
+            check_results_specialized!(it, |i| {
+                let mut parameters_from_rfold = vec![];
+                let rfold_result = i.rfold(vec![], |mut acc, v| {
+                    parameters_from_rfold.push((acc.clone(), v));
+                    acc.push(v);
+                    acc
+                });
+                (parameters_from_rfold, rfold_result)
+            });
+            check_results_specialized!(it, |mut i| {
+                let mut parameters_from_all = vec![];
+                let first = i.next();
+                let all_result = i.all(|x| {
+                    parameters_from_all.push(x);
+                    Some(x)==first
+                });
+                (parameters_from_all, all_result)
+            });
+            let size = it.clone().count();
+            for n in 0..size + 2 {
+                check_results_specialized!(it, |mut i| i.nth(n));
+            }
+            for n in 0..size + 2 {
+                check_results_specialized!(it, |mut i| i.nth_back(n));
+            }
+        }
+    }
+}
+
+/// Like `VecIntoIter<T>` with maximum 2 elements.
+#[derive(Debug, Clone, Default)]
+enum SmallIter2<T> {
+    #[default]
+    Zero,
+    One(T),
+    Two(T, T),
+}
+
+impl<T: Arbitrary> Arbitrary for SmallIter2<T> {
+    fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> Self {
+        match g.gen_range(0u8, 3) {
+            0 => Self::Zero,
+            1 => Self::One(T::arbitrary(g)),
+            2 => Self::Two(T::arbitrary(g), T::arbitrary(g)),
+            _ => unreachable!(),
+        }
+    }
+    // maybe implement shrink too, maybe not
+}
+
+impl<T> Iterator for SmallIter2<T> {
+    type Item = T;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match std::mem::take(self) {
+            Self::Zero => None,
+            Self::One(val) => Some(val),
+            Self::Two(val, second) => {
+                *self = Self::One(second);
+                Some(val)
+            }
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let len = match self {
+            Self::Zero => 0,
+            Self::One(_) => 1,
+            Self::Two(_, _) => 2,
+        };
+        (len, Some(len))
+    }
+}
+
+impl<T> DoubleEndedIterator for SmallIter2<T> {
+    fn next_back(&mut self) -> Option<Self::Item> {
+        match std::mem::take(self) {
+            Self::Zero => None,
+            Self::One(val) => Some(val),
+            Self::Two(first, val) => {
+                *self = Self::One(first);
+                Some(val)
+            }
+        }
+    }
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/test_core.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/test_core.rs
similarity index 71%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/test_core.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/test_core.rs
index df94eb6..eabd662 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/test_core.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/test_core.rs
@@ -4,18 +4,71 @@
 //! option. This file may not be copied, modified, or distributed
 //! except according to those terms.
 #![no_std]
+#![allow(deprecated)]
 
-use core::iter;
-use itertools as it;
-use crate::it::Itertools;
+use crate::it::chain;
+use crate::it::free::put_back;
 use crate::it::interleave;
 use crate::it::intersperse;
 use crate::it::intersperse_with;
-use crate::it::multizip;
-use crate::it::free::put_back;
 use crate::it::iproduct;
 use crate::it::izip;
-use crate::it::chain;
+use crate::it::multizip;
+use crate::it::Itertools;
+use core::iter;
+use itertools as it;
+
+#[allow(dead_code)]
+fn get_esi_then_esi<I: ExactSizeIterator + Clone>(it: I) {
+    fn is_esi(_: impl ExactSizeIterator) {}
+    is_esi(it.clone().get(1..4));
+    is_esi(it.clone().get(1..=4));
+    is_esi(it.clone().get(1..));
+    is_esi(it.clone().get(..4));
+    is_esi(it.clone().get(..=4));
+    is_esi(it.get(..));
+}
+
+#[allow(dead_code)]
+fn get_dei_esi_then_dei_esi<I: DoubleEndedIterator + ExactSizeIterator + Clone>(it: I) {
+    fn is_dei_esi(_: impl DoubleEndedIterator + ExactSizeIterator) {}
+    is_dei_esi(it.clone().get(1..4));
+    is_dei_esi(it.clone().get(1..=4));
+    is_dei_esi(it.clone().get(1..));
+    is_dei_esi(it.clone().get(..4));
+    is_dei_esi(it.clone().get(..=4));
+    is_dei_esi(it.get(..));
+}
+
+#[test]
+fn get_1_max() {
+    let mut it = (0..5).get(1..=usize::MAX);
+    assert_eq!(it.next(), Some(1));
+    assert_eq!(it.next_back(), Some(4));
+}
+
+#[test]
+#[should_panic]
+fn get_full_range_inclusive() {
+    let _it = (0..5).get(0..=usize::MAX);
+}
+
+#[test]
+fn product0() {
+    let mut prod = iproduct!();
+    assert_eq!(prod.next(), Some(()));
+    assert!(prod.next().is_none());
+}
+
+#[test]
+fn iproduct1() {
+    let s = "αβ";
+
+    let mut prod = iproduct!(s.chars());
+    assert_eq!(prod.next(), Some(('α',)));
+    assert_eq!(prod.next(), Some(('β',)));
+    assert!(prod.next().is_none());
+}
 
 #[test]
 fn product2() {
@@ -26,21 +79,18 @@
     assert!(prod.next() == Some(('α', 1)));
     assert!(prod.next() == Some(('β', 0)));
     assert!(prod.next() == Some(('β', 1)));
-    assert!(prod.next() == None);
+    assert!(prod.next().is_none());
 }
 
 #[test]
 fn product_temporary() {
-    for (_x, _y, _z) in iproduct!(
-        [0, 1, 2].iter().cloned(),
-        [0, 1, 2].iter().cloned(),
-        [0, 1, 2].iter().cloned())
+    for (_x, _y, _z) in
+        iproduct!([0, 1, 2].iter().cloned(), [0, 1, 2].iter().cloned(), [0, 1, 2].iter().cloned())
     {
         // ok
     }
 }
 
-
 #[test]
 fn izip_macro() {
     let mut zip = izip!(2..3);
@@ -61,7 +111,7 @@
 #[test]
 fn izip2() {
     let _zip1: iter::Zip<_, _> = izip!(1.., 2..);
-    let _zip2: iter::Zip<_, _> = izip!(1.., 2.., );
+    let _zip2: iter::Zip<_, _> = izip!(1.., 2..,);
 }
 
 #[test]
@@ -109,7 +159,7 @@
 #[test]
 fn chain2() {
     let _ = chain!(1.., 2..);
-    let _ = chain!(1.., 2.., );
+    let _ = chain!(1.., 2..,);
 }
 
 #[test]
@@ -127,7 +177,7 @@
 
 #[test]
 fn test_interleave() {
-    let xs: [u8; 0]  = [];
+    let xs: [u8; 0] = [];
     let ys = [7u8, 9, 8, 10];
     let zs = [2u8, 77];
     let it = interleave(xs.iter(), ys.iter());
@@ -155,15 +205,6 @@
     it::assert_equal(it, ys.iter());
 }
 
-#[allow(deprecated)]
-#[test]
-fn foreach() {
-    let xs = [1i32, 2, 3];
-    let mut sum = 0;
-    xs.iter().foreach(|elt| sum += *elt);
-    assert!(sum == 6);
-}
-
 #[test]
 fn dropping() {
     let xs = [1, 2, 3];
@@ -180,10 +221,7 @@
     let ys = [(0, 1), (2, 1)];
 
     // An iterator that gathers elements up in pairs
-    let pit = xs
-        .iter()
-        .cloned()
-        .batching(|it| it.next().and_then(|x| it.next().map(|y| (x, y))));
+    let pit = xs.iter().cloned().batching(|it| it.next().and_then(|x| it.next().map(|y| (x, y))));
     it::assert_equal(pit, ys.iter().cloned());
 }
 
@@ -197,21 +235,11 @@
     it::assert_equal(pb, xs.iter().cloned());
 }
 
-#[allow(deprecated)]
-#[test]
-fn step() {
-    it::assert_equal((0..10).step(1), 0..10);
-    it::assert_equal((0..10).step(2), (0..10).filter(|x: &i32| *x % 2 == 0));
-    it::assert_equal((0..10).step(10), 0..1);
-}
-
-#[allow(deprecated)]
 #[test]
 fn merge() {
-    it::assert_equal((0..10).step(2).merge((1..10).step(2)), 0..10);
+    it::assert_equal((0..10).step_by(2).merge((1..10).step_by(2)), 0..10);
 }
 
-
 #[test]
 fn repeatn() {
     let s = "α";
@@ -231,29 +259,26 @@
     use core::cell::Cell;
     #[derive(PartialEq, Debug)]
     struct Foo {
-        n: Cell<usize>
+        n: Cell<usize>,
     }
 
-    impl Clone for Foo
-    {
-        fn clone(&self) -> Self
-        {
+    impl Clone for Foo {
+        fn clone(&self) -> Self {
             let n = self.n.get();
             self.n.set(n + 1);
-            Foo { n: Cell::new(n + 1) }
+            Self { n: Cell::new(n + 1) }
         }
     }
 
-
     for n in 0..10 {
-        let f = Foo{n: Cell::new(0)};
+        let f = Foo { n: Cell::new(0) };
         let it = it::repeat_n(f, n);
         // drain it
         let last = it.last();
         if n == 0 {
             assert_eq!(last, None);
         } else {
-            assert_eq!(last, Some(Foo{n: Cell::new(n - 1)}));
+            assert_eq!(last, Some(Foo { n: Cell::new(n - 1) }));
         }
     }
 }
@@ -276,9 +301,9 @@
 }
 
 #[test]
-fn tree_fold1() {
+fn tree_reduce() {
     for i in 0..100 {
-        assert_eq!((0..i).tree_fold1(|x, y| x + y), (0..i).fold1(|x, y| x + y));
+        assert_eq!((0..i).tree_reduce(|x, y| x + y), (0..i).fold1(|x, y| x + y));
     }
 }
 
@@ -315,3 +340,28 @@
     assert_eq!(v[1..3].iter().cloned().product1::<i32>(), Some(2));
     assert_eq!(v[1..5].iter().cloned().product1::<i32>(), Some(24));
 }
+
+#[test]
+fn next_array() {
+    let v = [1, 2, 3, 4, 5];
+    let mut iter = v.iter();
+    assert_eq!(iter.next_array(), Some([]));
+    assert_eq!(iter.next_array().map(|[&x, &y]| [x, y]), Some([1, 2]));
+    assert_eq!(iter.next_array().map(|[&x, &y]| [x, y]), Some([3, 4]));
+    assert_eq!(iter.next_array::<2>(), None);
+}
+
+#[test]
+fn collect_array() {
+    let v = [1, 2];
+    let iter = v.iter().cloned();
+    assert_eq!(iter.collect_array(), Some([1, 2]));
+
+    let v = [1];
+    let iter = v.iter().cloned();
+    assert_eq!(iter.collect_array::<2>(), None);
+
+    let v = [1, 2, 3];
+    let iter = v.iter().cloned();
+    assert_eq!(iter.collect_array::<2>(), None);
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/test_std.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/test_std.rs
new file mode 100644
index 0000000..ad391faad6
--- /dev/null
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/test_std.rs
@@ -0,0 +1,1569 @@
+#![allow(unstable_name_collisions)]
+
+use crate::it::cloned;
+use crate::it::free::put_back_n;
+use crate::it::free::rciter;
+use crate::it::iproduct;
+use crate::it::izip;
+use crate::it::multipeek;
+use crate::it::multizip;
+use crate::it::peek_nth;
+use crate::it::repeat_n;
+use crate::it::ExactlyOneError;
+use crate::it::FoldWhile;
+use crate::it::Itertools;
+use itertools as it;
+use quickcheck as qc;
+use rand::{
+    distributions::{Distribution, Standard},
+    rngs::StdRng,
+    Rng, SeedableRng,
+};
+use rand::{seq::SliceRandom, thread_rng};
+use std::{cmp::min, fmt::Debug, marker::PhantomData};
+
+#[test]
+fn product3() {
+    let prod = iproduct!(0..3, 0..2, 0..2);
+    assert_eq!(prod.size_hint(), (12, Some(12)));
+    let v = prod.collect_vec();
+    for i in 0..3 {
+        for j in 0..2 {
+            for k in 0..2 {
+                assert!((i, j, k) == v[(i * 2 * 2 + j * 2 + k) as usize]);
+            }
+        }
+    }
+    for (_, _, _, _) in iproduct!(0..3, 0..2, 0..2, 0..3) { /* test compiles */ }
+}
+
+#[test]
+fn interleave_shortest() {
+    let v0: Vec<i32> = vec![0, 2, 4];
+    let v1: Vec<i32> = vec![1, 3, 5, 7];
+    let it = v0.into_iter().interleave_shortest(v1);
+    assert_eq!(it.size_hint(), (6, Some(6)));
+    assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5]);
+
+    let v0: Vec<i32> = vec![0, 2, 4, 6, 8];
+    let v1: Vec<i32> = vec![1, 3, 5];
+    let it = v0.into_iter().interleave_shortest(v1);
+    assert_eq!(it.size_hint(), (7, Some(7)));
+    assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5, 6]);
+
+    let i0 = ::std::iter::repeat(0);
+    let v1: Vec<_> = vec![1, 3, 5];
+    let it = i0.interleave_shortest(v1);
+    assert_eq!(it.size_hint(), (7, Some(7)));
+
+    let v0: Vec<_> = vec![0, 2, 4];
+    let i1 = ::std::iter::repeat(1);
+    let it = v0.into_iter().interleave_shortest(i1);
+    assert_eq!(it.size_hint(), (6, Some(6)));
+}
+
+#[test]
+fn duplicates_by() {
+    let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"];
+    let ys = ["aa", "bbbb", "cccc"];
+    it::assert_equal(ys.iter(), xs.iter().duplicates_by(|x| x[..2].to_string()));
+    it::assert_equal(
+        ys.iter(),
+        xs.iter().rev().duplicates_by(|x| x[..2].to_string()).rev(),
+    );
+    let ys_rev = ["ccc", "aa", "bbbbb"];
+    it::assert_equal(
+        ys_rev.iter(),
+        xs.iter().duplicates_by(|x| x[..2].to_string()).rev(),
+    );
+}
+
+#[test]
+fn duplicates() {
+    let xs = [0, 1, 2, 3, 2, 1, 3];
+    let ys = [2, 1, 3];
+    it::assert_equal(ys.iter(), xs.iter().duplicates());
+    it::assert_equal(ys.iter(), xs.iter().rev().duplicates().rev());
+    let ys_rev = [3, 2, 1];
+    it::assert_equal(ys_rev.iter(), xs.iter().duplicates().rev());
+
+    let xs = [0, 1, 0, 1];
+    let ys = [0, 1];
+    it::assert_equal(ys.iter(), xs.iter().duplicates());
+    it::assert_equal(ys.iter(), xs.iter().rev().duplicates().rev());
+    let ys_rev = [1, 0];
+    it::assert_equal(ys_rev.iter(), xs.iter().duplicates().rev());
+
+    let xs = [0, 1, 2, 1, 2];
+    let ys = vec![1, 2];
+    assert_eq!(ys, xs.iter().duplicates().cloned().collect_vec());
+    assert_eq!(
+        ys,
+        xs.iter().rev().duplicates().rev().cloned().collect_vec()
+    );
+    let ys_rev = vec![2, 1];
+    assert_eq!(ys_rev, xs.iter().duplicates().rev().cloned().collect_vec());
+}
+
+#[test]
+fn unique_by() {
+    let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"];
+    let ys = ["aaa", "bbbbb", "ccc"];
+    it::assert_equal(ys.iter(), xs.iter().unique_by(|x| x[..2].to_string()));
+    it::assert_equal(
+        ys.iter(),
+        xs.iter().rev().unique_by(|x| x[..2].to_string()).rev(),
+    );
+    let ys_rev = ["cccc", "aaaaa", "bbbb"];
+    it::assert_equal(
+        ys_rev.iter(),
+        xs.iter().unique_by(|x| x[..2].to_string()).rev(),
+    );
+}
+
+#[test]
+fn unique() {
+    let xs = [0, 1, 2, 3, 2, 1, 3];
+    let ys = [0, 1, 2, 3];
+    it::assert_equal(ys.iter(), xs.iter().unique());
+    it::assert_equal(ys.iter(), xs.iter().rev().unique().rev());
+    let ys_rev = [3, 1, 2, 0];
+    it::assert_equal(ys_rev.iter(), xs.iter().unique().rev());
+
+    let xs = [0, 1];
+    let ys = [0, 1];
+    it::assert_equal(ys.iter(), xs.iter().unique());
+    it::assert_equal(ys.iter(), xs.iter().rev().unique().rev());
+    let ys_rev = [1, 0];
+    it::assert_equal(ys_rev.iter(), xs.iter().unique().rev());
+}
+
+#[test]
+fn intersperse() {
+    let xs = ["a", "", "b", "c"];
+    let v: Vec<&str> = xs.iter().cloned().intersperse(", ").collect();
+    let text: String = v.concat();
+    assert_eq!(text, "a, , b, c".to_string());
+
+    let ys = [0, 1, 2, 3];
+    let mut it = ys[..0].iter().copied().intersperse(1);
+    assert!(it.next().is_none());
+}
+
+#[test]
+fn dedup() {
+    let xs = [0, 1, 1, 1, 2, 1, 3, 3];
+    let ys = [0, 1, 2, 1, 3];
+    it::assert_equal(ys.iter(), xs.iter().dedup());
+    let xs = [0, 0, 0, 0, 0];
+    let ys = [0];
+    it::assert_equal(ys.iter(), xs.iter().dedup());
+
+    let xs = [0, 1, 1, 1, 2, 1, 3, 3];
+    let ys = [0, 1, 2, 1, 3];
+    let mut xs_d = Vec::new();
+    xs.iter().dedup().fold((), |(), &elt| xs_d.push(elt));
+    assert_eq!(&xs_d, &ys);
+}
+
+#[test]
+fn coalesce() {
+    let data = [-1., -2., -3., 3., 1., 0., -1.];
+    let it = data.iter().cloned().coalesce(|x, y| {
+        if (x >= 0.) == (y >= 0.) {
+            Ok(x + y)
+        } else {
+            Err((x, y))
+        }
+    });
+    itertools::assert_equal(it.clone(), vec![-6., 4., -1.]);
+    assert_eq!(
+        it.fold(vec![], |mut v, n| {
+            v.push(n);
+            v
+        }),
+        vec![-6., 4., -1.]
+    );
+}
+
+#[test]
+fn dedup_by() {
+    let xs = [
+        (0, 0),
+        (0, 1),
+        (1, 1),
+        (2, 1),
+        (0, 2),
+        (3, 1),
+        (0, 3),
+        (1, 3),
+    ];
+    let ys = [(0, 0), (0, 1), (0, 2), (3, 1), (0, 3)];
+    it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.1 == y.1));
+    let xs = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)];
+    let ys = [(0, 1)];
+    it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.0 == y.0));
+
+    let xs = [
+        (0, 0),
+        (0, 1),
+        (1, 1),
+        (2, 1),
+        (0, 2),
+        (3, 1),
+        (0, 3),
+        (1, 3),
+    ];
+    let ys = [(0, 0), (0, 1), (0, 2), (3, 1), (0, 3)];
+    let mut xs_d = Vec::new();
+    xs.iter()
+        .dedup_by(|x, y| x.1 == y.1)
+        .fold((), |(), &elt| xs_d.push(elt));
+    assert_eq!(&xs_d, &ys);
+}
+
+#[test]
+fn dedup_with_count() {
+    let xs: [i32; 8] = [0, 1, 1, 1, 2, 1, 3, 3];
+    let ys: [(usize, &i32); 5] = [(1, &0), (3, &1), (1, &2), (1, &1), (2, &3)];
+
+    it::assert_equal(ys.iter().cloned(), xs.iter().dedup_with_count());
+
+    let xs: [i32; 5] = [0, 0, 0, 0, 0];
+    let ys: [(usize, &i32); 1] = [(5, &0)];
+
+    it::assert_equal(ys.iter().cloned(), xs.iter().dedup_with_count());
+}
+
+#[test]
+fn dedup_by_with_count() {
+    let xs = [
+        (0, 0),
+        (0, 1),
+        (1, 1),
+        (2, 1),
+        (0, 2),
+        (3, 1),
+        (0, 3),
+        (1, 3),
+    ];
+    let ys = [
+        (1, &(0, 0)),
+        (3, &(0, 1)),
+        (1, &(0, 2)),
+        (1, &(3, 1)),
+        (2, &(0, 3)),
+    ];
+
+    it::assert_equal(
+        ys.iter().cloned(),
+        xs.iter().dedup_by_with_count(|x, y| x.1 == y.1),
+    );
+
+    let xs = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)];
+    let ys = [(5, &(0, 1))];
+
+    it::assert_equal(
+        ys.iter().cloned(),
+        xs.iter().dedup_by_with_count(|x, y| x.0 == y.0),
+    );
+}
+
+#[test]
+fn all_equal() {
+    assert!("".chars().all_equal());
+    assert!("A".chars().all_equal());
+    assert!(!"AABBCCC".chars().all_equal());
+    assert!("AAAAAAA".chars().all_equal());
+    for (_key, mut sub) in &"AABBCCC".chars().chunk_by(|&x| x) {
+        assert!(sub.all_equal());
+    }
+}
+
+#[test]
+fn all_equal_value() {
+    assert_eq!("".chars().all_equal_value(), Err(None));
+    assert_eq!("A".chars().all_equal_value(), Ok('A'));
+    assert_eq!("AABBCCC".chars().all_equal_value(), Err(Some(('A', 'B'))));
+    assert_eq!("AAAAAAA".chars().all_equal_value(), Ok('A'));
+    {
+        let mut it = [1, 2, 3].iter().copied();
+        let result = it.all_equal_value();
+        assert_eq!(result, Err(Some((1, 2))));
+        let remaining = it.next();
+        assert_eq!(remaining, Some(3));
+        assert!(it.next().is_none());
+    }
+}
+
+#[test]
+fn all_unique() {
+    assert!("ABCDEFGH".chars().all_unique());
+    assert!(!"ABCDEFGA".chars().all_unique());
+    assert!(::std::iter::empty::<usize>().all_unique());
+}
+
+#[test]
+fn test_put_back_n() {
+    let xs = [0, 1, 1, 1, 2, 1, 3, 3];
+    let mut pb = put_back_n(xs.iter().cloned());
+    pb.next();
+    pb.next();
+    pb.put_back(1);
+    pb.put_back(0);
+    it::assert_equal(pb, xs.iter().cloned());
+}
+
+#[test]
+fn tee() {
+    let xs = [0, 1, 2, 3];
+    let (mut t1, mut t2) = xs.iter().cloned().tee();
+    assert_eq!(t1.next(), Some(0));
+    assert_eq!(t2.next(), Some(0));
+    assert_eq!(t1.next(), Some(1));
+    assert_eq!(t1.next(), Some(2));
+    assert_eq!(t1.next(), Some(3));
+    assert_eq!(t1.next(), None);
+    assert_eq!(t2.next(), Some(1));
+    assert_eq!(t2.next(), Some(2));
+    assert_eq!(t1.next(), None);
+    assert_eq!(t2.next(), Some(3));
+    assert_eq!(t2.next(), None);
+    assert_eq!(t1.next(), None);
+    assert_eq!(t2.next(), None);
+
+    let (t1, t2) = xs.iter().cloned().tee();
+    it::assert_equal(t1, xs.iter().cloned());
+    it::assert_equal(t2, xs.iter().cloned());
+
+    let (t1, t2) = xs.iter().cloned().tee();
+    it::assert_equal(t1.zip(t2), xs.iter().cloned().zip(xs.iter().cloned()));
+}
+
+#[test]
+fn test_rciter() {
+    let xs = [0, 1, 1, 1, 2, 1, 3, 5, 6];
+
+    let mut r1 = rciter(xs.iter().cloned());
+    let mut r2 = r1.clone();
+    assert_eq!(r1.next(), Some(0));
+    assert_eq!(r2.next(), Some(1));
+    let mut z = r1.zip(r2);
+    assert_eq!(z.next(), Some((1, 1)));
+    assert_eq!(z.next(), Some((2, 1)));
+    assert_eq!(z.next(), Some((3, 5)));
+    assert_eq!(z.next(), None);
+
+    // test intoiterator
+    let r1 = rciter(0..5);
+    let mut z = izip!(&r1, r1);
+    assert_eq!(z.next(), Some((0, 1)));
+}
+
+#[test]
+fn trait_pointers() {
+    struct ByRef<'r, I: ?Sized>(&'r mut I);
+
+    impl<'r, X, I> Iterator for ByRef<'r, I>
+    where
+        I: ?Sized + 'r + Iterator<Item = X>,
+    {
+        type Item = X;
+        fn next(&mut self) -> Option<Self::Item> {
+            self.0.next()
+        }
+    }
+
+    let mut it = Box::new(0..10) as Box<dyn Iterator<Item = i32>>;
+    assert_eq!(it.next(), Some(0));
+
+    {
+        let jt: &mut dyn Iterator<Item = i32> = &mut *it;
+        assert_eq!(jt.next(), Some(1));
+
+        {
+            let mut r = ByRef(jt);
+            assert_eq!(r.next(), Some(2));
+        }
+
+        assert_eq!(jt.find_position(|x| *x == 4), Some((1, 4)));
+        jt.for_each(|_| ());
+    }
+}
+
+#[test]
+fn merge_by() {
+    let odd: Vec<(u32, &str)> = vec![(1, "hello"), (3, "world"), (5, "!")];
+    let even = [(2, "foo"), (4, "bar"), (6, "baz")];
+    let expected = [
+        (1, "hello"),
+        (2, "foo"),
+        (3, "world"),
+        (4, "bar"),
+        (5, "!"),
+        (6, "baz"),
+    ];
+    let results = odd.iter().merge_by(even.iter(), |a, b| a.0 <= b.0);
+    it::assert_equal(results, expected.iter());
+}
+
+#[test]
+fn merge_by_btree() {
+    use std::collections::BTreeMap;
+    let mut bt1 = BTreeMap::new();
+    bt1.insert("hello", 1);
+    bt1.insert("world", 3);
+    let mut bt2 = BTreeMap::new();
+    bt2.insert("foo", 2);
+    bt2.insert("bar", 4);
+    let results = bt1.into_iter().merge_by(bt2, |a, b| a.0 <= b.0);
+    let expected = vec![("bar", 4), ("foo", 2), ("hello", 1), ("world", 3)];
+    it::assert_equal(results, expected);
+}
+
+#[test]
+fn kmerge() {
+    let its = (0..4).map(|s| (s..10).step_by(4));
+
+    it::assert_equal(its.kmerge(), 0..10);
+}
+
+#[test]
+fn kmerge_2() {
+    let its = vec![3, 2, 1, 0].into_iter().map(|s| (s..10).step_by(4));
+
+    it::assert_equal(its.kmerge(), 0..10);
+}
+
+#[test]
+fn kmerge_empty() {
+    let its = (0..4).map(|_| 0..0);
+    assert_eq!(its.kmerge().next(), None);
+}
+
+#[test]
+fn kmerge_size_hint() {
+    let its = (0..5).map(|_| (0..10));
+    assert_eq!(its.kmerge().size_hint(), (50, Some(50)));
+}
+
+#[test]
+fn kmerge_empty_size_hint() {
+    let its = (0..5).map(|_| (0..0));
+    assert_eq!(its.kmerge().size_hint(), (0, Some(0)));
+}
+
+#[test]
+fn join() {
+    let many = [1, 2, 3];
+    let one = [1];
+    let none: Vec<i32> = vec![];
+
+    assert_eq!(many.iter().join(", "), "1, 2, 3");
+    assert_eq!(one.iter().join(", "), "1");
+    assert_eq!(none.iter().join(", "), "");
+}
+
+#[test]
+fn sorted_unstable_by() {
+    let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| a.cmp(&b));
+    it::assert_equal(sc, vec![1, 2, 3, 4]);
+
+    let v = (0..5).sorted_unstable_by(|&a, &b| a.cmp(&b).reverse());
+    it::assert_equal(v, vec![4, 3, 2, 1, 0]);
+}
+
+#[test]
+fn sorted_unstable_by_key() {
+    let sc = [3, 4, 1, 2].iter().cloned().sorted_unstable_by_key(|&x| x);
+    it::assert_equal(sc, vec![1, 2, 3, 4]);
+
+    let v = (0..5).sorted_unstable_by_key(|&x| -x);
+    it::assert_equal(v, vec![4, 3, 2, 1, 0]);
+}
+
+#[test]
+fn sorted_by() {
+    let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| a.cmp(&b));
+    it::assert_equal(sc, vec![1, 2, 3, 4]);
+
+    let v = (0..5).sorted_by(|&a, &b| a.cmp(&b).reverse());
+    it::assert_equal(v, vec![4, 3, 2, 1, 0]);
+}
+
+#[cfg(not(miri))]
+qc::quickcheck! {
+    fn k_smallest_range(n: i64, m: u16, k: u16) -> () {
+        // u16 is used to constrain k and m to 0..2¹⁶,
+        //  otherwise the test could use too much memory.
+        let (k, m) = (k as usize, m as u64);
+
+        let mut v: Vec<_> = (n..n.saturating_add(m as _)).collect();
+        // Generate a random permutation of n..n+m
+        v.shuffle(&mut thread_rng());
+
+        // Construct the right answers for the top and bottom elements
+        let mut sorted = v.clone();
+        sorted.sort();
+        // how many elements are we checking
+        let num_elements = min(k, m as _);
+
+        // Compute the top and bottom k in various combinations
+        let sorted_smallest = sorted[..num_elements].iter().cloned();
+        let smallest = v.iter().cloned().k_smallest(k);
+        let smallest_by = v.iter().cloned().k_smallest_by(k, Ord::cmp);
+        let smallest_by_key = v.iter().cloned().k_smallest_by_key(k, |&x| x);
+
+        let sorted_largest = sorted[sorted.len() - num_elements..].iter().rev().cloned();
+        let largest = v.iter().cloned().k_largest(k);
+        let largest_by = v.iter().cloned().k_largest_by(k, Ord::cmp);
+        let largest_by_key = v.iter().cloned().k_largest_by_key(k, |&x| x);
+
+        // Check the variations produce the same answers and that they're right
+        it::assert_equal(smallest, sorted_smallest.clone());
+        it::assert_equal(smallest_by, sorted_smallest.clone());
+        it::assert_equal(smallest_by_key, sorted_smallest);
+
+        it::assert_equal(largest, sorted_largest.clone());
+        it::assert_equal(largest_by, sorted_largest.clone());
+        it::assert_equal(largest_by_key, sorted_largest);
+    }
+
+    fn k_smallest_relaxed_range(n: i64, m: u16, k: u16) -> () {
+        // u16 is used to constrain k and m to 0..2¹⁶,
+        //  otherwise the test could use too much memory.
+        let (k, m) = (k as usize, m as u64);
+
+        let mut v: Vec<_> = (n..n.saturating_add(m as _)).collect();
+        // Generate a random permutation of n..n+m
+        v.shuffle(&mut thread_rng());
+
+        // Construct the right answers for the top and bottom elements
+        let mut sorted = v.clone();
+        sorted.sort();
+        // how many elements are we checking
+        let num_elements = min(k, m as _);
+
+        // Compute the top and bottom k in various combinations
+        let sorted_smallest = sorted[..num_elements].iter().cloned();
+        let smallest = v.iter().cloned().k_smallest_relaxed(k);
+        let smallest_by = v.iter().cloned().k_smallest_relaxed_by(k, Ord::cmp);
+        let smallest_by_key = v.iter().cloned().k_smallest_relaxed_by_key(k, |&x| x);
+
+        let sorted_largest = sorted[sorted.len() - num_elements..].iter().rev().cloned();
+        let largest = v.iter().cloned().k_largest_relaxed(k);
+        let largest_by = v.iter().cloned().k_largest_relaxed_by(k, Ord::cmp);
+        let largest_by_key = v.iter().cloned().k_largest_relaxed_by_key(k, |&x| x);
+
+        // Check the variations produce the same answers and that they're right
+        it::assert_equal(smallest, sorted_smallest.clone());
+        it::assert_equal(smallest_by, sorted_smallest.clone());
+        it::assert_equal(smallest_by_key, sorted_smallest);
+
+        it::assert_equal(largest, sorted_largest.clone());
+        it::assert_equal(largest_by, sorted_largest.clone());
+        it::assert_equal(largest_by_key, sorted_largest);
+    }
+}
+
+#[derive(Clone, Debug)]
+struct RandIter<T: 'static + Clone + Send, R: 'static + Clone + Rng + SeedableRng + Send = StdRng> {
+    idx: usize,
+    len: usize,
+    rng: R,
+    _t: PhantomData<T>,
+}
+
+impl<T: Clone + Send, R: Clone + Rng + SeedableRng + Send> Iterator for RandIter<T, R>
+where
+    Standard: Distribution<T>,
+{
+    type Item = T;
+    fn next(&mut self) -> Option<T> {
+        if self.idx == self.len {
+            None
+        } else {
+            self.idx += 1;
+            Some(self.rng.gen())
+        }
+    }
+}
+
+impl<T: Clone + Send, R: Clone + Rng + SeedableRng + Send> qc::Arbitrary for RandIter<T, R> {
+    fn arbitrary<G: qc::Gen>(g: &mut G) -> Self {
+        Self {
+            idx: 0,
+            len: g.size(),
+            rng: R::seed_from_u64(g.next_u64()),
+            _t: PhantomData {},
+        }
+    }
+}
+
+// Check that taking the k smallest is the same as
+//  sorting then taking the k first elements
+fn k_smallest_sort<I>(i: I, k: u16)
+where
+    I: Iterator + Clone,
+    I::Item: Ord + Debug,
+{
+    let j = i.clone();
+    let i1 = i.clone();
+    let j1 = i.clone();
+    let k = k as usize;
+    it::assert_equal(i.k_smallest(k), j.sorted().take(k));
+    it::assert_equal(i1.k_smallest_relaxed(k), j1.sorted().take(k));
+}
+
+// Similar to `k_smallest_sort` but for our custom heap implementation.
+fn k_smallest_by_sort<I>(i: I, k: u16)
+where
+    I: Iterator + Clone,
+    I::Item: Ord + Debug,
+{
+    let j = i.clone();
+    let i1 = i.clone();
+    let j1 = i.clone();
+    let k = k as usize;
+    it::assert_equal(i.k_smallest_by(k, Ord::cmp), j.sorted().take(k));
+    it::assert_equal(i1.k_smallest_relaxed_by(k, Ord::cmp), j1.sorted().take(k));
+}
+
+macro_rules! generic_test {
+    ($f:ident, $($t:ty),+) => {
+        $(paste::item! {
+            qc::quickcheck! {
+                fn [< $f _ $t >](i: RandIter<$t>, k: u16) -> () {
+                    $f(i, k)
+                }
+            }
+        })+
+    };
+}
+
+#[cfg(not(miri))]
+generic_test!(k_smallest_sort, u8, u16, u32, u64, i8, i16, i32, i64);
+#[cfg(not(miri))]
+generic_test!(k_smallest_by_sort, u8, u16, u32, u64, i8, i16, i32, i64);
+
+#[test]
+fn sorted_by_key() {
+    let sc = [3, 4, 1, 2].iter().cloned().sorted_by_key(|&x| x);
+    it::assert_equal(sc, vec![1, 2, 3, 4]);
+
+    let v = (0..5).sorted_by_key(|&x| -x);
+    it::assert_equal(v, vec![4, 3, 2, 1, 0]);
+}
+
+#[test]
+fn sorted_by_cached_key() {
+    // Track calls to key function
+    let mut ncalls = 0;
+
+    let sorted = [3, 4, 1, 2].iter().cloned().sorted_by_cached_key(|&x| {
+        ncalls += 1;
+        x.to_string()
+    });
+    it::assert_equal(sorted, vec![1, 2, 3, 4]);
+    // Check key function called once per element
+    assert_eq!(ncalls, 4);
+
+    let mut ncalls = 0;
+
+    let sorted = (0..5).sorted_by_cached_key(|&x| {
+        ncalls += 1;
+        -x
+    });
+    it::assert_equal(sorted, vec![4, 3, 2, 1, 0]);
+    // Check key function called once per element
+    assert_eq!(ncalls, 5);
+}
+
+#[test]
+fn test_multipeek() {
+    let nums = vec![1u8, 2, 3, 4, 5];
+
+    let mp = multipeek(nums.iter().copied());
+    assert_eq!(nums, mp.collect::<Vec<_>>());
+
+    let mut mp = multipeek(nums.iter().copied());
+    assert_eq!(mp.peek(), Some(&1));
+    assert_eq!(mp.next(), Some(1));
+    assert_eq!(mp.peek(), Some(&2));
+    assert_eq!(mp.peek(), Some(&3));
+    assert_eq!(mp.next(), Some(2));
+    assert_eq!(mp.peek(), Some(&3));
+    assert_eq!(mp.peek(), Some(&4));
+    assert_eq!(mp.peek(), Some(&5));
+    assert_eq!(mp.peek(), None);
+    assert_eq!(mp.next(), Some(3));
+    assert_eq!(mp.next(), Some(4));
+    assert_eq!(mp.peek(), Some(&5));
+    assert_eq!(mp.peek(), None);
+    assert_eq!(mp.next(), Some(5));
+    assert_eq!(mp.next(), None);
+    assert_eq!(mp.peek(), None);
+}
+
+#[test]
+fn test_multipeek_reset() {
+    let data = [1, 2, 3, 4];
+
+    let mut mp = multipeek(cloned(&data));
+    assert_eq!(mp.peek(), Some(&1));
+    assert_eq!(mp.next(), Some(1));
+    assert_eq!(mp.peek(), Some(&2));
+    assert_eq!(mp.peek(), Some(&3));
+    mp.reset_peek();
+    assert_eq!(mp.peek(), Some(&2));
+    assert_eq!(mp.next(), Some(2));
+}
+
+#[test]
+fn test_multipeek_peeking_next() {
+    use crate::it::PeekingNext;
+    let nums = [1u8, 2, 3, 4, 5, 6, 7];
+
+    let mut mp = multipeek(nums.iter().copied());
+    assert_eq!(mp.peeking_next(|&x| x != 0), Some(1));
+    assert_eq!(mp.next(), Some(2));
+    assert_eq!(mp.peek(), Some(&3));
+    assert_eq!(mp.peek(), Some(&4));
+    assert_eq!(mp.peeking_next(|&x| x == 3), Some(3));
+    assert_eq!(mp.peek(), Some(&4));
+    assert_eq!(mp.peeking_next(|&x| x != 4), None);
+    assert_eq!(mp.peeking_next(|&x| x == 4), Some(4));
+    assert_eq!(mp.peek(), Some(&5));
+    assert_eq!(mp.peek(), Some(&6));
+    assert_eq!(mp.peeking_next(|&x| x != 5), None);
+    assert_eq!(mp.peek(), Some(&7));
+    assert_eq!(mp.peeking_next(|&x| x == 5), Some(5));
+    assert_eq!(mp.peeking_next(|&x| x == 6), Some(6));
+    assert_eq!(mp.peek(), Some(&7));
+    assert_eq!(mp.peek(), None);
+    assert_eq!(mp.next(), Some(7));
+    assert_eq!(mp.peek(), None);
+}
+
+#[test]
+fn test_repeat_n_peeking_next() {
+    use crate::it::PeekingNext;
+    let mut rn = repeat_n(0, 5);
+    assert_eq!(rn.peeking_next(|&x| x != 0), None);
+    assert_eq!(rn.peeking_next(|&x| x <= 0), Some(0));
+    assert_eq!(rn.next(), Some(0));
+    assert_eq!(rn.peeking_next(|&x| x <= 0), Some(0));
+    assert_eq!(rn.peeking_next(|&x| x != 0), None);
+    assert_eq!(rn.peeking_next(|&x| x >= 0), Some(0));
+    assert_eq!(rn.next(), Some(0));
+    assert_eq!(rn.peeking_next(|&x| x <= 0), None);
+    assert_eq!(rn.next(), None);
+}
+
+#[test]
+fn test_peek_nth() {
+    let nums = vec![1u8, 2, 3, 4, 5];
+
+    let iter = peek_nth(nums.iter().copied());
+    assert_eq!(nums, iter.collect::<Vec<_>>());
+
+    let mut iter = peek_nth(nums.iter().copied());
+
+    assert_eq!(iter.peek_nth(0), Some(&1));
+    assert_eq!(iter.peek_nth(0), Some(&1));
+    assert_eq!(iter.next(), Some(1));
+
+    assert_eq!(iter.peek_nth(0), Some(&2));
+    assert_eq!(iter.peek_nth(1), Some(&3));
+    assert_eq!(iter.next(), Some(2));
+
+    assert_eq!(iter.peek_nth(0), Some(&3));
+    assert_eq!(iter.peek_nth(1), Some(&4));
+    assert_eq!(iter.peek_nth(2), Some(&5));
+    assert_eq!(iter.peek_nth(3), None);
+
+    assert_eq!(iter.next(), Some(3));
+    assert_eq!(iter.next(), Some(4));
+
+    assert_eq!(iter.peek_nth(0), Some(&5));
+    assert_eq!(iter.peek_nth(1), None);
+    assert_eq!(iter.next(), Some(5));
+    assert_eq!(iter.next(), None);
+
+    assert_eq!(iter.peek_nth(0), None);
+    assert_eq!(iter.peek_nth(1), None);
+}
+
+#[test]
+fn test_peek_nth_peeking_next() {
+    use it::PeekingNext;
+    let nums = [1u8, 2, 3, 4, 5, 6, 7];
+    let mut iter = peek_nth(nums.iter().copied());
+
+    assert_eq!(iter.peeking_next(|&x| x != 0), Some(1));
+    assert_eq!(iter.next(), Some(2));
+
+    assert_eq!(iter.peek_nth(0), Some(&3));
+    assert_eq!(iter.peek_nth(1), Some(&4));
+    assert_eq!(iter.peeking_next(|&x| x == 3), Some(3));
+    assert_eq!(iter.peek(), Some(&4));
+
+    assert_eq!(iter.peeking_next(|&x| x != 4), None);
+    assert_eq!(iter.peeking_next(|&x| x == 4), Some(4));
+    assert_eq!(iter.peek_nth(0), Some(&5));
+    assert_eq!(iter.peek_nth(1), Some(&6));
+
+    assert_eq!(iter.peeking_next(|&x| x != 5), None);
+    assert_eq!(iter.peek(), Some(&5));
+
+    assert_eq!(iter.peeking_next(|&x| x == 5), Some(5));
+    assert_eq!(iter.peeking_next(|&x| x == 6), Some(6));
+    assert_eq!(iter.peek_nth(0), Some(&7));
+    assert_eq!(iter.peek_nth(1), None);
+    assert_eq!(iter.next(), Some(7));
+    assert_eq!(iter.peek(), None);
+}
+
+#[test]
+fn test_peek_nth_next_if() {
+    let nums = [1u8, 2, 3, 4, 5, 6, 7];
+    let mut iter = peek_nth(nums.iter().copied());
+
+    assert_eq!(iter.next_if(|&x| x != 0), Some(1));
+    assert_eq!(iter.next(), Some(2));
+
+    assert_eq!(iter.peek_nth(0), Some(&3));
+    assert_eq!(iter.peek_nth(1), Some(&4));
+    assert_eq!(iter.next_if_eq(&3), Some(3));
+    assert_eq!(iter.peek(), Some(&4));
+
+    assert_eq!(iter.next_if(|&x| x != 4), None);
+    assert_eq!(iter.next_if_eq(&4), Some(4));
+    assert_eq!(iter.peek_nth(0), Some(&5));
+    assert_eq!(iter.peek_nth(1), Some(&6));
+
+    assert_eq!(iter.next_if(|&x| x != 5), None);
+    assert_eq!(iter.peek(), Some(&5));
+
+    assert_eq!(iter.next_if(|&x| x % 2 == 1), Some(5));
+    assert_eq!(iter.next_if_eq(&6), Some(6));
+    assert_eq!(iter.peek_nth(0), Some(&7));
+    assert_eq!(iter.peek_nth(1), None);
+    assert_eq!(iter.next(), Some(7));
+    assert_eq!(iter.peek(), None);
+}
+
+#[test]
+fn pad_using() {
+    it::assert_equal((0..0).pad_using(1, |_| 1), 1..2);
+
+    let v: Vec<usize> = vec![0, 1, 2];
+    let r = v.into_iter().pad_using(5, |n| n);
+    it::assert_equal(r, vec![0, 1, 2, 3, 4]);
+
+    let v: Vec<usize> = vec![0, 1, 2];
+    let r = v.into_iter().pad_using(1, |_| panic!());
+    it::assert_equal(r, vec![0, 1, 2]);
+}
+
+#[test]
+fn chunk_by() {
+    for (ch1, sub) in &"AABBCCC".chars().chunk_by(|&x| x) {
+        for ch2 in sub {
+            assert_eq!(ch1, ch2);
+        }
+    }
+
+    for (ch1, sub) in &"AAABBBCCCCDDDD".chars().chunk_by(|&x| x) {
+        for ch2 in sub {
+            assert_eq!(ch1, ch2);
+            if ch1 == 'C' {
+                break;
+            }
+        }
+    }
+
+    let toupper = |ch: &char| ch.to_uppercase().next().unwrap();
+
+    // try all possible orderings
+    for indices in permutohedron::Heap::new(&mut [0, 1, 2, 3]) {
+        let chunks = "AaaBbbccCcDDDD".chars().chunk_by(&toupper);
+        let mut subs = chunks.into_iter().collect_vec();
+
+        for &idx in &indices[..] {
+            let (key, text) = match idx {
+                0 => ('A', "Aaa".chars()),
+                1 => ('B', "Bbb".chars()),
+                2 => ('C', "ccCc".chars()),
+                3 => ('D', "DDDD".chars()),
+                _ => unreachable!(),
+            };
+            assert_eq!(key, subs[idx].0);
+            it::assert_equal(&mut subs[idx].1, text);
+        }
+    }
+
+    let chunks = "AAABBBCCCCDDDD".chars().chunk_by(|&x| x);
+    let mut subs = chunks.into_iter().map(|(_, g)| g).collect_vec();
+
+    let sd = subs.pop().unwrap();
+    let sc = subs.pop().unwrap();
+    let sb = subs.pop().unwrap();
+    let sa = subs.pop().unwrap();
+    for (a, b, c, d) in multizip((sa, sb, sc, sd)) {
+        assert_eq!(a, 'A');
+        assert_eq!(b, 'B');
+        assert_eq!(c, 'C');
+        assert_eq!(d, 'D');
+    }
+
+    // check that the key closure is called exactly n times
+    {
+        let mut ntimes = 0;
+        let text = "AABCCC";
+        for (_, sub) in &text.chars().chunk_by(|&x| {
+            ntimes += 1;
+            x
+        }) {
+            for _ in sub {}
+        }
+        assert_eq!(ntimes, text.len());
+    }
+
+    {
+        let mut ntimes = 0;
+        let text = "AABCCC";
+        for _ in &text.chars().chunk_by(|&x| {
+            ntimes += 1;
+            x
+        }) {}
+        assert_eq!(ntimes, text.len());
+    }
+
+    {
+        let text = "ABCCCDEEFGHIJJKK";
+        let gr = text.chars().chunk_by(|&x| x);
+        it::assert_equal(gr.into_iter().flat_map(|(_, sub)| sub), text.chars());
+    }
+}
+
+#[test]
+fn chunk_by_lazy_2() {
+    let data = [0, 1];
+    let chunks = data.iter().chunk_by(|k| *k);
+    let gs = chunks.into_iter().collect_vec();
+    it::assert_equal(data.iter(), gs.into_iter().flat_map(|(_k, g)| g));
+
+    let data = [0, 1, 1, 0, 0];
+    let chunks = data.iter().chunk_by(|k| *k);
+    let mut gs = chunks.into_iter().collect_vec();
+    gs[1..].reverse();
+    it::assert_equal(&[0, 0, 0, 1, 1], gs.into_iter().flat_map(|(_, g)| g));
+
+    let grouper = data.iter().chunk_by(|k| *k);
+    let mut chunks = Vec::new();
+    for (k, chunk) in &grouper {
+        if *k == 1 {
+            chunks.push(chunk);
+        }
+    }
+    it::assert_equal(&mut chunks[0], &[1, 1]);
+
+    let data = [0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
+    let grouper = data.iter().chunk_by(|k| *k);
+    let mut chunks = Vec::new();
+    for (i, (_, chunk)) in grouper.into_iter().enumerate() {
+        if i < 2 {
+            chunks.push(chunk);
+        } else if i < 4 {
+            for _ in chunk {}
+        } else {
+            chunks.push(chunk);
+        }
+    }
+    it::assert_equal(&mut chunks[0], &[0, 0, 0]);
+    it::assert_equal(&mut chunks[1], &[1, 1]);
+    it::assert_equal(&mut chunks[2], &[3, 3]);
+
+    let data = [0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
+    let mut i = 0;
+    let grouper = data.iter().chunk_by(move |_| {
+        let k = i / 3;
+        i += 1;
+        k
+    });
+    for (i, chunk) in &grouper {
+        match i {
+            0 => it::assert_equal(chunk, &[0, 0, 0]),
+            1 => it::assert_equal(chunk, &[1, 1, 0]),
+            2 => it::assert_equal(chunk, &[0, 2, 2]),
+            3 => it::assert_equal(chunk, &[3, 3]),
+            _ => unreachable!(),
+        }
+    }
+}
+
+#[test]
+fn chunk_by_lazy_3() {
+    // test consuming each chunk on the lap after it was produced
+    let data = [0, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2];
+    let grouper = data.iter().chunk_by(|elt| *elt);
+    let mut last = None;
+    for (key, chunk) in &grouper {
+        if let Some(gr) = last.take() {
+            for elt in gr {
+                assert!(elt != key && i32::abs(elt - key) == 1);
+            }
+        }
+        last = Some(chunk);
+    }
+}
+
+#[test]
+fn chunks() {
+    let data = [0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3];
+    let grouper = data.iter().chunks(3);
+    for (i, chunk) in grouper.into_iter().enumerate() {
+        match i {
+            0 => it::assert_equal(chunk, &[0, 0, 0]),
+            1 => it::assert_equal(chunk, &[1, 1, 0]),
+            2 => it::assert_equal(chunk, &[0, 2, 2]),
+            3 => it::assert_equal(chunk, &[3, 3]),
+            _ => unreachable!(),
+        }
+    }
+}
+
+#[test]
+fn concat_empty() {
+    let data: Vec<Vec<()>> = Vec::new();
+    assert_eq!(data.into_iter().concat(), Vec::new())
+}
+
+#[test]
+fn concat_non_empty() {
+    let data = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
+    assert_eq!(data.into_iter().concat(), vec![1, 2, 3, 4, 5, 6, 7, 8, 9])
+}
+
+#[test]
+fn combinations() {
+    assert!((1..3).combinations(5).next().is_none());
+
+    let it = (1..3).combinations(2);
+    it::assert_equal(it, vec![vec![1, 2]]);
+
+    let it = (1..5).combinations(2);
+    it::assert_equal(
+        it,
+        vec![
+            vec![1, 2],
+            vec![1, 3],
+            vec![1, 4],
+            vec![2, 3],
+            vec![2, 4],
+            vec![3, 4],
+        ],
+    );
+
+    it::assert_equal((0..0).tuple_combinations::<(_, _)>(), <Vec<_>>::new());
+    it::assert_equal((0..1).tuple_combinations::<(_, _)>(), <Vec<_>>::new());
+    it::assert_equal((0..2).tuple_combinations::<(_, _)>(), vec![(0, 1)]);
+
+    it::assert_equal((0..0).combinations(2), <Vec<Vec<_>>>::new());
+    it::assert_equal((0..1).combinations(1), vec![vec![0]]);
+    it::assert_equal((0..2).combinations(1), vec![vec![0], vec![1]]);
+    it::assert_equal((0..2).combinations(2), vec![vec![0, 1]]);
+}
+
+#[test]
+fn combinations_of_too_short() {
+    for i in 1..10 {
+        assert!((0..0).combinations(i).next().is_none());
+        assert!((0..i - 1).combinations(i).next().is_none());
+    }
+}
+
+#[test]
+fn combinations_zero() {
+    it::assert_equal((1..3).combinations(0), vec![vec![]]);
+    it::assert_equal((0..0).combinations(0), vec![vec![]]);
+}
+
+fn binomial(n: usize, k: usize) -> usize {
+    if k > n {
+        0
+    } else {
+        (n - k + 1..=n).product::<usize>() / (1..=k).product::<usize>()
+    }
+}
+
+#[test]
+fn combinations_range_count() {
+    for n in 0..=7 {
+        for k in 0..=7 {
+            let len = binomial(n, k);
+            let mut it = (0..n).combinations(k);
+            assert_eq!(len, it.clone().count());
+            assert_eq!(len, it.size_hint().0);
+            assert_eq!(Some(len), it.size_hint().1);
+            for count in (0..len).rev() {
+                let elem = it.next();
+                assert!(elem.is_some());
+                assert_eq!(count, it.clone().count());
+                assert_eq!(count, it.size_hint().0);
+                assert_eq!(Some(count), it.size_hint().1);
+            }
+            let should_be_none = it.next();
+            assert!(should_be_none.is_none());
+        }
+    }
+}
+
+#[test]
+fn combinations_inexact_size_hints() {
+    for k in 0..=7 {
+        let mut numbers = (0..18).filter(|i| i % 2 == 0); // 9 elements
+        let mut it = numbers.clone().combinations(k);
+        let real_n = numbers.clone().count();
+        let len = binomial(real_n, k);
+        assert_eq!(len, it.clone().count());
+
+        let mut nb_loaded = 0;
+        let sh = numbers.size_hint();
+        assert_eq!(binomial(sh.0 + nb_loaded, k), it.size_hint().0);
+        assert_eq!(sh.1.map(|n| binomial(n + nb_loaded, k)), it.size_hint().1);
+
+        for next_count in 1..=len {
+            let elem = it.next();
+            assert!(elem.is_some());
+            assert_eq!(len - next_count, it.clone().count());
+            if next_count == 1 {
+                // The very first time, the lazy buffer is prefilled.
+                nb_loaded = numbers.by_ref().take(k).count();
+            } else {
+                // Then it loads one item each time until exhausted.
+                let nb = numbers.next();
+                if nb.is_some() {
+                    nb_loaded += 1;
+                }
+            }
+            let sh = numbers.size_hint();
+            if next_count > real_n - k + 1 {
+                assert_eq!(0, sh.0);
+                assert_eq!(Some(0), sh.1);
+                assert_eq!(real_n, nb_loaded);
+                // Once it's fully loaded, size hints of `it` are exacts.
+            }
+            assert_eq!(binomial(sh.0 + nb_loaded, k) - next_count, it.size_hint().0);
+            assert_eq!(
+                sh.1.map(|n| binomial(n + nb_loaded, k) - next_count),
+                it.size_hint().1
+            );
+        }
+        let should_be_none = it.next();
+        assert!(should_be_none.is_none());
+    }
+}
+
+#[test]
+fn permutations_zero() {
+    it::assert_equal((1..3).permutations(0), vec![vec![]]);
+    it::assert_equal((0..0).permutations(0), vec![vec![]]);
+}
+
+#[test]
+fn permutations_range_count() {
+    for n in 0..=4 {
+        for k in 0..=4 {
+            let len = if k <= n { (n - k + 1..=n).product() } else { 0 };
+            let mut it = (0..n).permutations(k);
+            assert_eq!(len, it.clone().count());
+            assert_eq!(len, it.size_hint().0);
+            assert_eq!(Some(len), it.size_hint().1);
+            for count in (0..len).rev() {
+                let elem = it.next();
+                assert!(elem.is_some());
+                assert_eq!(count, it.clone().count());
+                assert_eq!(count, it.size_hint().0);
+                assert_eq!(Some(count), it.size_hint().1);
+            }
+            let should_be_none = it.next();
+            assert!(should_be_none.is_none());
+        }
+    }
+}
+
+#[test]
+fn permutations_overflowed_size_hints() {
+    let mut it = std::iter::repeat(()).permutations(2);
+    assert_eq!(it.size_hint().0, usize::MAX);
+    assert_eq!(it.size_hint().1, None);
+    for nb_generated in 1..=1000 {
+        it.next();
+        assert!(it.size_hint().0 >= usize::MAX - nb_generated);
+        assert_eq!(it.size_hint().1, None);
+    }
+}
+
+#[test]
+#[cfg(not(miri))]
+fn combinations_with_replacement() {
+    // Pool smaller than n
+    it::assert_equal((0..1).combinations_with_replacement(2), vec![vec![0, 0]]);
+    // Pool larger than n
+    it::assert_equal(
+        (0..3).combinations_with_replacement(2),
+        vec![
+            vec![0, 0],
+            vec![0, 1],
+            vec![0, 2],
+            vec![1, 1],
+            vec![1, 2],
+            vec![2, 2],
+        ],
+    );
+    // Zero size
+    it::assert_equal((0..3).combinations_with_replacement(0), vec![vec![]]);
+    // Zero size on empty pool
+    it::assert_equal((0..0).combinations_with_replacement(0), vec![vec![]]);
+    // Empty pool
+    it::assert_equal(
+        (0..0).combinations_with_replacement(2),
+        <Vec<Vec<_>>>::new(),
+    );
+}
+
+#[test]
+fn combinations_with_replacement_range_count() {
+    for n in 0..=4 {
+        for k in 0..=4 {
+            let len = binomial(usize::saturating_sub(n + k, 1), k);
+            let mut it = (0..n).combinations_with_replacement(k);
+            assert_eq!(len, it.clone().count());
+            assert_eq!(len, it.size_hint().0);
+            assert_eq!(Some(len), it.size_hint().1);
+            for count in (0..len).rev() {
+                let elem = it.next();
+                assert!(elem.is_some());
+                assert_eq!(count, it.clone().count());
+                assert_eq!(count, it.size_hint().0);
+                assert_eq!(Some(count), it.size_hint().1);
+            }
+            let should_be_none = it.next();
+            assert!(should_be_none.is_none());
+        }
+    }
+}
+
+#[test]
+fn powerset() {
+    it::assert_equal((0..0).powerset(), vec![vec![]]);
+    it::assert_equal((0..1).powerset(), vec![vec![], vec![0]]);
+    it::assert_equal(
+        (0..2).powerset(),
+        vec![vec![], vec![0], vec![1], vec![0, 1]],
+    );
+    it::assert_equal(
+        (0..3).powerset(),
+        vec![
+            vec![],
+            vec![0],
+            vec![1],
+            vec![2],
+            vec![0, 1],
+            vec![0, 2],
+            vec![1, 2],
+            vec![0, 1, 2],
+        ],
+    );
+
+    assert_eq!((0..4).powerset().count(), 1 << 4);
+    assert_eq!((0..8).powerset().count(), 1 << 8);
+    assert_eq!((0..16).powerset().count(), 1 << 16);
+
+    for n in 0..=4 {
+        let mut it = (0..n).powerset();
+        let len = 2_usize.pow(n);
+        assert_eq!(len, it.clone().count());
+        assert_eq!(len, it.size_hint().0);
+        assert_eq!(Some(len), it.size_hint().1);
+        for count in (0..len).rev() {
+            let elem = it.next();
+            assert!(elem.is_some());
+            assert_eq!(count, it.clone().count());
+            assert_eq!(count, it.size_hint().0);
+            assert_eq!(Some(count), it.size_hint().1);
+        }
+        let should_be_none = it.next();
+        assert!(should_be_none.is_none());
+    }
+}
+
+#[test]
+fn diff_mismatch() {
+    let a = [1, 2, 3, 4];
+    let b = vec![1.0, 5.0, 3.0, 4.0];
+    let b_map = b.into_iter().map(|f| f as i32);
+    let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b);
+
+    assert!(match diff {
+        Some(it::Diff::FirstMismatch(1, _, from_diff)) =>
+            from_diff.collect::<Vec<_>>() == vec![5, 3, 4],
+        _ => false,
+    });
+}
+
+#[test]
+fn diff_longer() {
+    let a = [1, 2, 3, 4];
+    let b = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
+    let b_map = b.into_iter().map(|f| f as i32);
+    let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b);
+
+    assert!(match diff {
+        Some(it::Diff::Longer(_, remaining)) => remaining.collect::<Vec<_>>() == vec![5, 6],
+        _ => false,
+    });
+}
+
+#[test]
+fn diff_shorter() {
+    let a = [1, 2, 3, 4];
+    let b = vec![1.0, 2.0];
+    let b_map = b.into_iter().map(|f| f as i32);
+    let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b);
+
+    assert!(match diff {
+        Some(it::Diff::Shorter(len, _)) => len == 2,
+        _ => false,
+    });
+}
+
+#[test]
+fn extrema_set() {
+    use std::cmp::Ordering;
+
+    // A peculiar type: Equality compares both tuple items, but ordering only the
+    // first item. Used to distinguish equal elements.
+    #[derive(Clone, Debug, PartialEq, Eq)]
+    struct Val(u32, u32);
+
+    impl PartialOrd<Self> for Val {
+        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+            Some(self.cmp(other))
+        }
+    }
+
+    impl Ord for Val {
+        fn cmp(&self, other: &Self) -> Ordering {
+            self.0.cmp(&other.0)
+        }
+    }
+
+    assert_eq!(None::<u32>.iter().min_set(), Vec::<&u32>::new());
+    assert_eq!(None::<u32>.iter().max_set(), Vec::<&u32>::new());
+
+    assert_eq!(Some(1u32).iter().min_set(), vec![&1]);
+    assert_eq!(Some(1u32).iter().max_set(), vec![&1]);
+
+    let data = [Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)];
+
+    let min_set = data.iter().min_set();
+    assert_eq!(min_set, vec![&Val(0, 1), &Val(0, 2)]);
+
+    let min_set_by_key = data.iter().min_set_by_key(|v| v.1);
+    assert_eq!(min_set_by_key, vec![&Val(2, 0), &Val(1, 0)]);
+
+    let min_set_by = data.iter().min_set_by(|x, y| x.1.cmp(&y.1));
+    assert_eq!(min_set_by, vec![&Val(2, 0), &Val(1, 0)]);
+
+    let max_set = data.iter().max_set();
+    assert_eq!(max_set, vec![&Val(2, 0), &Val(2, 1)]);
+
+    let max_set_by_key = data.iter().max_set_by_key(|v| v.1);
+    assert_eq!(max_set_by_key, vec![&Val(0, 2)]);
+
+    let max_set_by = data.iter().max_set_by(|x, y| x.1.cmp(&y.1));
+    assert_eq!(max_set_by, vec![&Val(0, 2)]);
+}
+
+#[test]
+fn minmax() {
+    use crate::it::MinMaxResult;
+    use std::cmp::Ordering;
+
+    // A peculiar type: Equality compares both tuple items, but ordering only the
+    // first item.  This is so we can check the stability property easily.
+    #[derive(Clone, Debug, PartialEq, Eq)]
+    struct Val(u32, u32);
+
+    impl PartialOrd<Self> for Val {
+        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+            Some(self.cmp(other))
+        }
+    }
+
+    impl Ord for Val {
+        fn cmp(&self, other: &Self) -> Ordering {
+            self.0.cmp(&other.0)
+        }
+    }
+
+    assert_eq!(
+        None::<Option<u32>>.iter().minmax(),
+        MinMaxResult::NoElements
+    );
+
+    assert_eq!(Some(1u32).iter().minmax(), MinMaxResult::OneElement(&1));
+
+    let data = [Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)];
+
+    let minmax = data.iter().minmax();
+    assert_eq!(minmax, MinMaxResult::MinMax(&Val(0, 1), &Val(2, 1)));
+
+    let (min, max) = data.iter().minmax_by_key(|v| v.1).into_option().unwrap();
+    assert_eq!(min, &Val(2, 0));
+    assert_eq!(max, &Val(0, 2));
+
+    let (min, max) = data
+        .iter()
+        .minmax_by(|x, y| x.1.cmp(&y.1))
+        .into_option()
+        .unwrap();
+    assert_eq!(min, &Val(2, 0));
+    assert_eq!(max, &Val(0, 2));
+}
+
+#[test]
+fn format() {
+    let data = [0, 1, 2, 3];
+    let ans1 = "0, 1, 2, 3";
+    let ans2 = "0--1--2--3";
+
+    let t1 = format!("{}", data.iter().format(", "));
+    assert_eq!(t1, ans1);
+    let t2 = format!("{:?}", data.iter().format("--"));
+    assert_eq!(t2, ans2);
+
+    let dataf = [1.1, 5.71828, -22.];
+    let t3 = format!("{:.2e}", dataf.iter().format(", "));
+    assert_eq!(t3, "1.10e0, 5.72e0, -2.20e1");
+}
+
+#[test]
+fn while_some() {
+    let ns = (1..10)
+        .map(|x| if x % 5 != 0 { Some(x) } else { None })
+        .while_some();
+    it::assert_equal(ns, vec![1, 2, 3, 4]);
+}
+
+#[test]
+fn fold_while() {
+    let mut iterations = 0;
+    let vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+    let sum = vec
+        .into_iter()
+        .fold_while(0, |acc, item| {
+            iterations += 1;
+            let new_sum = acc + item;
+            if new_sum <= 20 {
+                FoldWhile::Continue(new_sum)
+            } else {
+                FoldWhile::Done(acc)
+            }
+        })
+        .into_inner();
+    assert_eq!(iterations, 6);
+    assert_eq!(sum, 15);
+}
+
+#[test]
+fn tree_reduce() {
+    let x = [
+        "",
+        "0",
+        "0 1 x",
+        "0 1 x 2 x",
+        "0 1 x 2 3 x x",
+        "0 1 x 2 3 x x 4 x",
+        "0 1 x 2 3 x x 4 5 x x",
+        "0 1 x 2 3 x x 4 5 x 6 x x",
+        "0 1 x 2 3 x x 4 5 x 6 7 x x x",
+        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 x",
+        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x x",
+        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 x x",
+        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x x",
+        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 x x",
+        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x x x",
+        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x 14 x x x",
+        "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x 14 15 x x x x",
+    ];
+    for (i, &s) in x.iter().enumerate() {
+        let expected = if s.is_empty() {
+            None
+        } else {
+            Some(s.to_string())
+        };
+        let num_strings = (0..i).map(|x| x.to_string());
+        let actual = num_strings.tree_reduce(|a, b| format!("{} {} x", a, b));
+        assert_eq!(actual, expected);
+    }
+}
+
+#[test]
+fn exactly_one_question_mark_syntax_works() {
+    exactly_one_question_mark_return().unwrap_err();
+}
+
+fn exactly_one_question_mark_return() -> Result<(), ExactlyOneError<std::slice::Iter<'static, ()>>>
+{
+    [].iter().exactly_one()?;
+    Ok(())
+}
+
+#[test]
+fn multiunzip() {
+    let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = [(0, 1, 2), (3, 4, 5), (6, 7, 8)]
+        .iter()
+        .cloned()
+        .multiunzip();
+    assert_eq!((a, b, c), (vec![0, 3, 6], vec![1, 4, 7], vec![2, 5, 8]));
+    let (): () = [(), (), ()].iter().cloned().multiunzip();
+    #[allow(clippy::type_complexity)]
+    let t: (
+        Vec<_>,
+        Vec<_>,
+        Vec<_>,
+        Vec<_>,
+        Vec<_>,
+        Vec<_>,
+        Vec<_>,
+        Vec<_>,
+        Vec<_>,
+        Vec<_>,
+        Vec<_>,
+        Vec<_>,
+    ) = [(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)]
+        .iter()
+        .cloned()
+        .multiunzip();
+    assert_eq!(
+        t,
+        (
+            vec![0],
+            vec![1],
+            vec![2],
+            vec![3],
+            vec![4],
+            vec![5],
+            vec![6],
+            vec![7],
+            vec![8],
+            vec![9],
+            vec![10],
+            vec![11]
+        )
+    );
+}
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/tuples.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/tuples.rs
similarity index 100%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/tuples.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/tuples.rs
diff --git a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/zip.rs b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/zip.rs
similarity index 71%
rename from third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/zip.rs
rename to third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/zip.rs
index 75157d3..2f192b5 100644
--- a/third_party/rust/chromium_crates_io/vendor/itertools-v0_11/tests/zip.rs
+++ b/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/tests/zip.rs
@@ -1,17 +1,14 @@
-use itertools::Itertools;
-use itertools::EitherOrBoth::{Both, Left, Right};
-use itertools::free::zip_eq;
 use itertools::multizip;
+use itertools::EitherOrBoth::{Both, Left, Right};
+use itertools::Itertools;
 
 #[test]
 fn zip_longest_fused() {
     let a = [Some(1), None, Some(3), Some(4)];
     let b = [1, 2, 3];
 
-    let unfused = a.iter().batching(|it| *it.next().unwrap())
-        .zip_longest(b.iter().cloned());
-    itertools::assert_equal(unfused,
-                       vec![Both(1, 1), Right(2), Right(3)]);
+    let unfused = a.iter().batching(|it| *it.next().unwrap()).zip_longest(b.iter().cloned());
+    itertools::assert_equal(unfused, vec![Both(1, 1), Right(2), Right(3)]);
 }
 
 #[test]
@@ -20,7 +17,7 @@
     let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
     let v2 = &[10, 11, 12];
 
-    assert_eq!(c.zip_longest(v.iter()).size_hint(), (std::usize::MAX, None));
+    assert_eq!(c.zip_longest(v.iter()).size_hint(), (usize::MAX, None));
 
     assert_eq!(v.iter().zip_longest(v2.iter()).size_hint(), (10, Some(10)));
 }
@@ -54,24 +51,3 @@
     assert_eq!(it.next_back(), Some((1, 1)));
     assert_eq!(it.next_back(), None);
 }
-
-
-#[should_panic]
-#[test]
-fn zip_eq_panic1()
-{
-    let a = [1, 2];
-    let b = [1, 2, 3];
-
-    zip_eq(&a, &b).count();
-}
-
-#[should_panic]
-#[test]
-fn zip_eq_panic2()
-{
-    let a: [i32; 0] = [];
-    let b = [1, 2, 3];
-
-    zip_eq(&a, &b).count();
-}
diff --git a/third_party/rust/itertools/v0_11/BUILD.gn b/third_party/rust/itertools/v0_14/BUILD.gn
similarity index 79%
rename from third_party/rust/itertools/v0_11/BUILD.gn
rename to third_party/rust/itertools/v0_14/BUILD.gn
index b4e7d57..f2cb9a43 100644
--- a/third_party/rust/itertools/v0_11/BUILD.gn
+++ b/third_party/rust/itertools/v0_14/BUILD.gn
@@ -10,59 +10,61 @@
 
 cargo_crate("lib") {
   crate_name = "itertools"
-  epoch = "0.11"
+  epoch = "0.14"
   crate_type = "rlib"
   crate_root =
-      "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lib.rs"
+      "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lib.rs"
   sources = [
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/coalesce.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/map.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/mod.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/adaptors/multi_product.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/combinations_with_replacement.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/concat_impl.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/cons_tuples_impl.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/diff.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/duplicates_impl.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/either_or_both.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/exactly_one_err.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/extrema_set.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/flatten_ok.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/format.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/free.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/group_map.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/groupbylazy.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/grouping_map.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/impl_macros.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/intersperse.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/k_smallest.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/kmerge_impl.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lazy_buffer.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/lib.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/merge_join.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/minmax.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/multipeek_impl.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/pad_tail.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peek_nth.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/peeking_take_while.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/permutations.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/powerset.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/process_results_impl.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/put_back_n_impl.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/rciter_impl.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/repeatn.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/size_hint.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/sources.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/take_while_inclusive.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tee.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/tuple_impl.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unique_impl.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/unziptuple.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/with_position.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_eq_impl.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/zip_longest.rs",
-    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_11/src/ziptuple.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/coalesce.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/map.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/mod.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/adaptors/multi_product.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/combinations_with_replacement.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/concat_impl.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/cons_tuples_impl.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/diff.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/duplicates_impl.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/either_or_both.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/exactly_one_err.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/extrema_set.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/flatten_ok.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/format.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/free.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/group_map.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/groupbylazy.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/grouping_map.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/impl_macros.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/intersperse.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/iter_index.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/k_smallest.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/kmerge_impl.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lazy_buffer.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/lib.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/merge_join.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/minmax.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/multipeek_impl.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/next_array.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/pad_tail.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peek_nth.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/peeking_take_while.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/permutations.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/powerset.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/process_results_impl.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/put_back_n_impl.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/rciter_impl.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/repeatn.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/size_hint.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/sources.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/take_while_inclusive.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tee.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/tuple_impl.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unique_impl.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/unziptuple.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/with_position.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_eq_impl.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/zip_longest.rs",
+    "//third_party/rust/chromium_crates_io/vendor/itertools-v0_14/src/ziptuple.rs",
   ]
   inputs = []
 
@@ -73,7 +75,7 @@
   cargo_pkg_description =
       "Extra iterator adaptors, iterator methods, free functions, and macros."
   cargo_pkg_repository = "https://github.com/rust-itertools/itertools"
-  cargo_pkg_version = "0.11.0"
+  cargo_pkg_version = "0.14.0"
 
   allow_unsafe = true
 
diff --git a/third_party/rust/itertools/v0_11/README.chromium b/third_party/rust/itertools/v0_14/README.chromium
similarity index 77%
rename from third_party/rust/itertools/v0_11/README.chromium
rename to third_party/rust/itertools/v0_14/README.chromium
index 68a08d5a..f5ce0ef 100644
--- a/third_party/rust/itertools/v0_11/README.chromium
+++ b/third_party/rust/itertools/v0_14/README.chromium
@@ -1,10 +1,10 @@
 Name: itertools
 URL: https://crates.io/crates/itertools
-Version: 0.11.0
-Revision: 62a6401afd6d45e1c2aea94c05cb5c70076b2ca4
+Version: 0.14.0
+Revision: a015a6831525ee1637df747d3f530a627d9741bf
 Update Mechanism: Manual (https://crbug.com/449898466)
 License: Apache-2.0
-License File: //third_party/rust/chromium_crates_io/vendor/itertools-v0_11/LICENSE-APACHE
+License File: //third_party/rust/chromium_crates_io/vendor/itertools-v0_14/LICENSE-APACHE
 Shipped: no
 Security Critical: no
 
diff --git a/third_party/rust/prost_derive/v0_14/BUILD.gn b/third_party/rust/prost_derive/v0_14/BUILD.gn
index c842797..dfa6b3ac 100644
--- a/third_party/rust/prost_derive/v0_14/BUILD.gn
+++ b/third_party/rust/prost_derive/v0_14/BUILD.gn
@@ -36,7 +36,7 @@
 
   deps = [
     "//third_party/rust/anyhow/v1:lib",
-    "//third_party/rust/itertools/v0_11:lib",
+    "//third_party/rust/itertools/v0_14:lib",
     "//third_party/rust/proc_macro2/v1:lib",
     "//third_party/rust/quote/v1:lib",
     "//third_party/rust/syn/v2:lib",
diff --git a/third_party/spirv-tools/src b/third_party/spirv-tools/src
index 47eef7b..6e9d60d 160000
--- a/third_party/spirv-tools/src
+++ b/third_party/spirv-tools/src
@@ -1 +1 @@
-Subproject commit 47eef7be333fe709795fd695ba4f8e58c70a0cf0
+Subproject commit 6e9d60d80905f561858c2b1234bbb82bb529c044
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index d61243d..c29f9df 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit d61243d1b0837231283e456a908637ead7917dd9
+Subproject commit c29f9df0edc12f925253a95526823c15ed249d6a
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src
index 562af80..4fc964e 160000
--- a/third_party/vulkan-validation-layers/src
+++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@
-Subproject commit 562af80edf57126f333036a707da8cb2f9c1522e
+Subproject commit 4fc964ebd2fcad5d65372512bd483767f08dc345
diff --git a/tools/metrics/histograms/metadata/android/enums.xml b/tools/metrics/histograms/metadata/android/enums.xml
index 83e2a3cc..3528cab 100644
--- a/tools/metrics/histograms/metadata/android/enums.xml
+++ b/tools/metrics/histograms/metadata/android/enums.xml
@@ -738,6 +738,8 @@
   <int value="179" label="WebSettingsCompat.setMaxPrerenders"/>
   <int value="180" label="WebSettingsCompat.setMaxPrefetches"/>
   <int value="181" label="WebSettingsCompat.setPrefetchTtlSeconds"/>
+  <int value="182" label="BackForwardCacheSettings.getKeepForwardEntries"/>
+  <int value="183" label="BackForwardCacheSettings.setKeepForwardEntries"/>
 <!-- LINT.ThenChange(/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebViewChromiumFactory.java:ApiCall) -->
 
 </enum>
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml
index affd61e..0741dcd 100644
--- a/tools/metrics/histograms/metadata/apps/histograms.xml
+++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -790,13 +790,16 @@
 </histogram>
 
 <histogram name="Apps.AppList.GeminiSearchBoxIcon" enum="SearchBoxIconEvent"
-    expires_after="2026-03-01">
+    expires_after="2027-03-01">
   <owner>yawano@google.com</owner>
   <owner>assistive-eng@google.com</owner>
   <summary>
     Impression or click events for a Gemini search box icon. This metric was
     previously recorded as Assistant.NewEntryPoint.Eligibility and
     Assistant.NewEntryPoint.Launcher before M138.
+
+    Warning: this histogram was expired from 2026-03-01 to 2026-03-10; data may
+    be missing.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/assistant/histograms.xml b/tools/metrics/histograms/metadata/assistant/histograms.xml
index 4b4c5c4..3bd5a42 100644
--- a/tools/metrics/histograms/metadata/assistant/histograms.xml
+++ b/tools/metrics/histograms/metadata/assistant/histograms.xml
@@ -23,7 +23,7 @@
 <histograms>
 
 <histogram name="Assistant.NewEntryPoint.Eligibility"
-    enum="AssistantNewEntryPointEligibility" expires_after="2026-04-12">
+    enum="AssistantNewEntryPointEligibility" expires_after="2027-04-12">
   <owner>yawano@google.com</owner>
   <owner>assistive-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 364111b..a4bb840 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -961,6 +961,20 @@
   </summary>
 </histogram>
 
+<histogram name="Autofill.Actor.AutofillAttentionDialogsPerTask"
+    units="dialogs" expires_after="2026-11-30">
+  <owner>battre@chromium.org</owner>
+  <owner>jkeitel@google.com</owner>
+  <owner>slobodan@chromium.org</owner>
+  <owner>chrome-autofill-alerts@google.com</owner>
+  <summary>
+    Records the number of autofill attention dialogs presented in the UI to the
+    user for a given task. Recorded in `ActionTrackerForMetrics`'s destructor.
+    The metric is incremented by one for every UI dialog (even if it contains
+    more than 1 suggestion dropdowns).
+  </summary>
+</histogram>
+
 <histogram name="Autofill.Actor.AutofillSuggestionAccepted.RecordType"
     enum="ActorFormFillingRequestedData" expires_after="2026-11-30">
   <owner>battre@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index a95cf6e..657f776 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -4365,6 +4365,19 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.ModelContext.DelayedToolCount" units="count"
+    expires_after="2027-03-10">
+  <owner>mfoltz@chromium.org</owner>
+  <owner>agentic-web-platform-team@google.com</owner>
+  <summary>
+    Records the total number of tools registered with navigator.modelContext
+    after some delay after the first registration. This is recorded once per top
+    level document. It provides an approximate number of tool registrations,
+    assuming that declarative tool registrations happen on the main document
+    load, followed by some imperative tool registrations in script.
+  </summary>
+</histogram>
+
 <histogram name="Blink.Network.DataUrlLength{Type}" units="characters"
     expires_after="2026-06-25">
   <owner>nidhijaju@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/glic/enums.xml b/tools/metrics/histograms/metadata/glic/enums.xml
index 6bd6fe8..3b28d77 100644
--- a/tools/metrics/histograms/metadata/glic/enums.xml
+++ b/tools/metrics/histograms/metadata/glic/enums.xml
@@ -559,6 +559,7 @@
   <int value="19" label="Automatically opened for a PDF"/>
   <int value="20" label="From capture region hotkey"/>
   <int value="21" label="In-product help"/>
+  <int value="22" label="User clicked an anchored contextual cue chip"/>
 </enum>
 
 <!-- LINT.ThenChange(//chrome/browser/glic/host/glic.mojom:InvocationSource) -->
@@ -753,6 +754,10 @@
   <int value="86" label="IPH, attached, audio mode."/>
   <int value="87" label="IPH, detached, text mode."/>
   <int value="88" label="IPH, detached, audio mode."/>
+  <int value="89" label="Anchored contextual cue, attached, text mode."/>
+  <int value="90" label="Anchored contextual cue, attached, audio mode."/>
+  <int value="91" label="Anchored contextual cue, detached, text mode."/>
+  <int value="92" label="Anchored contextual cue, detached, audio mode."/>
 </enum>
 
 <!-- LINT.ThenChange(//chrome/browser/glic/glic_metrics.h:ResponseSegmentation) -->
diff --git a/tools/metrics/histograms/metadata/optimization/histograms.xml b/tools/metrics/histograms/metadata/optimization/histograms.xml
index e99cd86..8de0ebf 100644
--- a/tools/metrics/histograms/metadata/optimization/histograms.xml
+++ b/tools/metrics/histograms/metadata/optimization/histograms.xml
@@ -1796,6 +1796,28 @@
 </histogram>
 
 <histogram
+    name="OptimizationGuide.PageContentAnnotations.CategoryClassifier.EducationScore"
+    units="%" expires_after="2026-09-10">
+  <owner>zekunjiang@google.com</owner>
+  <owner>chrome-intelligence-core@google.com</owner>
+  <summary>
+    Records the score (0-100) produced by the On Device Category Classifier for
+    the Education category. Recorded once for every page load.
+  </summary>
+</histogram>
+
+<histogram
+    name="OptimizationGuide.PageContentAnnotations.CategoryClassifier.ShoppingScore"
+    units="%" expires_after="2026-09-10">
+  <owner>zekunjiang@google.com</owner>
+  <owner>chrome-intelligence-core@google.com</owner>
+  <summary>
+    Records the score (0-100) produced by the On Device Category Classifier for
+    the Shopping category. Recorded once for every page load.
+  </summary>
+</histogram>
+
+<histogram
     name="OptimizationGuide.PageContentAnnotations.GoogleSearchMetadataExtracted"
     enum="Boolean" expires_after="2026-03-29">
   <owner>sophiechang@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/page/enums.xml b/tools/metrics/histograms/metadata/page/enums.xml
index dd3aeeb51..3ef7ef3 100644
--- a/tools/metrics/histograms/metadata/page/enums.xml
+++ b/tools/metrics/histograms/metadata/page/enums.xml
@@ -302,6 +302,7 @@
   <int value="39" label="RecordReplay"/>
   <int value="40" label="Indigo"/>
   <int value="41" label="Federation"/>
+  <int value="42" label="Glic"/>
 </enum>
 
 <!-- LINT.ThenChange(//chrome/browser/ui/page_action/page_action_icon_type.h:PageActionIconType) -->
diff --git a/tools/metrics/histograms/metadata/stability/enums.xml b/tools/metrics/histograms/metadata/stability/enums.xml
index de97da0..3ea66989 100644
--- a/tools/metrics/histograms/metadata/stability/enums.xml
+++ b/tools/metrics/histograms/metadata/stability/enums.xml
@@ -501,6 +501,7 @@
   <int value="333" label="RFH_NEW_ISOLATED_WEB_APP_PERMISSION_POLICIES"/>
   <int value="334" label="RFH_CREATE_NEW_WINDOW_INVALID_DISPOSITION"/>
   <int value="335" label="RFH_CREATE_NEW_WINDOW_FROM_SANDBOXED_FRAME"/>
+  <int value="336" label="RFH_MODAL_DIALOG_FROM_SANDBOXED_FRAME"/>
 </enum>
 
 <enum name="BadMessageReasonExtensions">
diff --git a/ui/android/java/src/org/chromium/ui/util/ClickWithMetaStateCallback.java b/ui/android/java/src/org/chromium/ui/util/ClickWithMetaStateCallback.java
index 3004cbb..71ba924 100644
--- a/ui/android/java/src/org/chromium/ui/util/ClickWithMetaStateCallback.java
+++ b/ui/android/java/src/org/chromium/ui/util/ClickWithMetaStateCallback.java
@@ -10,6 +10,11 @@
 @NullMarked
 @FunctionalInterface
 public interface ClickWithMetaStateCallback {
-    /** Called when a click occurs. {@param metaState} is the meta key state at the time. */
-    void onClickWithMeta(int metaState);
+    /**
+     * Called when a click occurs.
+     *
+     * @param metaState The meta key state at the time.
+     * @param buttonState The mouse button state.
+     */
+    void onClickWithMeta(int metaState, int buttonState);
 }
diff --git a/ui/android/java/src/org/chromium/ui/widget/ChromeImageButton.java b/ui/android/java/src/org/chromium/ui/widget/ChromeImageButton.java
index a206d74..a87bce1 100644
--- a/ui/android/java/src/org/chromium/ui/widget/ChromeImageButton.java
+++ b/ui/android/java/src/org/chromium/ui/widget/ChromeImageButton.java
@@ -8,6 +8,7 @@
 import android.content.Context;
 import android.graphics.PorterDuff.Mode;
 import android.util.AttributeSet;
+import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
@@ -28,6 +29,11 @@
     // Used to keep track of meta state so that things like shift+click and ctrl+click can work.
     private int mLastEventMetaState;
 
+    // Used to keep track of button state so that things like middle click can work.
+    private int mLastEventButtonState;
+
+    private @Nullable ClickWithMetaStateCallback mClickCallback;
+
     public ChromeImageButton(Context context) {
         super(context);
     }
@@ -62,32 +68,68 @@
         // 5) The OnClickListener is called, which calls the callback set in setClickCallback and
         // provides the meta state saved in (3).
         mLastEventMetaState = event.getMetaState();
+        mLastEventButtonState = 0;
         return super.onKeyUp(keyCode, event);
     }
 
     @Override
     @SuppressLint("ClickableViewAccessibility")
     public boolean onTouchEvent(MotionEvent event) {
-        // NOTE: Update `mLastEventMetaState` in anticipation of a potential click.
-        // This handles button activations with touch/mouse. The way it works is as follows:
+        // NOTE: Update `mLastEventMetaState` and `mLastEventButtonState` in anticipation of a
+        // potential click. This handles button activations with touch/mouse. The way it works is as
+        // follows:
         // 1) User clicks/touches button.
         // 2) This method is triggered.
-        // 3) The current meta state is saved.
+        // 3) The current meta state and button state are saved.
         // 4) The button is clicked because user clicked button in (1).
         // 5) The OnClickListener is called, which calls the callback set in setClickCallback and
-        // provides the meta state saved in (3).
+        // provides the meta state and button state saved in (3).
         mLastEventMetaState = event.getMetaState();
+
+        int action = event.getActionMasked();
+        if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_BUTTON_PRESS) {
+            mLastEventButtonState = event.getButtonState();
+        }
+
+        // Consume middle-clicks to avoid triggering the OnClickListener. Without this, a
+        // middle-click (e.g. from a mouse) would trigger both the middle-click specific action
+        // (handled in onGenericMotionEvent) and the standard click action (triggered by
+        // super.onTouchEvent), leading to duplicate or conflicting behaviors.
+        if ((mLastEventButtonState & MotionEvent.BUTTON_TERTIARY) != 0) {
+            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+                mLastEventButtonState = 0;
+            }
+            return true;
+        }
+
         return super.onTouchEvent(event);
     }
 
+    @Override
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0
+                && event.getActionMasked() == MotionEvent.ACTION_BUTTON_PRESS
+                && event.getActionButton() == MotionEvent.BUTTON_TERTIARY) {
+            if (mClickCallback != null) {
+                mClickCallback.onClickWithMeta(event.getMetaState(), MotionEvent.BUTTON_TERTIARY);
+            }
+            return true;
+        }
+        return super.onGenericMotionEvent(event);
+    }
+
     /**
      * Sets the callback to notify of button click events. The callback is provided the meta state
-     * of the most recent key/touch event.
+     * and button state of the most recent key/touch event.
      *
      * @param callback the callback to notify.
      */
     public void setClickCallback(@Nullable ClickWithMetaStateCallback callback) {
+        mClickCallback = callback;
         setOnClickListener(
-                callback != null ? (v) -> callback.onClickWithMeta(mLastEventMetaState) : null);
+                callback != null
+                        ? (v) ->
+                                callback.onClickWithMeta(mLastEventMetaState, mLastEventButtonState)
+                        : null);
     }
 }
diff --git a/ui/android/window_android.h b/ui/android/window_android.h
index f532fba0..114a82e 100644
--- a/ui/android/window_android.h
+++ b/ui/android/window_android.h
@@ -235,4 +235,19 @@
 
 }  // namespace ui
 
+namespace jni_zero {
+template <>
+inline ui::WindowAndroid* FromJniType<ui::WindowAndroid*>(
+    JNIEnv* env,
+    const JavaRef<jobject>& j_obj) {
+  return ui::WindowAndroid::FromJavaWindowAndroid(j_obj);
+}
+
+template <>
+inline ScopedJavaLocalRef<jobject> ToJniType(JNIEnv* env,
+                                             ui::WindowAndroid* obj) {
+  return obj->GetJavaObject();
+}
+}  // namespace jni_zero
+
 #endif  // UI_ANDROID_WINDOW_ANDROID_H_
diff --git a/ui/views/test/mock_activation_controller.h b/ui/views/test/mock_activation_controller.h
index 01364eb4..de23bf3 100644
--- a/ui/views/test/mock_activation_controller.h
+++ b/ui/views/test/mock_activation_controller.h
@@ -10,9 +10,14 @@
 
 #include "base/functional/callback.h"
 #include "base/memory/weak_ptr.h"
+#include "ui/views/buildflags.h"
 #include "ui/views/widget/widget_activation_delegate.h"
 #include "ui/views/widget/widget_observer.h"
 
+// Only desktop aura platforms or macOS needs the activation emulation.
+#if BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
+#define USE_MOCK_ACTIVATION_CONTROLLER 1
+
 namespace views {
 class Widget;
 namespace test {
@@ -63,4 +68,6 @@
 }  // namespace test
 }  // namespace views
 
+#endif  // BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_MAC)
+
 #endif  // UI_VIEWS_TEST_MOCK_ACTIVATION_CONTROLLER_H_
diff --git a/ui/views/window/native_frame_view.cc b/ui/views/window/native_frame_view.cc
index 8c3d80c..4f5033f 100644
--- a/ui/views/window/native_frame_view.cc
+++ b/ui/views/window/native_frame_view.cc
@@ -46,6 +46,12 @@
 }
 
 int NativeFrameView::NonClientHitTest(const gfx::Point& point) {
+  if (!non_client_hit_test_callback_.is_null()) {
+    if (auto result = non_client_hit_test_callback_.Run(point); result) {
+      return result.value();
+    }
+  }
+
   return widget_->client_view()->NonClientHitTest(point);
 }
 
diff --git a/ui/webui/resources/cr_components/composebox/composebox.html.ts b/ui/webui/resources/cr_components/composebox/composebox.html.ts
index 0cd2200..0896249 100644
--- a/ui/webui/resources/cr_components/composebox/composebox.html.ts
+++ b/ui/webui/resources/cr_components/composebox/composebox.html.ts
@@ -176,14 +176,16 @@
        button enabled/disabled state. -->
     ${!this.searchboxNextEnabled ? getSubmitButtonHtml.bind(this)() : ''}
   </div>
-  <cr-composebox-voice-search id="voiceSearch"
-      @voice-search-cancel="${this.onVoiceSearchCancel_}"
-      @voice-search-final-result="${this.onVoiceSearchFinalResult_}"
-      @voice-search-error="${this.onVoiceSearchError_}"
-      @transcript-update="${this.onTranscriptUpdate_}"
-      @speech-received="${this.onSpeechReceived_}"
-      exportparts="voice-close-button">
-  </cr-composebox-voice-search>
+  ${this.shouldShowVoiceSearch_() ? html`
+    <cr-composebox-voice-search id="voiceSearch"
+        @voice-search-cancel="${this.onVoiceSearchCancel_}"
+        @voice-search-final-result="${this.onVoiceSearchFinalResult_}"
+        @voice-search-error="${this.onVoiceSearchError_}"
+        @transcript-update="${this.onTranscriptUpdate_}"
+        @speech-received="${this.onSpeechReceived_}"
+        exportparts="voice-close-button">
+    </cr-composebox-voice-search>
+  ` : ''}
   ${this.shouldShowSuggestionActivityLink_()
       && this.suggestionActivityEnabled ? html`
     <div id="suggestionActivity">
diff --git a/ui/webui/resources/cr_components/composebox/composebox.ts b/ui/webui/resources/cr_components/composebox/composebox.ts
index f6f62697..5878badd 100644
--- a/ui/webui/resources/cr_components/composebox/composebox.ts
+++ b/ui/webui/resources/cr_components/composebox/composebox.ts
@@ -50,6 +50,7 @@
 import type {ContextualEntrypointAndMenuElement} from './contextual_entrypoint_and_menu.js';
 import type {ErrorScrimElement} from './error_scrim.js';
 import type {ComposeboxFileCarouselElement} from './file_carousel.js';
+import {WindowProxy} from './window_proxy.js';
 
 export enum VoiceSearchAction {
   ACTIVATE = 0,
@@ -76,7 +77,6 @@
     fileInputs: ComposeboxFileInputsElement,
     matches: ComposeboxDropdownElement,
     errorScrim: ErrorScrimElement,
-    voiceSearch: ComposeboxVoiceSearchElement,
     caret: HTMLInputElement,
     mirror: HTMLDivElement,
   };
@@ -907,8 +907,11 @@
 
   protected shouldShowVoiceSearch_(): boolean {
     const isExpanded = this.showDropdown_ || this.files_.size > 0;
-    return isExpanded ? this.showVoiceSearchInExpandedComposebox_ :
-                        this.showVoiceSearchInSteadyComposebox_;
+    const isFeatureEnabled = isExpanded ?
+        this.showVoiceSearchInExpandedComposebox_ :
+        this.showVoiceSearchInSteadyComposebox_;
+    return isFeatureEnabled &&
+        WindowProxy.getInstance().hasWebkitSpeechRecognition();
   }
 
   protected shouldShowVoiceSearchAnimation_(): boolean {
@@ -1288,7 +1291,9 @@
     this.fire('voice-search-action', {value: VoiceSearchAction.ACTIVATE});
     // For contextual tasks composebox voice metrics.
     this.fire('composebox-voice-search-start');
-    this.$.voiceSearch.start();
+    this.shadowRoot
+        .querySelector<ComposeboxVoiceSearchElement>(
+            'cr-composebox-voice-search')!.start();
   }
 
   protected onVoiceSearchCancel_(e: CustomEvent<boolean>) {
diff --git a/ui/webui/resources/cr_components/composebox/composebox_tool_chips.html.ts b/ui/webui/resources/cr_components/composebox/composebox_tool_chips.html.ts
index 8e01b32..01e0674 100644
--- a/ui/webui/resources/cr_components/composebox/composebox_tool_chips.html.ts
+++ b/ui/webui/resources/cr_components/composebox/composebox_tool_chips.html.ts
@@ -9,8 +9,9 @@
 
 export function getHtml(this: ComposeboxElement) {
   // clang-format off
+  // TODO(crbug.com/491669968): Clean up redundant `activeToolMode_`.
   return html`
-${this.activeToolMode_ === ComposeboxToolMode.kDeepSearch ? html`
+${this.inputState_?.activeTool === ComposeboxToolMode.kDeepSearch ? html`
   <cr-composebox-tool-chip
       id="deepSearchChip"
       exportparts="tool-chip-label"
@@ -24,7 +25,7 @@
       @click="${this.onDeepSearchClick_}">
   </cr-composebox-tool-chip>
 ` : ''}
-${this.activeToolMode_ === ComposeboxToolMode.kImageGen ? html`
+${this.inputState_?.activeTool === ComposeboxToolMode.kImageGen ? html`
   <cr-composebox-tool-chip
       id="nanoBananaChip"
       exportparts="tool-chip-label"
@@ -38,7 +39,7 @@
       @click="${this.onCreateImageClick_}">
   </cr-composebox-tool-chip>
 ` : ''}
-${this.activeToolMode_ === ComposeboxToolMode.kCanvas ? html`
+${this.inputState_?.activeTool === ComposeboxToolMode.kCanvas ? html`
   <cr-composebox-tool-chip
       id="canvasChip"
       exportparts="tool-chip-label"
diff --git a/ui/webui/resources/cr_components/composebox/contextual_action_menu.css b/ui/webui/resources/cr_components/composebox/contextual_action_menu.css
index c9a55c4..6f4616b8 100644
--- a/ui/webui/resources/cr_components/composebox/contextual_action_menu.css
+++ b/ui/webui/resources/cr_components/composebox/contextual_action_menu.css
@@ -82,11 +82,13 @@
 }
 
 .dropdown-item:hover {
-  background-color: var(--cr-hover-background-color);
+  background-color: var(--color-context-menu-hover-background,
+      var(--cr-hover-background-color));
 }
 
 .dropdown-item:focus {
-  background-color: var(--cr-hover-background-color);
+  background-color: var(--color-context-menu-hover-background,
+      var(--cr-hover-background-color));
   outline: none;
 }
 
@@ -131,11 +133,14 @@
 }
 
 #multi-tab-add {
-  color: var(--color-composebox-file-chip-text);
+  color: var(--color-composebox-context-menu-text,
+      var(--color-composebox-file-chip-text));
 }
 
 #multi-tab-check {
-  color: var(--color-composebox-tab-selector-button-selected);
+  color: var(--color-composebox-context-menu-checkbox-override,
+      /* Default defined in colors.css: */
+      var(--color-composebox-tab-selector-button-selected));
 }
 
 #model-check {
diff --git a/ui/webui/resources/cr_components/composebox/window_proxy.ts b/ui/webui/resources/cr_components/composebox/window_proxy.ts
index 79565a5..341c5728 100644
--- a/ui/webui/resources/cr_components/composebox/window_proxy.ts
+++ b/ui/webui/resources/cr_components/composebox/window_proxy.ts
@@ -31,4 +31,8 @@
   matchMedia(query: string): MediaQueryList {
     return window.matchMedia(query);
   }
+
+  hasWebkitSpeechRecognition(): boolean {
+    return 'webkitSpeechRecognition' in window;
+  }
 }
diff --git a/ui/webui/resources/tools/eslint/lit_element_structure.js b/ui/webui/resources/tools/eslint/lit_element_structure.js
index dadfd84..783cf16 100644
--- a/ui/webui/resources/tools/eslint/lit_element_structure.js
+++ b/ui/webui/resources/tools/eslint/lit_element_structure.js
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import assert from 'node:assert';
+import path from 'node:path';
 
 import {ESLintUtils} from '../../../../../third_party/node/node_modules/@typescript-eslint/utils/dist/index.js';
 
@@ -67,16 +68,6 @@
       return;
     }
 
-    if (!node.id.name.endsWith('Element')) {
-      this.context.report({
-        node: node,
-        messageId: 'incorrectClassName',
-        data: {
-          className: node.id.name,
-        },
-      });
-    }
-
     this.node = node;
   }
 
@@ -275,6 +266,65 @@
     });
   }
 
+  runConsistentClassDomNameCheck() {
+    if (!this.isLitElement) {
+      return;
+    }
+
+    if (!this.node.id.name.endsWith('Element')) {
+      this.context.report({
+        node: this.node,
+        messageId: 'incorrectClassNameSuffix',
+        data: {
+          className: this.node.id.name,
+        },
+      });
+      return;
+    }
+
+    if (this.domName === '') {
+      // Handle case where the element is used as a super class for other
+      // elements and has no DOM name.
+      return;
+    }
+
+    if (this.domName.endsWith('-element')) {
+      this.context.report({
+        node: this.node,
+        messageId: 'incorrectDomNameSuffix',
+        data: {
+          domName: this.domName,
+        },
+      });
+      return;
+    }
+
+    const domNameParts = this.domName.split('-');
+    const isClassNameConsistent = domNameParts.some((_, i) => {
+      const candidateSuffix =
+          dashCaseToCamelCase('-' + domNameParts.slice(i).join('-')) +
+          'Element';
+
+      // Allow a class name prefix that does not exist in the DOM name, only if
+      // all the DOM name parts are reflected in the class name.
+      return i === 0 ? this.node.id.name.endsWith(candidateSuffix) :
+                       candidateSuffix === this.node.id.name;
+    });
+
+    if (isClassNameConsistent) {
+      return;
+    }
+
+    this.context.report({
+      node: this.node,
+      messageId: 'inconsistentClassName',
+      data: {
+        className: this.node.id.name,
+        domName: this.domName,
+      },
+    });
+  }
+
   runMethodDefinitionOrderCheck() {
     if (!this.isLitElement || !this.node) {
       return;
@@ -319,8 +369,12 @@
           'Use this.fire(...) instead of this.dispatchEvent(new CustomEvent(...))..',
       useFireHelperWithEventName:
           'Use this.fire(...) instead of this.dispatchEvent(new CustomEvent(...)), for event \'{{eventName}}\'.',
-      incorrectClassName:
-          'CrLitElement subclass {{className}} should end with the \'Element\' suffix.',
+      incorrectClassNameSuffix:
+          'Class name \'{{className}}\' should end with the \'Element\' suffix.',
+      incorrectDomNameSuffix:
+          'DOM name \'{{domName}}\' should not end with the \'-element\' suffix.',
+      inconsistentClassName:
+          'Naming of class/dom pair {{className}} ↔ {{domName}} is inconsistent.',
       incorrectDollarSignNotation:
           'Use camelCase instead of dash-case for DOM ids, change this.$[\'{{dashCaseName}}\'] to this.$.{{camelCaseName}}.',
       incorrectMethodDefinitionOrder:
@@ -450,6 +504,7 @@
         currentClassInfo.runMissingStaticIsGetterCheck();
         currentClassInfo.runMissingSuperCallsCheck();
         currentClassInfo.runMethodDefinitionOrderCheck();
+        currentClassInfo.runConsistentClassDomNameCheck();
       },
       ['Program > TSModuleDeclaration[kind=global] > TSModuleBlock > TSInterfaceDeclaration[id.name="HTMLElementTagNameMap"] > TSInterfaceBody > TSPropertySignature'](
           node) {
diff --git a/ui/webui/resources/tools/eslint_ts_test.py b/ui/webui/resources/tools/eslint_ts_test.py
index b0856825..1c7db477 100755
--- a/ui/webui/resources/tools/eslint_ts_test.py
+++ b/ui/webui/resources/tools/eslint_ts_test.py
@@ -241,7 +241,9 @@
     self.assertTrue(_EXPECTED_STRING in str(context.exception))
 
     _EXPECTED_INCONSISTENT_METHOD_DEFINITION_ORDER_ERROR = "Inconsistent method definition order in class %(className)s. Expected %(expectedOrder)s, found %(actualOrder)s"
-    _EXPECTED_INCORRECT_CLASS_NAME_ERROR = 'CrLitElement subclass %(className)s should end with the \'Element\' suffix'
+    _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR = 'Class name \'%(className)s\' should end with the \'Element\' suffix'
+    _EXPECTED_INCONSISTENT_CLASS_NAME_ERROR = 'Naming of class/dom pair %(className)s ↔ %(domName)s is inconsistent'
+    _EXPECTED_INCORRECT_DOM_NAME_SUFFIX_ERROR = 'DOM name \'%(domName)s\' should not end with the \'-element\' suffix'
     _EXPECTED_INCORRECT_DOLLAR_SIGN_NOTATION_ERROR = 'Use camelCase instead of dash-case for DOM ids, change this.$[\'%(dashCaseName)s\'] to this.$.%(camelCaseName)s'
     _EXPECTED_MISSING_CUSTOM_ELEMENTS_DEFINE_ERROR = "Missing customElements.define(%(className)s.is, %(className)s) call"
     _EXPECTED_MISSING_STATIC_GET_IS_ERROR = "Missing 'static get is() {...}' for web component class %(className)s"
@@ -287,6 +289,9 @@
             'lifecycleMethods': ', '.join(super_call_required_methods),
         },
         # Case 1.6
+        _EXPECTED_INCORRECT_DOM_NAME_SUFFIX_ERROR % {
+            'domName': 'test-error6-element',
+        },
         _EXPECTED_INCONSISTENT_METHOD_DEFINITION_ORDER_ERROR % {
             'className':
                 'TestError6Element',
@@ -315,21 +320,26 @@
             'name': '_otherEvent',
         },
         # Case 1.7
-        _EXPECTED_INCORRECT_CLASS_NAME_ERROR % {
+        _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % {
             'className': 'TestError7ElementFoo',
         },
         # Case 1.8
-        _EXPECTED_INCORRECT_CLASS_NAME_ERROR % {
+        _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % {
             'className': 'TestError8ElementFoo',
         },
         # Case 1.9
-        _EXPECTED_INCORRECT_CLASS_NAME_ERROR % {
+        _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % {
             'className': 'TestError9ElementFoo',
         },
         # Case 1.10
-        _EXPECTED_INCORRECT_CLASS_NAME_ERROR % {
+        _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % {
             'className': 'TestError10ElementFoo',
         },
+        # Case 1.11
+        _EXPECTED_INCONSISTENT_CLASS_NAME_ERROR % {
+            'className': 'TestError11Element',
+            'domName': 'test-other-error11',
+        },
     ]
     for e in errors:
       self.assertTrue(
@@ -352,7 +362,7 @@
             'className': 'TestNoError1Element',
             'lifecycleMethods': ', '.join(super_call_required_methods)
         },
-        _EXPECTED_INCORRECT_CLASS_NAME_ERROR % {
+        _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % {
             'className': 'TestNoError1Element',
         },
         # Case 2.2
@@ -370,10 +380,13 @@
             'className': 'TestNoError2Element',
             'lifecycleMethods': ', '.join(super_call_required_methods),
         },
-        _EXPECTED_INCORRECT_CLASS_NAME_ERROR % {
+        _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % {
             'className': 'TestNoError2Element',
         },
         # Case 2.3
+        _EXPECTED_INCORRECT_DOM_NAME_SUFFIX_ERROR % {
+            'domName': 'test-no-error3',
+        },
         _EXPECTED_MISSING_STATIC_GET_IS_ERROR % {
             'className': 'TestNoError3Element',
         },
@@ -396,9 +409,13 @@
             'actualOrder':
                 '',
         },
-        _EXPECTED_INCORRECT_CLASS_NAME_ERROR % {
+        _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % {
             'className': 'TestNoError3Element',
         },
+        _EXPECTED_INCONSISTENT_CLASS_NAME_ERROR % {
+            'className': 'TestNoError3Element',
+            'domName': 'test-no-error3',
+        },
         _EXPECTED_USE_FIRE_HELPER_WITH_EVENT_NAME_ERROR % {
             'eventName': 'bar-updated',
         },
@@ -415,7 +432,7 @@
             'name': '_otherEvent2',
         },
         # Case 2.4
-        _EXPECTED_INCORRECT_CLASS_NAME_ERROR % {
+        _EXPECTED_INCORRECT_CLASS_NAME_SUFFIX_ERROR % {
             'className': 'TestNoError4Element',
         },
     ]
diff --git a/ui/webui/resources/tools/tests/eslint_ts/with_webui_plugin_lit_element_structure_violations.ts b/ui/webui/resources/tools/tests/eslint_ts/with_webui_plugin_lit_element_structure_violations.ts
index 5f1451bb..3b6e49d 100644
--- a/ui/webui/resources/tools/tests/eslint_ts/with_webui_plugin_lit_element_structure_violations.ts
+++ b/ui/webui/resources/tools/tests/eslint_ts/with_webui_plugin_lit_element_structure_violations.ts
@@ -69,10 +69,11 @@
 customElements.define(TestError5Element.is, TestError5Element);
 
 // Case1.6: Class with
-//  1) Incorrect order method definition order
-//  2) Usage of this.dispatchEvent(new CustomEvent(...))
-//  3) Usage of incorrect dollar sign notation.
-//  4) Usage of CustomEvent type without a type parameter.
+//  1) Inconsistent DOM name suffix.
+//  2) Incorrect order method definition order
+//  3) Usage of this.dispatchEvent(new CustomEvent(...))
+//  4) Usage of incorrect dollar sign notation.
+//  5) Usage of CustomEvent type without a type parameter.
 export class TestError6Element extends CrLitElement {
   override render() {
     return '';
@@ -83,7 +84,7 @@
   }
 
   static get is() {
-    return 'test-error6';
+    return 'test-error6-element';
   }
 
   static override get properties() {
@@ -132,7 +133,7 @@
 
 declare global {
   interface HTMLElementTagNameMap {
-    'test-error6': TestError6Element;
+    'test-error6-element': TestError6Element;
   }
 }
 
@@ -204,6 +205,21 @@
 
 customElements.define(TestError10ElementFoo.is, TestError10ElementFoo);
 
+// Case1.11: Class with inconsistent class name.
+export class TestError11Element extends CrLitElement {
+  static get is() {
+    return 'test-other-error11';
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'test-other-error11': TestError11Element;
+  }
+}
+
+customElements.define(TestError11Element.is, TestError11Element);
+
 
 /* Cases with no violations below. */
 
diff --git a/url/origin.h b/url/origin.h
index fa37b81..97fa03d 100644
--- a/url/origin.h
+++ b/url/origin.h
@@ -512,7 +512,8 @@
 
 }  // namespace url
 
-#if BUILDFLAG(IS_ANDROID) && !BUILDFLAG(CRONET_BUILD)
+#if (BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_ROBOLECTRIC)) && \
+    !BUILDFLAG(CRONET_BUILD)
 namespace jni_zero {
 
 // @JniType conversion function.
diff --git a/v8 b/v8
index 3ec501c..705aa9d3 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 3ec501ccaac5680794d8f70459fde1e7c120abe6
+Subproject commit 705aa9d3796d3e20193c302e1a32fa05ab4363dc