diff --git a/DEPS b/DEPS
index 1696ee1..b5e74b25 100644
--- a/DEPS
+++ b/DEPS
@@ -311,19 +311,19 @@
   # 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': 'f9143bd3d8c2762c7cf11c1eb1d7e23d2c94ec26',
+  'src_internal_revision': '572bdfc0045579bddb74a4c981aa48805106aad3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '2c49b3f9c3c2dececf1b41a9334312a85ef8aa9f',
+  'skia_revision': 'd8415c5d7b91dbfae97a4786a35e892935b07ede',
   # 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': '97206e58a92220c1b9b45ba6a22225903c96d9b1',
+  'v8_revision': 'de3dee3a9fef6b33968ade6f6e870f7e635a5443',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '942c07d0c8a6ab15368faa6c972385536189244c',
+  'angle_revision': '0b3709b9cf4a6b8c75576b0b8c77bcda80fe38f0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -399,7 +399,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': 'e510e6588351303b677e5ac144db4755fef96f1c',
+  'devtools_frontend_revision': 'dfec07e090d057f295fe61e870922e4e6edcfc59',
   # 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.
@@ -423,7 +423,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '7b04a3a55a4f689fb66b8cc758e440d60b976155',
+  'dawn_revision': '936cc1681ce39fd2d758fe3d74f7878cbb5573ea',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -459,7 +459,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling crabbyavif
   # and whatever else without interference from each other.
-  'crabbyavif_revision': '593c3744025312e2859d2e93016dcdbd8033cb81',
+  'crabbyavif_revision': '3bd20ee70c9a3505eb6846bef81c2876ada113fa',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Speedometer main
   # and whatever else without interference from each other.
@@ -1115,10 +1115,10 @@
     'condition': 'non_git_source',
     'objects': [
       {
-        'object_name': 'meet-gpu-tests/899887179.tar.gz',
-        'sha256sum': '55ddf82c04d8be72d83aae1ae0c29d68fa0d1c14627cfb4ae5100e4a88a29651',
-        'size_bytes': 277398041,
-        'generation': 1776242839897846,
+        'object_name': 'meet-gpu-tests/900467620.tar.gz',
+        'sha256sum': '4fb30cd0c16990f42897b95eaf0eb2e604c26a554719a0a7dd219df9f7602426',
+        'size_bytes': 277398422,
+        'generation': 1776329240513988,
       },
     ],
   },
@@ -1219,7 +1219,7 @@
       'packages': [
           {
               'package': 'chromium/android_webview/tools/orderfiles/arm',
-              'version': 'aZMllksW_qNuiNtFArPIVozKZk9-e98pYsXTOnlFl5QC',
+              'version': 'TXCZ-_rvOrKhDR0Pc3FoxQ9EZ-LWHr-fhgSpJd2oBHAC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1596,7 +1596,7 @@
   },
 
   'src/chrome/test/data/autofill/captured_sites/artifacts': {
-    'url': Var('chrome_git') + '/chrome/test/captured_sites/autofill.git' + '@' + 'cf4512236aee7a35b15bf7b782f4fade8ae9f06b',
+    'url': Var('chrome_git') + '/chrome/test/captured_sites/autofill.git' + '@' + '3a4f9cb1d9eab377eadb9f55b5f833d6fbbe5369',
     'condition': 'checkout_chromium_autofill_test_dependencies',
   },
 
@@ -1626,7 +1626,7 @@
     'packages': [
       {
         'package': 'chromium/chrome/test/data/variations/cipd',
-        'version': 'Cv1maCO-AuEykofLbPouOgrxR-sV0Kq5LBTDqBO8dC8C',
+        'version': 'UyiKYWzJ_-GXA31sbjIo4uxlUfxmfX1nt--RoFbz9A0C',
       },
     ],
     'condition': 'non_git_source',
@@ -1638,7 +1638,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'dd7e41d58f73e6718581674e8f0841fcd40ac49f',
+    '97bf935c264d604563f58ace7cb554bc4f34aa19',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -2181,7 +2181,7 @@
   },
 
   'src/third_party/google-truth/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/truth.git' + '@' + '33387149b465f82712a817e6744847fe136949b3',
+      'url': Var('chromium_git') + '/external/github.com/google/truth.git' + '@' + 'fc54b9e4436ed8ca8bcae7dd71e07a6857e13836',
       'condition': 'checkout_android',
   },
 
@@ -3021,7 +3021,7 @@
     Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'),
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '07a13d347ff2cd44325259be766567c7f9e87360',
+    Var('webrtc_git') + '/src.git' + '@' + '9dde36ebf937da0ab92837932b253cc3d42c8dc2',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -3149,7 +3149,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/boca_receiver_app/app',
-        'version': '_rI8Qk32zRNtXo2qtDN9r3H6DB5Yo8PkTGZic3RMNzEC',
+        'version': '80066NiPV2SxOhv1SHzdWR49-vNj06J-shziHq3WfiEC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3160,7 +3160,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/boca_app/app',
-        'version': 'Qzg7qVR6Y8nx6txQ29PD-RSDUljejaTAYEsZelHuh2gC',
+        'version': 'BTNiKOYjplH9HE-cfeN8WmLEjWkNfFd3GY36k_UDP7QC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3215,7 +3215,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'xsCWRrGM-mZo8gGLPIrzqSqtQ4k0i8kTzUqBpe_ujFQC',
+        'version': 'xM2oD0s1npfET4CZ1LcM2XZIEb7tz3JSvEA22gvoWnoC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3733,7 +3733,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        '1feab29cd0b19345316dcba75410882454da4139',
+        '003f3f9444e566f3362c7d550ad394573136df35',
       'condition': 'checkout_src_internal',
   },
 
@@ -3805,7 +3805,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '83596e01ff4bbb77fee10fee3562d12542e0483f',
+        '7d651e31bd37875eaad1c2aa117a962033c7e163',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/chrome/VERSION b/chrome/VERSION
index ed7a14da..84e60a0 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=149
 MINOR=0
-BUILD=7796
+BUILD=7797
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImpl.java
index 150b4d3..c2e768d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImpl.java
@@ -515,7 +515,9 @@
     }
 
     /**
-     * Generates a URI for the proto format to be shared with the specified package.
+     * Generates a URI for the proto format to be shared with the specified package. Only one URI is
+     * valid at a time, and only the result from the most recent call to either this or {@link
+     * #getAssistContentStructuredDataForUrl} will be valid.
      *
      * @param url The URL of the currently active page, the returned URI will only work for this
      *     URL.
@@ -523,7 +525,8 @@
      *     {@code query()}
      * @param targetPackage The package to grant access to. If null, the default assistant package
      *     will be used.
-     * @return A URI to be used with the {@code query()} method to extract the text of {@code url}.
+     * @return A URI to be used with the {@code openFile()} method to extract the contents of {@code
+     *     url}.
      */
     public static @Nullable Uri getProtoContentUriForUrl(
             String url, Supplier<@Nullable Tab> tabSupplier, @Nullable String targetPackage) {
@@ -550,7 +553,8 @@
 
     /**
      * Generates a JSON string to be attached to AssistContent to be shared with the specified
-     * package.
+     * package. Only one URI is valid at a time, and only the result from the most recent call to
+     * either this or {@link #getProtoContentUriForUrl} will be valid.
      *
      * @param url The URL of the currently active page, the returned URI will only work for this
      *     URL.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImplUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImplUnitTest.java
index 94aa2f5e..56123ab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImplUnitTest.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/provider/PageContentProviderImplUnitTest.java
@@ -263,6 +263,30 @@
     }
 
     @Test
+    public void testGetProtoContentUriForUrl() {
+        var eventChecker = getWatcherForEvent(PageContentProviderEvent.GET_CONTENT_URI_SUCCESS);
+        Uri protoContentUri;
+        try (HistogramWatcher histogramWatcher =
+                HistogramWatcher.newSingleRecordWatcher(
+                        "Android.AssistContent.WebPageContentProvider.TargetPackageProvided",
+                        true)) {
+            protoContentUri =
+                    PageContentProviderImpl.getProtoContentUriForUrl(
+                            JUnitTestGURLs.GOOGLE_URL.getSpec(),
+                            mActivityTabProvider,
+                            "com.google.android.googlequicksearchbox");
+        }
+
+        assertNotNull(protoContentUri);
+        verify(mContextSpy)
+                .grantUriPermission(
+                        eq("com.google.android.googlequicksearchbox"),
+                        eq(protoContentUri),
+                        eq(Intent.FLAG_GRANT_READ_URI_PERMISSION));
+        eventChecker.assertExpected();
+    }
+
+    @Test
     public void testTextQueryValidContentUrl() {
         setInnerTextExtractionResult("Page contents!", 200);
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
index 826a2ac..0195152 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
@@ -287,6 +287,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testAddBookmarkToOtherFolder() {
         mActivityTestRule.loadUrl(mTestPage);
         BookmarkTestUtil.readPartnerBookmarks(mActivityTestRule.getActivityTestRule());
@@ -323,6 +324,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testOpenBookmark() throws InterruptedException, ExecutionException {
         addBookmark(TEST_PAGE_TITLE_GOOGLE, mTestPage);
         openBookmarkManager();
@@ -345,6 +347,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testUrlComposition() {
         BookmarkTestUtil.readPartnerBookmarks(mActivityTestRule.getActivityTestRule());
         runOnUiThreadBlocking(
@@ -548,6 +551,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testSearchBookmarks() throws Exception {
         BookmarkId folder = addFolder(TEST_FOLDER_TITLE);
         addBookmark(TEST_PAGE_TITLE_GOOGLE, mTestPage, folder);
@@ -735,6 +739,7 @@
     @MediumTest
     @EnableFeatures(ChromeFeatureList.ENABLE_ESCAPE_HANDLING_FOR_SECONDARY_ACTIVITIES)
     @Restriction({DeviceFormFactor.ONLY_TABLET})
+    @DisabledTest(message = "crbug.com/391655333")
     public void testTabletSearch_EscapeKeyClearsSearch() throws Exception {
         // Setup: Add a bookmark so the folder isn't empty.
         BookmarkId folder = addFolder(TEST_FOLDER_TITLE);
@@ -780,6 +785,7 @@
     @Test
     @MediumTest
     @DisableIf.Device(DeviceFormFactor.DESKTOP) // https://crbug.com/481444847
+    @DisabledTest(message = "crbug.com/391655333")
     public void testSearchBookmarks_Delete() throws Exception {
         BookmarkId testFolder = addFolder(TEST_FOLDER_TITLE);
         addBookmark(TEST_PAGE_TITLE_GOOGLE, mTestPage, testFolder);
@@ -835,6 +841,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testSearchBookmarks_DeleteFolderWithChildrenInResults() throws Exception {
         BookmarkId testFolder = addFolder(TEST_FOLDER_TITLE);
         addBookmark(TEST_PAGE_TITLE_FOO, mTestPageFoo, testFolder);
@@ -946,6 +953,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testEndIconVisibilityInSelectionMode() throws Exception {
         addFolder(TEST_FOLDER_TITLE);
         addBookmark(TEST_TITLE_A, mTestUrlA);
@@ -1171,6 +1179,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testSmallDrag_Down_MixedFoldersAndBookmarks() throws Exception {
         List<BookmarkId> initial = new ArrayList<>();
         List<BookmarkId> expected = new ArrayList<>();
@@ -1238,6 +1247,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testPromoDraggability() throws Exception {
         addFolder(TEST_FOLDER_TITLE);
 
@@ -1260,6 +1270,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testItemDraggability() throws Exception {
         addBookmark("a", mTestUrlA);
         addFolder(TEST_FOLDER_TITLE);
@@ -1278,6 +1289,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testCannotSelectPromo() throws Exception {
         addFolder(TEST_FOLDER_TITLE);
 
@@ -1359,6 +1371,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testMoveUpGoneForTopElement() throws Exception {
         addBookmark(TEST_PAGE_TITLE_GOOGLE, mTestUrlA);
         addFolder(TEST_FOLDER_TITLE);
@@ -1397,6 +1410,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testMoveButtonsGoneWithOneBookmark() throws Exception {
         addFolder(TEST_FOLDER_TITLE);
         openBookmarkManager();
@@ -1416,6 +1430,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testTopLevelFolders() throws Exception {
         // NOTE: Hide promos to ensure top level-folders will fit the viewport.
         SigninPromoCoordinator.disablePromoForTesting();
@@ -1432,6 +1447,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testTopLevelFolderUpdateAfterSync() throws Exception {
         // Set up the test and open the bookmark manager to the Mobile Bookmarks folder.
         BookmarkTestUtil.readPartnerBookmarks(mActivityTestRule.getActivityTestRule());
@@ -1598,6 +1614,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testAddBookmarkInBackgroundWithSelection() throws Exception {
         BookmarkId folder = addFolder(TEST_FOLDER_TITLE);
         addBookmark(TEST_PAGE_TITLE_FOO, mTestPageFoo, folder);
@@ -1670,6 +1687,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testDeleteSomeSelectedBookmarksInBackground() throws Exception {
         // selected on bookmarks and then remove one of them in background
         // in the meantime, the toolbar stays in selection mode
@@ -1714,6 +1732,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testUpdateSelectedBookmarkInBackground() throws Exception {
         BookmarkId folder = addFolder(TEST_FOLDER_TITLE);
         BookmarkId id = addBookmark(TEST_PAGE_TITLE_FOO, mTestPageFoo, folder);
@@ -1752,6 +1771,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testBookmarksDoesNotRecordLaunchMetrics() throws Throwable {
         assertEquals(
                 1,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java
index 5e1251d..4f71136 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java
@@ -240,6 +240,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testReadingListItemMenuItems() throws Exception {
         SigninPromoCoordinator.disablePromoForTesting();
         addReadingListBookmark(TEST_PAGE_TITLE_GOOGLE, mTestUrlA);
@@ -266,6 +267,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testReadingListItemMenuItems_ReadItem() throws Exception {
         SigninPromoCoordinator.disablePromoForTesting();
         BookmarkId id = addReadingListBookmark(TEST_PAGE_TITLE_GOOGLE, mTestUrlA);
@@ -339,6 +341,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testReadingListEmptyStateView() throws Exception {
         SigninPromoCoordinator.disablePromoForTesting();
         openBookmarkManager();
@@ -462,6 +465,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testReadingListFolderShownOneUnreadPage() throws Exception {
         SigninPromoCoordinator.disablePromoForTesting();
         // Add two reading list items and set one as read.
@@ -483,6 +487,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testReadingListFolderShownMultipleUnreadPages() throws Exception {
         SigninPromoCoordinator.disablePromoForTesting();
         // Add three reading list items and set one as read.
@@ -505,6 +510,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testReadingListFolderShown_SetReadingListStatus() throws Exception {
         SigninPromoCoordinator.disablePromoForTesting();
         // Add three reading list items and set one as read.
@@ -530,6 +536,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testReadingListItemsInSelectionMode() throws Exception {
         SigninPromoCoordinator.disablePromoForTesting();
         addReadingListBookmark(TEST_PAGE_TITLE_GOOGLE, mTestUrlA);
@@ -578,6 +585,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testReadingListItemsInSelectionMode_SearchMode() throws Exception {
         SigninPromoCoordinator.disablePromoForTesting();
         addReadingListBookmark(TEST_PAGE_TITLE_GOOGLE, mTestUrlA);
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 964c969..93f472e 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5337,9 +5337,6 @@
       <message name="IDS_EXTENSION_PROMPT_GRANT_PERMISSIONS_CHECKBOX" desc="Label next to the checkbox in the extension or app installation prompt, which indicates if a user wants to grant access to all requested sites.">
         Grant access to all requested sites.
       </message>
-      <message name="IDS_EXTENSION_PROMPT_MESSAGE_FROM_ADMIN" desc="Second line in the content area of the extension of app blocked prompt. It gives user the additional message which comes from their administrator.">
-        From your administrator: <ph name="ADMIN_MESSAGE">$1<ex>Please visit www.example.com/extension for more information.</ex></ph>
-      </message>
 
       <message name="IDS_EXTENSION_PERMISSION_LINE" desc="Template for item in list of privileges that an extension or app has">
           • <ph name="PERMISSION">$1<ex>Read and change all your data on all websites</ex></ph>
@@ -6113,13 +6110,6 @@
         Activate the extension
       </message>
 
-      <message name="IDS_EXTENSIONS_SUPERVISED_USER_PARENTAL_PERMISSION_FAILURE" desc="The error information displayed in extra details if the parent permission request flow fails unexpectedly when a supervised user attempts to install or enable an extension. Note that this does not include the case where a parent fails to enter their password.">
-        Parent permission request failed.
-      </message>
-      <message name="IDS_EXTENSIONS_SUPERVISED_USER_BLOCKED_BY_PARENT" desc="The information displayed in extra details if the parent blocks or cancels the installation flow for an extension.">
-        Your parent didn't approve this extension
-      </message>
-
       <message name="IDS_EXTENSION_PACK_DIALOG_HEADING" desc="The heading of the pack extension dialog.">
         Select the root directory of the extension to pack. To update an extension, also select the private key file to reuse.
       </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 31ac68f..43e7cf7 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -991,12 +991,6 @@
     "//chrome/browser/push_messaging:impl",
   ]
 
-  if (is_android) {
-    # TODO(crbug.com/353332589): Remove this circular dependency when
-    # profiles/profile_manager.h gets componentized.
-    allow_circular_includes_from += [ "//chrome/browser/share" ]
-  }
-
   public_deps = [
     # WARNING WARNING WARNING
     # New dependencies outside of //chrome/browser should be added to
@@ -1311,7 +1305,7 @@
     "//chrome/browser/serial",
     "//chrome/browser/serial:impl",
     "//chrome/browser/sessions",
-    "//chrome/browser/share",
+    "//chrome/browser/share:impl",
     "//chrome/browser/sharing",
     "//chrome/browser/sharing:impl",
     "//chrome/browser/sharing/click_to_call",
@@ -6761,8 +6755,6 @@
     "resource_coordinator/tab_load_tracker_test_support.h",
     "safe_browsing/notification_content_detection/mock_notification_content_detection_service.cc",
     "safe_browsing/notification_content_detection/mock_notification_content_detection_service.h",
-    "share/fake_share_history.cc",
-    "share/fake_share_history.h",
     "signin/chrome_signin_client_test_util.cc",
     "signin/chrome_signin_client_test_util.h",
     "ssl/ssl_browsertest_util.cc",
@@ -6800,7 +6792,7 @@
     "//chrome/browser/policy/messaging_layer/proto:crd_event_proto",
     "//chrome/browser/search_engine_choice",
     "//chrome/browser/search_engines",
-    "//chrome/browser/share",
+    "//chrome/browser/share:test_support",
     "//chrome/browser/translate",
     "//chrome/common",
     "//chrome/common/notifications",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index e23f4380..4f90fea 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -12461,6 +12461,12 @@
      flag_descriptions::kAutofillAiDedupeEntitiesDescription, kOsAll,
      FEATURE_VALUE_TYPE(autofill::features::kAutofillAiDedupeEntities)},
 
+    {"autofill-ai-no-filling-icons-experiment",
+     flag_descriptions::kAutofillAiNoFillingIconsExperimentName,
+     flag_descriptions::kAutofillAiNoFillingIconsExperimentDescription, kOsAll,
+     FEATURE_VALUE_TYPE(
+         autofill::features::kAutofillAiNoFillingIconsExperiment)},
+
     {"autofill-ai-reauth-required",
      flag_descriptions::kAutofillAiReauthRequiredName,
      flag_descriptions::kAutofillAiReauthRequiredDescription, kOsAll,
diff --git a/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java b/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java
index 4e9a265..e9fb320cc 100644
--- a/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java
+++ b/chrome/browser/android/examples/custom_tabs_client/src/java/org/chromium/customtabsclient/MainActivity.java
@@ -250,6 +250,8 @@
             "androidx.browser.customtabs.extra.ACTIVITY_SCROLL_CONTENT_RESIZE";
     private static final String EXTRA_OMNIBOX_ENABLED =
             "org.chromium.chrome.browser.customtabs.OMNIBOX_ENABLED";
+    private static final String EXTRA_TRANSLUCENT_BACKGROUND =
+            "androidx.browser.customtabs.extra.TRANSLUCENT_BACKGROUND";
 
     private final ActivityResultLauncher<Intent> mLauncher =
             AuthTabIntent.registerActivityResultLauncher(this, this::handleAuthResult);
@@ -1309,7 +1311,12 @@
         }
 
         customTabsIntent.intent.putExtra(EXTRA_OMNIBOX_ENABLED, mSearchInCctCheckbox.isChecked());
-
+        CheckBox bgAlpha = findViewById(R.id.bgalpha_checkbox);
+        if (bgAlpha.isChecked()) {
+            EditText bgAlphaText = findViewById(R.id.bgalpha_text);
+            int bgColor = Color.parseColor(bgAlphaText.getText().toString());
+            customTabsIntent.intent.putExtra(EXTRA_TRANSLUCENT_BACKGROUND, bgColor);
+        }
         if (mCctType.equals(CCT_OPTION_AUTHTAB)) {
             launchAuthTab(url);
             editor.putString(SHARED_PREF_CUSTOM_SCHEME, mCustomScheme);
diff --git a/chrome/browser/android/examples/custom_tabs_client/src/res/layout/main.xml b/chrome/browser/android/examples/custom_tabs_client/src/res/layout/main.xml
index b006d46..a24dd3a 100644
--- a/chrome/browser/android/examples/custom_tabs_client/src/res/layout/main.xml
+++ b/chrome/browser/android/examples/custom_tabs_client/src/res/layout/main.xml
@@ -794,5 +794,29 @@
             android:layout_height="wrap_content"
             android:checked = "false"
             android:text="@string/allow_initial_navigation_to_leave" />
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:gravity="center_vertical"
+            android:layout_marginStart="3dp"
+            android:layout_marginBottom="16dp">
+            <CheckBox
+                android:id="@+id/bgalpha_checkbox"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:checked="false"
+                android:text="@string/bgalpha_label" />
+            <EditText
+                android:id="@+id/bgalpha_text"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:gravity="center_vertical|end"
+                android:minWidth="40sp"
+                android:inputType="text"
+                android:text="@string/bgalpha_defvalue"
+                android:contentDescription="@string/bgalpha_label"
+                style="?attr/textAppearanceLabelLarge" />
+        </LinearLayout>
     </LinearLayout>
 </ScrollView>
diff --git a/chrome/browser/android/examples/custom_tabs_client/src/res/values/strings.xml b/chrome/browser/android/examples/custom_tabs_client/src/res/values/strings.xml
index 54fcfa3..2a5a7a66 100644
--- a/chrome/browser/android/examples/custom_tabs_client/src/res/values/strings.xml
+++ b/chrome/browser/android/examples/custom_tabs_client/src/res/values/strings.xml
@@ -87,4 +87,6 @@
     <string name="add_overflow_custom_menu_item_text"> Add an overflow Contextual Menu Item </string>
     <string name="allow_initial_navigation_to_leave">Allow initial URL to leave browser</string>
     <string name="navbar_color_toolbar_text">Set navigation bar color to toolbar color</string>
+    <string name="bgalpha_label">Background Color</string>
+    <string name="bgalpha_defvalue">#80000000</string>
 </resources>
diff --git a/chrome/browser/android/send_tab_to_self/send_tab_to_self_android_bridge.cc b/chrome/browser/android/send_tab_to_self/send_tab_to_self_android_bridge.cc
index cd0b353..57d352a 100644
--- a/chrome/browser/android/send_tab_to_self/send_tab_to_self_android_bridge.cc
+++ b/chrome/browser/android/send_tab_to_self/send_tab_to_self_android_bridge.cc
@@ -97,20 +97,6 @@
                         std::move(commit_confirmation));
 }
 
-// Deletes the entry associated with the passed in GUID.
-static void JNI_SendTabToSelfAndroidBridge_DeleteEntry(
-    JNIEnv* env,
-    Profile* profile,
-    const JavaRef<jstring>& j_guid) {
-  SendTabToSelfModel* model =
-      SendTabToSelfSyncServiceFactory::GetForProfile(profile)
-          ->GetSendTabToSelfModel();
-  if (model->IsReady()) {
-    const std::string guid = ConvertJavaStringToUTF8(env, j_guid);
-    model->DeleteEntry(guid);
-  }
-}
-
 // Marks the entry with the associated GUID as opened.
 static void JNI_SendTabToSelfAndroidBridge_MarkEntryOpened(
     JNIEnv* env,
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index c943fc60..721888c 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -1384,7 +1384,8 @@
         // Create a new browser window (if necessary) and navigate to the
         // downloads page if the user chooses to wait.
         BrowserWindowInterface* browser =
-            chrome::FindBrowserWithProfile(profile);
+            ProfileBrowserCollection::GetForProfile(profile)
+                ->GetLastActiveBrowser();
         if (!browser) {
           browser = Browser::Create(Browser::CreateParams(profile, true));
           browser->GetWindow()->Show();
diff --git a/chrome/browser/ash/accessibility/BUILD.gn b/chrome/browser/ash/accessibility/BUILD.gn
index 42c027a..39d7888 100644
--- a/chrome/browser/ash/accessibility/BUILD.gn
+++ b/chrome/browser/ash/accessibility/BUILD.gn
@@ -56,6 +56,7 @@
     "//chromeos/strings",
     "//components/application_locale_storage",
     "//components/input",
+    "//device/bluetooth/public/cpp",
     "//components/language/core/browser",
     "//components/language/core/common",
     "//components/live_caption:constants",
diff --git a/chrome/browser/ash/accessibility/accessibility_manager.cc b/chrome/browser/ash/accessibility/accessibility_manager.cc
index d82e768d..09edce0 100644
--- a/chrome/browser/ash/accessibility/accessibility_manager.cc
+++ b/chrome/browser/ash/accessibility/accessibility_manager.cc
@@ -94,6 +94,7 @@
 #include "content/public/browser/media_session_service.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/common/content_switches.h"
+#include "device/bluetooth/public/cpp/bluetooth_address.h"
 #include "extensions/browser/api/virtual_keyboard_private/virtual_keyboard_delegate.h"
 #include "extensions/browser/api/virtual_keyboard_private/virtual_keyboard_private_api.h"
 #include "extensions/common/constants.h"
@@ -203,11 +204,16 @@
   UpstartClient* client = UpstartClient::Get();
   client->StopJob(kBrlttyUpstartJobName, {}, base::DoNothing());
 
-  std::vector<std::string> args;
   if (address.empty())
     return;
 
-  args.push_back(base::StringPrintf("ADDRESS=%s", address.c_str()));
+  // The address is used as an environment variable in the brltty Upstart job,
+  // so a malformed value could allow injection.
+  std::string canonical = device::CanonicalizeBluetoothAddress(address);
+  CHECK(!canonical.empty());
+
+  std::vector<std::string> args;
+  args.push_back(base::StringPrintf("ADDRESS=%s", canonical.c_str()));
   client->StartJob(kBrlttyUpstartJobName, args, base::DoNothing());
 }
 
diff --git a/chrome/browser/ash/app_list/DEPS b/chrome/browser/ash/app_list/DEPS
index e3d797c0..3f7f2d70 100644
--- a/chrome/browser/ash/app_list/DEPS
+++ b/chrome/browser/ash/app_list/DEPS
@@ -58,6 +58,7 @@
   "+chrome/browser/ui/browser_navigator_params.h",
   "+chrome/browser/ui/browser_window.h",
   "+chrome/browser/ui/browser_window/public/browser_window_interface.h",
+  "+chrome/browser/ui/browser_window/public/profile_browser_collection.h",
   "+chrome/browser/ui/chrome_pages.h",
   "+chrome/browser/ui/web_applications",
   "+chrome/browser/ui/webui/ash/settings/app_management",
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 9858740..978355d 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
@@ -82,6 +82,7 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/common/chrome_features.h"
@@ -874,7 +875,8 @@
             prefs->GetLastLaunchTime(app_constants::kChromeAppId));
 
   // Close the regular browser.
-  CloseBrowserSynchronously(chrome::FindBrowserWithProfile(profile));
+  CloseBrowserSynchronously(
+      ProfileBrowserCollection::GetForProfile(profile)->GetLastActiveBrowser());
   EXPECT_EQ(0U, chrome::GetBrowserCount(profile));
   // Recorded the launch time should not update.
   EXPECT_EQ(time_recorded1,
diff --git a/chrome/browser/ash/app_mode/DEPS b/chrome/browser/ash/app_mode/DEPS
index 23f3c492..df5c565 100644
--- a/chrome/browser/ash/app_mode/DEPS
+++ b/chrome/browser/ash/app_mode/DEPS
@@ -39,7 +39,6 @@
   "+chrome/browser/extensions/install_tracker_factory.h",
   "+chrome/browser/extensions/updater/extension_updater.h",
   "+chrome/browser/extensions/updater/extension_updater_factory.h",
-  "+chrome/browser/extensions/webstore_install_helper.h",
   "+chrome/browser/net",
   "+chrome/browser/policy",
   "+chrome/browser/profiles",
diff --git a/chrome/browser/ash/app_mode/kiosk_app_data.cc b/chrome/browser/ash/app_mode/kiosk_app_data.cc
index 7040205..377326c 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_data.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_data.cc
@@ -25,7 +25,6 @@
 #include "base/values.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_data_delegate.h"
 #include "chrome/browser/ash/app_mode/kiosk_chrome_app_manager.h"
-#include "chrome/browser/extensions/webstore_install_helper.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/account_id/account_id.h"
@@ -41,6 +40,7 @@
 #include "extensions/browser/install/crx_install_error.h"
 #include "extensions/browser/sandboxed_unpacker.h"
 #include "extensions/browser/webstore_data_fetcher.h"
+#include "extensions/browser/webstore_install_helper.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension_resource.h"
 #include "extensions/common/extension_urls.h"
@@ -204,7 +204,7 @@
   void Start(const std::string& app_id,
              const std::string& manifest,
              const GURL& icon_url,
-             network::mojom::URLLoaderFactory* loader_factory) {
+             scoped_refptr<network::SharedURLLoaderFactory> loader_factory) {
     scoped_refptr<extensions::WebstoreInstallHelper> webstore_helper =
         new extensions::WebstoreInstallHelper(this, app_id, manifest, icon_url);
     webstore_helper->Start(loader_factory);
@@ -505,7 +505,7 @@
   // WebstoreDataParser deletes itself when done.
   (new WebstoreDataParser(weak_factory_.GetWeakPtr()))
       ->Start(app_id(), item_snippet.manifest(), icon_url,
-              shared_url_loader_factory_.get());
+              shared_url_loader_factory_);
 }
 
 void KioskAppData::OnWebstoreResponseParseFailure(
diff --git a/chrome/browser/ash/login/auth_factors_policy/BUILD.gn b/chrome/browser/ash/login/auth_factors_policy/BUILD.gn
index 93e2ba5e..f137b0af 100644
--- a/chrome/browser/ash/login/auth_factors_policy/BUILD.gn
+++ b/chrome/browser/ash/login/auth_factors_policy/BUILD.gn
@@ -21,6 +21,7 @@
   ]
   deps = [
     "//chrome/browser/ash/login",
+    "//chrome/browser/ash/login/quick_unlock",
     "//chromeos/ash/components/browser_context_helper",
     "//chromeos/ash/services/auth_factor_config",
     "//components/session_manager/core",
diff --git a/chrome/browser/ash/login/auth_factors_policy/local_auth_factors_policy_controller.cc b/chrome/browser/ash/login/auth_factors_policy/local_auth_factors_policy_controller.cc
index 1455713..1a48e3c 100644
--- a/chrome/browser/ash/login/auth_factors_policy/local_auth_factors_policy_controller.cc
+++ b/chrome/browser/ash/login/auth_factors_policy/local_auth_factors_policy_controller.cc
@@ -16,6 +16,7 @@
 #include "base/functional/callback_helpers.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
+#include "chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h"
 #include "chrome/browser/ash/login/reauth_stats.h"
 #include "chromeos/ash/components/login/auth/auth_factor_editor.h"
 #include "chromeos/ash/components/login/auth/public/authentication_error.h"
@@ -64,6 +65,11 @@
       base::BindRepeating(
           &LocalAuthFactorsPolicyController::OnAllowedAuthFactorsPrefUpdated,
           base::Unretained(this)));
+  pref_change_registrar_.Add(
+      ash::prefs::kQuickUnlockModeAllowlist,
+      base::BindRepeating(
+          &LocalAuthFactorsPolicyController::OnAllowedAuthFactorsPrefUpdated,
+          base::Unretained(this)));
   OnAllowedAuthFactorsPrefUpdated();
 }
 
@@ -112,11 +118,16 @@
       config.FindFactorByType(cryptohome::AuthFactorType::kPassword);
   auto* pin_factor = config.FindFactorByType(cryptohome::AuthFactorType::kPin);
 
+  bool pin_is_secondary_and_allowed =
+      pin_factor && password_factor &&
+      ash::auth::IsGaiaPassword(*password_factor) &&
+      !ash::quick_unlock::IsPinDisabledByPolicy(
+          pref_change_registrar_.prefs(), ash::quick_unlock::Purpose::kUnlock);
+
   bool has_local_auth_factors =
-      pin_factor ||
+      (pin_factor && !pin_is_secondary_and_allowed) ||
       (password_factor && ash::auth::IsLocalPassword(*password_factor));
-  // TODO: b/487597055 - Do not force reauth when pin allowed by QuickUnlock
-  // policy.
+
   if (has_local_auth_factors) {
     user_manager::UserManager::Get()->SaveForceOnlineSignin(
         user_context->GetAccountId(), /*force_online_signin=*/true);
diff --git a/chrome/browser/ash/login/auth_factors_policy/local_auth_factors_policy_controller_browsertest.cc b/chrome/browser/ash/login/auth_factors_policy/local_auth_factors_policy_controller_browsertest.cc
index a27e95d9..1f630c66 100644
--- a/chrome/browser/ash/login/auth_factors_policy/local_auth_factors_policy_controller_browsertest.cc
+++ b/chrome/browser/ash/login/auth_factors_policy/local_auth_factors_policy_controller_browsertest.cc
@@ -85,14 +85,22 @@
   }
 
  protected:
+  void UpdatePolicy() { provider_.UpdateChromePolicy(policy_map_); }
+
   void DisableAllAllowedAuthFactorsPolicy() {
-    base::Value allowed_auth_factors(base::Value::Type::LIST);
-    policy::PolicyMap user_policy;
-    user_policy.Set(policy::key::kAllowedLocalAuthFactors,
+    policy_map_.Set(policy::key::kAllowedLocalAuthFactors,
                     policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
                     policy::POLICY_SOURCE_CLOUD,
-                    std::move(allowed_auth_factors), nullptr);
-    provider_.UpdateChromePolicy(user_policy);
+                    base::Value(base::Value::Type::LIST), nullptr);
+    UpdatePolicy();
+  }
+
+  void SetQuickUnlockAllowedModesPolicy(base::ListValue modes) {
+    policy_map_.Set(policy::key::kQuickUnlockModeAllowlist,
+                    policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
+                    policy::POLICY_SOURCE_CLOUD, base::Value(std::move(modes)),
+                    nullptr);
+    UpdatePolicy();
   }
 
   void PerformLoginAndVerifyPolicyEnforcement(
@@ -145,6 +153,7 @@
       &cryptohome_};
   std::unique_ptr<base::AutoReset<bool>> ignore_sync_errors_for_test_;
   testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
+  policy::PolicyMap policy_map_;
   base::test::ScopedFeatureList feature_list_{
       ash::features::kManagedLocalPinAndPassword};
 };
@@ -174,15 +183,34 @@
       LoginScreenTestApi::IsForcedOnlineSignin(pin_only_user_.account_id));
 }
 
-IN_PROC_BROWSER_TEST_F(LocalAuthFactorsPolicyControllerTest,
-                       PRE_ForceReauthForOnlinePasswordAndPinOnly) {
+IN_PROC_BROWSER_TEST_F(
+    LocalAuthFactorsPolicyControllerTest,
+    PRE_NoForceReauthForOnlinePasswordAndPinWhenPinAllowedByQuickUnlock) {
+  PerformLoginAndVerifyPolicyEnforcement(gaia_password_and_pin_user_,
+                                         ReauthReason::kNone);
+}
+
+IN_PROC_BROWSER_TEST_F(
+    LocalAuthFactorsPolicyControllerTest,
+    NoForceReauthForOnlinePasswordAndPinWhenPinAllowedByQuickUnlock) {
+  EXPECT_FALSE(LoginScreenTestApi::IsForcedOnlineSignin(
+      gaia_password_and_pin_user_.account_id));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    LocalAuthFactorsPolicyControllerTest,
+    PRE_ForceReauthForOnlinePasswordAndPinWhenPinDisallowedByQuickUnlock) {
+  base::ListValue quick_unlock_allowlist;
+  // Disallow PIN by providing an empty allowlist.
+  SetQuickUnlockAllowedModesPolicy(std::move(quick_unlock_allowlist));
   PerformLoginAndVerifyPolicyEnforcement(
       gaia_password_and_pin_user_,
       ReauthReason::kForcedByLocalAuthFactorsPolicy);
 }
 
-IN_PROC_BROWSER_TEST_F(LocalAuthFactorsPolicyControllerTest,
-                       ForceReauthForOnlinePasswordAndPinOnly) {
+IN_PROC_BROWSER_TEST_F(
+    LocalAuthFactorsPolicyControllerTest,
+    ForceReauthForOnlinePasswordAndPinWhenPinDisallowedByQuickUnlock) {
   EXPECT_TRUE(LoginScreenTestApi::IsForcedOnlineSignin(
       gaia_password_and_pin_user_.account_id));
 }
@@ -211,4 +239,27 @@
       LoginScreenTestApi::IsForcedOnlineSignin(gaia_password_user_.account_id));
 }
 
+IN_PROC_BROWSER_TEST_F(LocalAuthFactorsPolicyControllerTest,
+                       PRE_ReauthForcedWhenPinDisabledAfterPolicyEnforcement) {
+  // Pin allowed by QuickUnlock, so we do not expect a Reauth.
+  PerformLoginAndVerifyPolicyEnforcement(gaia_password_and_pin_user_,
+                                         ReauthReason::kNone);
+  // Now we disable PIN via QuickUnlock policy.
+  ClearPendingPrefProcessedCallback();
+  base::ListValue quick_unlock_allowlist;
+  // Empty list means no modes allowed, so PIN is disabled.
+  SetQuickUnlockAllowedModesPolicy(std::move(quick_unlock_allowlist));
+  WaitForPrefProcessedCallback();
+
+  // Now that PIN is disabled, it's no longer a safe secondary factor.
+  // Reauth should be forced.
+  AssertReauthReason(gaia_password_and_pin_user_.account_id,
+                     ReauthReason::kForcedByLocalAuthFactorsPolicy);
+}
+
+IN_PROC_BROWSER_TEST_F(LocalAuthFactorsPolicyControllerTest,
+                       ReauthForcedWhenPinDisabledAfterPolicyEnforcement) {
+  EXPECT_TRUE(LoginScreenTestApi::IsForcedOnlineSignin(
+      gaia_password_and_pin_user_.account_id));
+}
 }  // namespace ash
diff --git a/chrome/browser/ash/login/auth_flows_login_ui_test.cc b/chrome/browser/ash/login/auth_flows_login_ui_test.cc
index 868632c..d357ff8b 100644
--- a/chrome/browser/ash/login/auth_flows_login_ui_test.cc
+++ b/chrome/browser/ash/login/auth_flows_login_ui_test.cc
@@ -159,10 +159,18 @@
 };
 
 // ----------------------------------------------------------
-class AuthFlowsLoginRecoverUserTest : public AuthFlowsLoginTestBase {
+class AuthFlowsLoginRecoverUserTest : public AuthFlowsLoginTestBase,
+                                      public testing::WithParamInterface<bool> {
  public:
   AuthFlowsLoginRecoverUserTest()
-      : AuthFlowsLoginTestBase(/* require_reauth */ false) {}
+      : AuthFlowsLoginTestBase(/* require_reauth */ false) {
+    std::vector<base::test::FeatureRef> enabled;
+    if (GetParam()) {
+      enabled.push_back(ash::features::kManagedLocalPinAndPassword);
+      enabled.push_back(ash::features::kRecoveryFlowReorder);
+    }
+    scoped_features_.InitWithFeatures(enabled, {});
+  }
 
   ~AuthFlowsLoginRecoverUserTest() override = default;
 
@@ -192,6 +200,9 @@
     gaia->TypePassword(password);
     gaia->ContinueLogin();
   }
+
+ private:
+  base::test::ScopedFeatureList scoped_features_;
 };
 
 // ----------------------------------------------------------
@@ -454,7 +465,7 @@
 
 // ----------------------------------------------------------
 
-IN_PROC_BROWSER_TEST_F(AuthFlowsLoginRecoverUserTest,
+IN_PROC_BROWSER_TEST_P(AuthFlowsLoginRecoverUserTest,
                        LocalPasswordWithRecovery) {
   const auto& user = with_local_pw_recovery_;
 
@@ -470,9 +481,11 @@
   // Recovery password update confirmation.
   test::RecoveryPasswordUpdatedPageWaiter()->Wait();
   test::RecoveryPasswordUpdatedProceedAction();
+  // After successful password update, the session should start automatically.
+  login_mixin_.WaitForActiveSession();
 }
 
-IN_PROC_BROWSER_TEST_F(AuthFlowsLoginRecoverUserTest,
+IN_PROC_BROWSER_TEST_P(AuthFlowsLoginRecoverUserTest,
                        LocalPasswordWithoutRecoveryCancelLAD) {
   const auto& user = with_local_pw_;
   // Start recovery flow without recovery auth factor.
@@ -482,7 +495,7 @@
   test::LocalDataLossWarningPageWaiter()->Wait();
 }
 
-IN_PROC_BROWSER_TEST_F(AuthFlowsLoginRecoverUserTest,
+IN_PROC_BROWSER_TEST_P(AuthFlowsLoginRecoverUserTest,
                        GaiaPasswordWithRecovery) {
   const auto& user = with_gaia_pw_recovery_;
 
@@ -497,7 +510,7 @@
   login_mixin_.WaitForActiveSession();
 }
 
-IN_PROC_BROWSER_TEST_F(AuthFlowsLoginRecoverUserTest,
+IN_PROC_BROWSER_TEST_P(AuthFlowsLoginRecoverUserTest,
                        GaiaPasswordWithoutRecovery) {
   const auto& user = with_gaia_pw_;
 
@@ -516,7 +529,7 @@
   login_mixin_.WaitForActiveSession();
 }
 
-IN_PROC_BROWSER_TEST_F(AuthFlowsLoginRecoverUserTest,
+IN_PROC_BROWSER_TEST_P(AuthFlowsLoginRecoverUserTest,
                        GaiaPasswordWithoutRecoveryInvalidPassword) {
   const auto& user = with_gaia_pw_;
 
@@ -532,7 +545,7 @@
   pw_changed->InvalidPasswordFeedback()->Wait();
 }
 
-IN_PROC_BROWSER_TEST_F(AuthFlowsLoginRecoverUserTest,
+IN_PROC_BROWSER_TEST_P(AuthFlowsLoginRecoverUserTest,
                        GaiaPasswordWithoutRecoveryForgotPasswordClick) {
   const auto& user = with_gaia_pw_;
 
@@ -550,17 +563,10 @@
 // Parameterized on a boolean that represents whether the recovery flow password
 // reset order changes (kRecoveryFlowReorder) are enabled or not.
 class AuthFlowsLoginRecoverUserTestPasswordlessRecovery
-    : public AuthFlowsLoginRecoverUserTest,
-      public ::testing::WithParamInterface<bool> {
+    : public AuthFlowsLoginRecoverUserTest {
  public:
-  AuthFlowsLoginRecoverUserTestPasswordlessRecovery() {
-    if (GetParam()) {
-      feature_list_.InitAndEnableFeature(ash::features::kRecoveryFlowReorder);
-    }
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
+  AuthFlowsLoginRecoverUserTestPasswordlessRecovery() = default;
+  ~AuthFlowsLoginRecoverUserTestPasswordlessRecovery() override = default;
 };
 
 // Ensures that a user with PIN-only (without recovery) is shown the local data
@@ -613,4 +619,8 @@
                          AuthFlowsLoginRecoverUserTestPasswordlessRecovery,
                          ::testing::ValuesIn({true, false}));
 
+INSTANTIATE_TEST_SUITE_P(AuthFlowsLoginRecoverUserTestTests,
+                         AuthFlowsLoginRecoverUserTest,
+                         ::testing::Bool());
+
 }  // namespace ash
diff --git a/chrome/browser/ash/login/existing_user_controller.cc b/chrome/browser/ash/login/existing_user_controller.cc
index 1e1ed62f..87303dc 100644
--- a/chrome/browser/ash/login/existing_user_controller.cc
+++ b/chrome/browser/ash/login/existing_user_controller.cc
@@ -812,8 +812,7 @@
   auto auth_setup_flow = GetLoginDisplayHost()
                              ->GetWizardContext()
                              ->knowledge_factor_setup.auth_setup_flow;
-  if (!has_required_feature_flags ||
-      auth_setup_flow != WizardContext::AuthChangeFlow::kReauthentication) {
+  if (auth_setup_flow != WizardContext::AuthChangeFlow::kReauthentication) {
     return false;
   }
 
@@ -826,10 +825,12 @@
   auto allowed_local_auth_factors =
       AuthPolicyConnector::Get()->AllowedLocalAuthFactors(
           user_context.GetAccountId());
+  // Policy is only unset for unmanaged users.
+  bool policy_unset = !allowed_local_auth_factors.has_value();
   bool policy_allows_local_auth_factors =
       allowed_local_auth_factors.has_value() &&
       !allowed_local_auth_factors->empty();
-  if (policy_allows_local_auth_factors ||
+  if (policy_unset || policy_allows_local_auth_factors ||
       !UserHasAnyLocalAuthFactors(user_context)) {
     return false;
   }
@@ -842,17 +843,17 @@
 
 bool ExistingUserController::MaybeShowPasswordSelectionScreen(
     const UserContext& user_context) {
+  auto* wizard_context = GetLoginDisplayHost()->GetWizardContext();
   if (!ash::features::IsRecoveryFlowReorderEnabled() ||
       auth_mode_ != LoginPerformer::AuthorizationMode::kExternal ||
-      GetLoginDisplayHost()
-              ->GetWizardContext()
-              ->knowledge_factor_setup.auth_setup_flow !=
+      !wizard_context->allow_factor_change_during_recovery ||
+      wizard_context->knowledge_factor_setup.auth_setup_flow !=
           WizardContext::AuthChangeFlow::kRecovery) {
     return false;
   }
-  GetLoginDisplayHost()->GetWizardContext()->extra_factors_token =
-      AuthSessionStorage::Get()->Store(
-          std::make_unique<UserContext>(user_context));
+  wizard_context->allow_factor_change_during_recovery = false;
+  wizard_context->extra_factors_token = AuthSessionStorage::Get()->Store(
+      std::make_unique<UserContext>(user_context));
   GetLoginDisplayHost()->GetSigninUI()->ShowPasswordSelectionScreen();
 
   return true;
diff --git a/chrome/browser/ash/login/screens/osauth/remove_local_auth_factors_screen_browsertest.cc b/chrome/browser/ash/login/screens/osauth/remove_local_auth_factors_screen_browsertest.cc
index 048d840..6db2b0a 100644
--- a/chrome/browser/ash/login/screens/osauth/remove_local_auth_factors_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/osauth/remove_local_auth_factors_screen_browsertest.cc
@@ -252,7 +252,7 @@
                        PRE_AuthFactorsRemovedForGaiaPasswordPinUser) {
   // Test Setup: Log the user in offline and apply a policy disabling all
   // local auth factors.
-  LoginOfflineAndSetPolicy(pin_only_user_.account_id);
+  LoginOfflineAndSetPolicy(gaia_password_and_pin_user_.account_id);
 }
 
 IN_PROC_BROWSER_TEST_F(RemoveLocalAuthFactorsScreenTest,
diff --git a/chrome/browser/ash/login/wizard_context.h b/chrome/browser/ash/login/wizard_context.h
index 119444ae..9c0ed5f 100644
--- a/chrome/browser/ash/login/wizard_context.h
+++ b/chrome/browser/ash/login/wizard_context.h
@@ -278,6 +278,12 @@
   // True when user is inside the "Add Person" flow.
   bool is_add_person_flow = false;
 
+  // Whether the password selection screen should be shown during the
+  // recovery flow. This is used to ensure the screen is shown only when
+  // intended, as some recovery-like flows (e.g., manual GAIA password change)
+  // should bypass it.
+  bool allow_factor_change_during_recovery = false;
+
   // True if user clicked "Select more fatures" button on the last CHOOBE
   // selected screen.
   bool return_to_choobe_screen = false;
diff --git a/chrome/browser/ash/login/wizard_controller.cc b/chrome/browser/ash/login/wizard_controller.cc
index 80cb0e9..1a19268 100644
--- a/chrome/browser/ash/login/wizard_controller.cc
+++ b/chrome/browser/ash/login/wizard_controller.cc
@@ -1924,6 +1924,7 @@
           NOTREACHED() << "Recovery can not be used during initial setup.";
         case WizardContext::AuthChangeFlow::kRecovery:
           if (ash::features::IsRecoveryFlowReorderEnabled()) {
+            wizard_context_->allow_factor_change_during_recovery = true;
             ObtainContextAndLoginAuthenticated();
           } else {
             ShowPasswordSelectionScreen();
@@ -2864,6 +2865,15 @@
 
 void WizardController::FinalizeAuthWithContext(
     std::unique_ptr<UserContext> context) {
+  auto mount_state = context->GetMountState();
+  if (!mount_state.has_value()) {
+    // In certain edge cases, such as a Reauthentication that transitions into
+    // recovery with an automatic online password update, we can reach this
+    // point before the cryptohome is mounted. In these cases, we need to
+    // perform a login instead of just finalizing the auth.
+    LoginAuthenticatedWithContext(std::move(context));
+    return;
+  }
   ash::LoginDisplayHost::default_host()
       ->GetExistingUserController()
       ->FinalizeAuthAndStartSession(*context);
diff --git a/chrome/browser/ash/mahi/BUILD.gn b/chrome/browser/ash/mahi/BUILD.gn
index b922e65..0f12030 100644
--- a/chrome/browser/ash/mahi/BUILD.gn
+++ b/chrome/browser/ash/mahi/BUILD.gn
@@ -51,6 +51,7 @@
     "//chrome/browser/manta",
     "//chrome/browser/manta:impl",
     "//chrome/browser/profiles:profile_manager",
+    "//chrome/browser/ui/browser_window",
     "//chromeos/ash/experiences/settings_ui",
     "//chromeos/components/magic_boost/public/cpp",
     "//chromeos/components/mahi/public/cpp",
diff --git a/chrome/browser/ash/mahi/DEPS b/chrome/browser/ash/mahi/DEPS
index 07a5b5e..cd9d035 100644
--- a/chrome/browser/ash/mahi/DEPS
+++ b/chrome/browser/ash/mahi/DEPS
@@ -20,7 +20,7 @@
   "+chrome/browser/manta",
   "+chrome/browser/policy/profile_policy_connector.h",
   "+chrome/browser/profiles",
-  "+chrome/browser/ui/browser_finder.h",
+  "+chrome/browser/ui/browser_window/public/profile_browser_collection.h",
   "+chrome/test/base",
 ]
 
diff --git a/chrome/browser/ash/mahi/mahi_manager_impl.cc b/chrome/browser/ash/mahi/mahi_manager_impl.cc
index 8ea5e8f4..a35f917f 100644
--- a/chrome/browser/ash/mahi/mahi_manager_impl.cc
+++ b/chrome/browser/ash/mahi/mahi_manager_impl.cc
@@ -40,7 +40,7 @@
 #include "chrome/browser/manta/manta_service_factory.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chromeos/ash/experiences/settings_ui/settings_app_manager.h"
 #include "chromeos/components/magic_boost/public/cpp/magic_boost_state.h"
 #include "chromeos/components/mahi/public/cpp/mahi_manager.h"
@@ -610,8 +610,9 @@
   ai_metadata.Set(feedback::kMahiMetadataKey, "true");
 
   chrome::ShowFeedbackPage(
-      /*browser=*/chrome::FindBrowserWithProfile(
-          ProfileManager::GetActiveUserProfile()),
+      /*browser=*/ProfileBrowserCollection::GetForProfile(
+          ProfileManager::GetActiveUserProfile())
+          ->GetLastActiveBrowser(),
       /*source=*/feedback::kFeedbackSourceAI, description_template,
       /*description_placeholder_text=*/
       base::UTF16ToUTF8(
diff --git a/chrome/browser/ash/system_web_apps/apps/DEPS b/chrome/browser/ash/system_web_apps/apps/DEPS
index 7126736..34fbf2dc 100644
--- a/chrome/browser/ash/system_web_apps/apps/DEPS
+++ b/chrome/browser/ash/system_web_apps/apps/DEPS
@@ -72,6 +72,7 @@
   "+chrome/browser/ui/browser_navigator.h",
   "+chrome/browser/ui/browser_navigator_params.h",
   "+chrome/browser/ui/browser_window.h",
+  "+chrome/browser/ui/browser_window/public/profile_browser_collection.h",
   "+chrome/browser/ui/chrome_pages.h",
   "+chrome/browser/ui/tabs",
   "+chrome/browser/ui/web_applications",
diff --git a/chrome/browser/ash/system_web_apps/apps/personalization_app/BUILD.gn b/chrome/browser/ash/system_web_apps/apps/personalization_app/BUILD.gn
index 9ac46c6f..908bf6b 100644
--- a/chrome/browser/ash/system_web_apps/apps/personalization_app/BUILD.gn
+++ b/chrome/browser/ash/system_web_apps/apps/personalization_app/BUILD.gn
@@ -102,6 +102,7 @@
     "//chrome/browser/manta",
     "//chrome/browser/manta:impl",
     "//chrome/browser/ui/ash/thumbnail_loader",
+    "//chrome/browser/ui/browser_window",
     "//chrome/browser/ui/webui/sanitized_image:sanitized_image_source",
     "//chrome/browser/web_applications",
     "//chrome/browser/web_applications/mojom:mojom_web_apps_enum_shared",
diff --git a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_base.cc b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_base.cc
index 0ad87734..9be20da3 100644
--- a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_base.cc
+++ b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_sea_pen_provider_base.cc
@@ -32,7 +32,7 @@
 #include "chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chromeos/ash/components/demo_mode/utils/demo_session_utils.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "components/feedback/feedback_constants.h"
@@ -374,7 +374,8 @@
 
   base::RecordAction(base::UserMetricsAction("SeaPen_FeedbackPressed"));
   chrome::ShowFeedbackPage(
-      /*browser=*/chrome::FindBrowserWithProfile(profile_),
+      /*browser=*/ProfileBrowserCollection::GetForProfile(profile_)
+          ->GetLastActiveBrowser(),
       /*source=*/feedback::kFeedbackSourceAI,
       /*description_template=*/feedback_text,
       /*description_placeholder_text=*/
diff --git a/chrome/browser/autofill/android/BUILD.gn b/chrome/browser/autofill/android/BUILD.gn
index 6b1a55d9..048b7704 100644
--- a/chrome/browser/autofill/android/BUILD.gn
+++ b/chrome/browser/autofill/android/BUILD.gn
@@ -331,6 +331,7 @@
     "//components/autofill/android:main_autofill_java",
     "//components/browser_ui/bottomsheet/android:java",
     "//components/browser_ui/settings/android:java",
+    "//components/browser_ui/styles/android:java",
     "//components/browser_ui/widget/android:java",
     "//components/device_reauth:device_reauth_java_enums",
     "//components/prefs/android:java",
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/common/EditorViewBase.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/common/EditorViewBase.java
index 1d46a75..5c17100 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/common/EditorViewBase.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/common/EditorViewBase.java
@@ -156,6 +156,11 @@
 
         mContainerView =
                 LayoutInflater.from(mContext).inflate(R.layout.autofill_editor_dialog, null);
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_AI_WITH_DATA_SCHEMA)) {
+            // TODO: crbug.com/501305415 - Move to xml.
+            mContainerView.setBackgroundColor(
+                    SemanticColorUtils.getSettingsBackgroundColor(mContext));
+        }
         setContentView(mContainerView);
 
         mContentView = mContainerView.findViewById(R.id.contents);
@@ -163,6 +168,10 @@
         prepareToolbar();
 
         mButtonBar = mContainerView.findViewById(R.id.button_bar);
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_AI_WITH_DATA_SCHEMA)) {
+            // TODO: crbug.com/501305415 - Move to xml.
+            mButtonBar.setBackgroundColor(SemanticColorUtils.getSettingsBackgroundColor(mContext));
+        }
         mButtonBar.findViewById(R.id.button_primary).setId(R.id.editor_dialog_done_button);
         mButtonBar.findViewById(R.id.button_secondary).setId(R.id.payments_edit_cancel_button);
 
@@ -409,7 +418,11 @@
     private void prepareToolbar() {
         EditorDialogToolbar toolbar =
                 (EditorDialogToolbar) mContainerView.findViewById(R.id.action_bar);
-        toolbar.setBackgroundColor(SemanticColorUtils.getDefaultBgColor(toolbar.getContext()));
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_AI_WITH_DATA_SCHEMA)) {
+            toolbar.setBackgroundColor(SemanticColorUtils.getSettingsBackgroundColor(mContext));
+        } else {
+            toolbar.setBackgroundColor(SemanticColorUtils.getDefaultBgColor(mContext));
+        }
         toolbar.setTitleTextAppearance(
                 toolbar.getContext(), R.style.TextAppearance_Headline_Primary);
 
diff --git a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptTest.java b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptTest.java
index c23424e2..8f5325d 100644
--- a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptTest.java
+++ b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptTest.java
@@ -35,7 +35,9 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.browser.autofill.editors.address.AddressEditorCoordinator;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.sync.SyncServiceFactory;
@@ -52,6 +54,7 @@
 /** Unit tests for {@link SaveUpdateAddressProfilePrompt}. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
+@EnableFeatures(ChromeFeatureList.AUTOFILL_AI_WITH_DATA_SCHEMA)
 public class SaveUpdateAddressProfilePromptTest {
     private static final long NATIVE_SAVE_UPDATE_ADDRESS_PROFILE_PROMPT_CONTROLLER = 100L;
     private static final boolean NO_MIGRATION = false;
diff --git a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/address/AddressEditorTest.java b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/address/AddressEditorTest.java
index d4232e1..122ccda 100644
--- a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/address/AddressEditorTest.java
+++ b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/address/AddressEditorTest.java
@@ -68,6 +68,7 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.AutofillAddress;
@@ -81,6 +82,7 @@
 import org.chromium.chrome.browser.autofill.editors.address.AddressEditorCoordinator.Delegate;
 import org.chromium.chrome.browser.autofill.editors.common.EditorComponentsProperties;
 import org.chromium.chrome.browser.autofill.editors.common.EditorComponentsProperties.EditorItem;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.sync.SyncServiceFactory;
@@ -113,6 +115,7 @@
 /** Unit tests for autofill address editor. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
+@EnableFeatures(ChromeFeatureList.AUTOFILL_AI_WITH_DATA_SCHEMA)
 public class AddressEditorTest {
     private static final String USER_EMAIL = "example@gmail.com";
     private static final Locale DEFAULT_LOCALE = Locale.getDefault();
diff --git a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorModuleTest.java b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorModuleTest.java
index 778da8e..7315455 100644
--- a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorModuleTest.java
+++ b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorModuleTest.java
@@ -33,6 +33,8 @@
 import static org.chromium.chrome.browser.autofill.editors.utils.TestUtils.setDropdownValue;
 
 import android.app.Activity;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.text.Spanned;
 import android.text.TextUtils;
 import android.text.style.ClickableSpan;
@@ -57,6 +59,8 @@
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.Features.DisableFeatures;
+import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.AutofillProfileBridge;
@@ -69,6 +73,7 @@
 import org.chromium.chrome.browser.autofill.editors.common.date_field.DateFieldView;
 import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncher;
 import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherFactory;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.components.autofill.DropdownKeyValue;
@@ -82,6 +87,7 @@
 import org.chromium.components.autofill.autofill_ai.EntityType;
 import org.chromium.components.autofill.autofill_ai.EntityTypeName;
 import org.chromium.components.autofill.autofill_ai.RecordType;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.signin.base.CoreAccountInfo;
 import org.chromium.components.signin.identitymanager.IdentityManager;
 import org.chromium.google_apis.gaia.GaiaId;
@@ -96,6 +102,7 @@
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 @Batch(Batch.UNIT_TESTS)
+@EnableFeatures(ChromeFeatureList.AUTOFILL_AI_WITH_DATA_SCHEMA)
 public class EntityEditorModuleTest {
     private static final String USER_EMAIL = "example@gmail.com";
     private static final AttributeType PASSPORT_NAME_ATTRIBUTE_TYPE =
@@ -306,6 +313,37 @@
 
     @Test
     @SmallTest
+    public void testBackgroundColor() {
+        when(mPersonalDataManager.getDefaultCountryCodeForNewAddress()).thenReturn("US");
+        showEditorDialog(NEW_LOCAL_PASSPORT);
+        verifyHasExpectedBackgroundColor(
+                mContainerView, SemanticColorUtils.getSettingsBackgroundColor(mActivity));
+        verifyHasExpectedBackgroundColor(
+                mContainerView.findViewById(R.id.action_bar),
+                SemanticColorUtils.getSettingsBackgroundColor(mActivity));
+        verifyHasExpectedBackgroundColor(
+                mContainerView.findViewById(R.id.button_bar),
+                SemanticColorUtils.getSettingsBackgroundColor(mActivity));
+    }
+
+    @Test
+    @SmallTest
+    @DisableFeatures(ChromeFeatureList.AUTOFILL_AI_WITH_DATA_SCHEMA)
+    public void testBackgroundColorWhenAutofillAiDisabled() {
+        when(mPersonalDataManager.getDefaultCountryCodeForNewAddress()).thenReturn("US");
+        showEditorDialog(NEW_LOCAL_PASSPORT);
+        verifyHasExpectedBackgroundColor(
+                mContainerView, SemanticColorUtils.getDefaultBgColor(mActivity));
+        verifyHasExpectedBackgroundColor(
+                mContainerView.findViewById(R.id.action_bar),
+                SemanticColorUtils.getDefaultBgColor(mActivity));
+        verifyHasExpectedBackgroundColor(
+                mContainerView.findViewById(R.id.button_bar),
+                SemanticColorUtils.getDefaultBgColor(mActivity));
+    }
+
+    @Test
+    @SmallTest
     public void testOpenHelpAndFeedback() {
         showEditorDialog(LOCAL_PASSPORT);
 
@@ -837,6 +875,14 @@
         fail("Source notice not found");
     }
 
+    private void verifyHasExpectedBackgroundColor(View view, int expectedColor) {
+        Drawable background = view.getBackground();
+        assertTrue(background instanceof ColorDrawable);
+        ColorDrawable colorDrawable = (ColorDrawable) background;
+
+        assertEquals(expectedColor, colorDrawable.getColor());
+    }
+
     private void clickClickableSpan(CharSequence text) {
         Spanned spanned = (Spanned) text;
         ClickableSpan[] spans = spanned.getSpans(0, text.length(), ClickableSpan.class);
diff --git a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorViewTest.java b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorViewTest.java
index 8dd04bd..55435983 100644
--- a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorViewTest.java
+++ b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorViewTest.java
@@ -22,13 +22,16 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.editors.common.EditorDialogToolbar;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.ui.base.TestActivity;
 
 /** Unit tests for autofill entity editor. */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
+@EnableFeatures(ChromeFeatureList.AUTOFILL_AI_WITH_DATA_SCHEMA)
 public class EntityEditorViewTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
diff --git a/chrome/browser/autofill/otp_manager_browsertest.cc b/chrome/browser/autofill/otp_manager_browsertest.cc
index 09dcd1c3..8bb185a 100644
--- a/chrome/browser/autofill/otp_manager_browsertest.cc
+++ b/chrome/browser/autofill/otp_manager_browsertest.cc
@@ -19,6 +19,7 @@
 #include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
 #include "components/autofill/core/browser/ui/test_autofill_external_delegate.h"
 #include "components/autofill/core/common/signatures.h"
+#include "components/one_time_tokens/core/browser/encrypted_message_reference.h"
 #include "components/one_time_tokens/core/browser/gmail_otp_backend.h"
 #include "components/one_time_tokens/core/browser/one_time_token.h"
 #include "components/one_time_tokens/core/browser/one_time_token_retrieval_error.h"
@@ -92,7 +93,7 @@
 
   // one_time_tokens::GmailOtpBackend:
   void OnIncomingOneTimeTokenBackendTickle(
-      const one_time_tokens::GmailOtpBackend::EncryptedMessageReference&
+      const one_time_tokens::EncryptedMessageReference&
           encrypted_message_reference) override {}
 
   // Simulates the reception of a Gmail OTP.
diff --git a/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkOpenerTest.java b/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkOpenerTest.java
index 107091d..ef72a661 100644
--- a/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkOpenerTest.java
+++ b/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkOpenerTest.java
@@ -30,6 +30,7 @@
 import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.DoNotBatch;
 import org.chromium.base.test.util.ImportantFormFactors;
 import org.chromium.base.test.util.UserActionTester;
@@ -158,6 +159,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testOpenBookmarkInCurrentTab() {
         GURL url = new GURL(getOriginalNativeNtpUrl());
         BookmarkId id = addMobileBookmark("test", url);
@@ -185,6 +187,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testOpenBookmarkInCurrentTab_ReadingList() {
         GURL url = new GURL("https://google.com"); // Chrome URLs not allowed for reading list
         BookmarkId id = addReadingListBookmark("test", url);
@@ -213,6 +216,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testOpenBookmarkInCurrentTab_Incognito() {
         GURL url = new GURL(getOriginalNativeNtpUrl());
         BookmarkId id = addMobileBookmark("test", url);
@@ -233,6 +237,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testOpenBookmarksInNewTabs() {
         GURL url = new GURL(UrlConstants.ABOUT_URL);
 
@@ -267,6 +272,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testOpenBookmarksInNewTabs_Incognito() {
         GURL url = new GURL(UrlConstants.ABOUT_URL);
 
diff --git a/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoTest.java b/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoTest.java
index 3b2ce94..f77268de 100644
--- a/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoTest.java
+++ b/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkPersonalizedSigninPromoTest.java
@@ -32,6 +32,7 @@
 
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
@@ -90,6 +91,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void shouldHideBookmarksSigninPromoIfDataTypesAreManagedByPolicy() {
         SyncServiceFactory.setInstanceForTesting(mSyncService);
         when(mSyncService.isTypeManagedByPolicy(UserSelectableType.BOOKMARKS)).thenReturn(true);
@@ -100,6 +102,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void shouldShowBookmarksSigninPromoIfDataTypesAreNotManagedByPolicy() {
         SyncServiceFactory.setInstanceForTesting(mSyncService);
         when(mSyncService.isTypeManagedByPolicy(UserSelectableType.BOOKMARKS)).thenReturn(false);
@@ -110,6 +113,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void shouldHideBookmarksSigninPromoIfDataTypesSyncing() {
         SyncServiceFactory.setInstanceForTesting(mSyncService);
         when(mSyncService.isTypeManagedByPolicy(UserSelectableType.BOOKMARKS)).thenReturn(false);
@@ -121,6 +125,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void shouldShowBookmarksSigninPromoIfBookmarkNotSyncing() {
         SyncServiceFactory.setInstanceForTesting(mSyncService);
         when(mSyncService.isTypeManagedByPolicy(UserSelectableType.BOOKMARKS)).thenReturn(false);
@@ -131,6 +136,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void shouldShowBookmarksSigninPromoIfReadingListNotSyncing() {
         SyncServiceFactory.setInstanceForTesting(mSyncService);
         when(mSyncService.isTypeManagedByPolicy(UserSelectableType.BOOKMARKS)).thenReturn(false);
diff --git a/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowTest.java b/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowTest.java
index 59f3e1d..3dc47633 100644
--- a/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowTest.java
+++ b/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowTest.java
@@ -152,6 +152,7 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
+    @DisabledTest(message = "crbug.com/391655333")
     public void testBookmarkSaveFlow() throws IOException {
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
@@ -354,6 +355,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "crbug.com/391655333")
     public void testBookmarkSaveFlowEdit() throws IOException {
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
diff --git a/chrome/browser/contextual_tasks/BUILD.gn b/chrome/browser/contextual_tasks/BUILD.gn
index 0eb31f6..62db38f 100644
--- a/chrome/browser/contextual_tasks/BUILD.gn
+++ b/chrome/browser/contextual_tasks/BUILD.gn
@@ -178,7 +178,10 @@
   }
 
   if (enable_dice_support) {
-    sources += [ "search_ai_mode_promo_tab_helper.h" ]
+    sources += [
+      "search_ai_mode_promo_tab_helper.h",
+      "search_ai_mode_signin_promo_controller_observer.h",
+    ]
   }
 
   public_deps = [
diff --git a/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper.cc b/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper.cc
index b4b8512c..57085ec 100644
--- a/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper.cc
+++ b/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper.cc
@@ -191,12 +191,16 @@
   signin_promo_controller_factory_for_testing_ = std::move(factory_callback);
 }
 
+SearchAIModeSignInPromoController*
+SearchAiModePromoTabHelper::GetSigninPromoControllerForTesting() {
+  CHECK_IS_TEST();
+  return signin_promo_controller_.get();
+}
+
 void SearchAiModePromoTabHelper::TriggerCoBrowsePostSignIn() {
   if (!aim_search_web_contents_ || aim_search_web_contents_.WasInvalidated() ||
-      !IsAIModeSearch(aim_search_web_contents_.get())) {
-    return;
-  }
-  if (!target_url_.is_valid()) {
+      !IsAIModeSearch(aim_search_web_contents_.get()) ||
+      !target_url_.is_valid()) {
     SelfDestruct();
     return;
   }
@@ -265,15 +269,6 @@
     return;
   }
 
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
-  // If we know already that the promo cannot be shown (e.g. due to rate limits)
-  // abort the flow and self-destruct.
-  if (!signin::ShouldShowSearchAIModeSignInPromo(*profile)) {
-    SelfDestruct();
-    return;
-  }
-
   // Determine if the navigation was initiated from an AI search page.
   std::optional<base::WeakPtr<content::WebContents>> initiator =
       GetInitiatorWebContents(*navigation_handle);
@@ -317,6 +312,7 @@
     signin_promo_controller_ =
         std::make_unique<SearchAIModeSignInPromoController>(web_contents());
   }
+  signin_promo_controller_observation_.Observe(signin_promo_controller_.get());
 
   tabs::TabInterface* tab =
       tabs::TabInterface::MaybeGetFromContents(web_contents());
@@ -324,15 +320,19 @@
     BrowserView* browser_view =
         BrowserView::GetBrowserViewForBrowser(tab->GetBrowserWindowInterface());
     if (browser_view && identity_manager_) {
-      identity_manager_scoped_observation_.Observe(identity_manager_);
-      signin_promo_controller_->MaybeShowPromo(browser_view);
+      bool promo_triggered =
+          signin_promo_controller_->MaybeShowPromo(browser_view);
+      if (promo_triggered) {
+        identity_manager_scoped_observation_.Observe(identity_manager_);
+      }
+      // If the promo is not triggered then `this` object is already destructed
+      // as `OnFlowAborted` is invoked.
     }
   }
+}
 
-  // If we do not await a sign-in event, remove this observer.
-  if (!identity_manager_scoped_observation_.IsObserving()) {
-    SelfDestruct();
-  }
+void SearchAiModePromoTabHelper::OnFlowAborted() {
+  SelfDestruct();
 }
 
 void SearchAiModePromoTabHelper::OnPrimaryAccountChanged(
@@ -409,6 +409,7 @@
 void SearchAiModePromoTabHelper::SelfDestruct() {
   contextual_task_observer_.reset();
   identity_manager_scoped_observation_.Reset();
+  signin_promo_controller_observation_.Reset();
   if (web_contents()) {
     web_contents()->RemoveUserData(SearchAiModePromoTabHelper::UserDataKey());
   }
diff --git a/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper.h b/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper.h
index 9b2e9ac..a2006a0 100644
--- a/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper.h
+++ b/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper.h
@@ -9,7 +9,9 @@
 
 #include "base/functional/callback.h"
 #include "base/functional/callback_forward.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/contextual_tasks/contextual_tasks_ui_service.h"
+#include "chrome/browser/contextual_tasks/search_ai_mode_signin_promo_controller_observer.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -39,7 +41,8 @@
 class SearchAiModePromoTabHelper
     : public content::WebContentsObserver,
       public content::WebContentsUserData<SearchAiModePromoTabHelper>,
-      public signin::IdentityManager::Observer {
+      public signin::IdentityManager::Observer,
+      public SearchAIModeSignInPromoControllerObserver {
  public:
   SearchAiModePromoTabHelper(const SearchAiModePromoTabHelper&) = delete;
   SearchAiModePromoTabHelper& operator=(const SearchAiModePromoTabHelper&) =
@@ -51,6 +54,7 @@
       base::RepeatingCallback<
           std::unique_ptr<SearchAIModeSignInPromoController>(
               content::WebContents*)> factory_callback);
+  SearchAIModeSignInPromoController* GetSigninPromoControllerForTesting();
 
  private:
   friend class content::WebContentsUserData<SearchAiModePromoTabHelper>;
@@ -74,6 +78,9 @@
   void OnIdentityManagerShutdown(
       signin::IdentityManager* identity_manager) override;
 
+  // SearchAIModeSignInPromoControllerObserver implementation:
+  void OnFlowAborted() override;
+
   void TriggerCoBrowsePostSignIn();
   void MaybeTriggerCobrowse(const CoreAccountInfo& account_info);
 
@@ -101,6 +108,9 @@
   base::ScopedObservation<signin::IdentityManager,
                           signin::IdentityManager::Observer>
       identity_manager_scoped_observation_{this};
+  base::ScopedObservation<SearchAIModeSignInPromoController,
+                          SearchAIModeSignInPromoControllerObserver>
+      signin_promo_controller_observation_{this};
   base::WeakPtrFactory<SearchAiModePromoTabHelper> weak_ptr_factory_{this};
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 };
diff --git a/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper_browsertest.cc b/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper_browsertest.cc
index b6fdbf65..04caa5b 100644
--- a/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper_browsertest.cc
+++ b/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper_browsertest.cc
@@ -8,6 +8,8 @@
 #include <string>
 
 #include "base/functional/bind.h"
+#include "base/functional/callback_forward.h"
+#include "base/test/run_until.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/autocomplete/aim_eligibility_service_factory.h"
 #include "chrome/browser/contextual_tasks/contextual_tasks_service_factory.h"
@@ -27,12 +29,15 @@
 #include "components/omnibox/browser/mock_aim_eligibility_service.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/base/signin_switches.h"
+#include "components/signin/public/identity_manager/identity_test_utils.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/url_loader_interceptor.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "url/gurl.h"
 
+class BrowserView;
+
 namespace contextual_tasks {
 
 namespace {
@@ -43,7 +48,7 @@
   explicit MockSearchAIModeSignInPromoController(
       content::WebContents* web_contents)
       : SearchAIModeSignInPromoController(web_contents) {}
-  MOCK_METHOD(void, MaybeShowPromo, (BrowserView*), (override));
+  MOCK_METHOD(bool, MaybeShowPromo, (BrowserView*), (override));
 };
 
 }  // namespace
@@ -141,7 +146,10 @@
     CHECK(web_contents);
     auto controller =
         std::make_unique<MockSearchAIModeSignInPromoController>(web_contents);
-    EXPECT_CALL(*controller, MaybeShowPromo(testing::_)).Times(expected_calls_count);
+    EXPECT_CALL(*controller, MaybeShowPromo(testing::_))
+        .Times(expected_calls_count)
+        .WillRepeatedly(testing::Return(true));
+
     return controller;
   }
 
@@ -224,4 +232,135 @@
   EXPECT_FALSE(SearchAiModePromoTabHelper::FromWebContents(new_contents));
 }
 
+// Tests that the helper stays alive if the promo is accepted.
+IN_PROC_BROWSER_TEST_F(SearchAiModePromoTabHelperBrowserTest,
+                       StaysAliveOnPromoAccept) {
+  // 1. Navigate active tab to AI page.
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), ai_url_));
+  content::WebContents* source_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // 2. Open a new tab from the AI page.
+  content::WebContentsAddedObserver observer;
+  ASSERT_TRUE(content::ExecJs(source_contents,
+                              "window.open('" + target_url_.spec() + "')",
+                              content::EXECUTE_SCRIPT_DEFAULT_OPTIONS));
+  content::WebContents* new_contents = observer.GetWebContents();
+
+  // 3. Verify helper has been created.
+  auto* helper = SearchAiModePromoTabHelper::FromWebContents(new_contents);
+  ASSERT_TRUE(helper);
+
+  helper->SetSigninPromoControllerFactoryForTesting(base::BindRepeating(
+      &SearchAiModePromoTabHelperBrowserTest::CreateMockController,
+      base::Unretained(this), /*expected_calls_count=*/1));
+
+  // Ensure navigation is finished and promo logic runs.
+  ASSERT_TRUE(content::WaitForLoadStop(new_contents));
+
+  // 4. Simulate accept.
+  CHECK(helper->GetSigninPromoControllerForTesting());
+  helper->GetSigninPromoControllerForTesting()->HandlePromoClosing(
+      views::Widget::ClosedReason::kAcceptButtonClicked);
+
+  // 5. Verify helper is STILL ALIVE.
+  EXPECT_TRUE(SearchAiModePromoTabHelper::FromWebContents(new_contents));
+}
+
+// Tests that the helper is destroyed if the initiator tab is closed before
+// sign-in.
+IN_PROC_BROWSER_TEST_F(SearchAiModePromoTabHelperBrowserTest,
+                       AbortsFlowIfInitiatorTabIsClosedWhenUserSignsIn) {
+  // 1. Navigate active tab to AI page.
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), ai_url_));
+  content::WebContents* source_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // 2. Open a new tab from the AI page.
+  content::WebContentsAddedObserver observer;
+  ASSERT_TRUE(content::ExecJs(source_contents,
+                              "window.open('" + target_url_.spec() + "')",
+                              content::EXECUTE_SCRIPT_DEFAULT_OPTIONS));
+  content::WebContents* new_contents = observer.GetWebContents();
+
+  // 3. Verify helper has been created.
+  auto* helper = SearchAiModePromoTabHelper::FromWebContents(new_contents);
+  ASSERT_TRUE(helper);
+
+  helper->SetSigninPromoControllerFactoryForTesting(base::BindRepeating(
+      &SearchAiModePromoTabHelperBrowserTest::CreateMockController,
+      base::Unretained(this), /*expected_calls_count=*/1));
+
+  // Ensure navigation is finished and promo logic runs.
+  ASSERT_TRUE(content::WaitForLoadStop(new_contents));
+
+  // 4. Close the initiator tab.
+  // The helper will still be alive for now, but when the users interacts
+  // with the promo it will detect the initiator's destruction and will be
+  // destroyed.
+  int source_index =
+      browser()->tab_strip_model()->GetIndexOfWebContents(source_contents);
+  browser()->tab_strip_model()->CloseWebContentsAt(
+      source_index, TabCloseTypes::CLOSE_USER_GESTURE);
+
+  // Verify helper is alive (listening for sign-in).
+  ASSERT_TRUE(SearchAiModePromoTabHelper::FromWebContents(new_contents));
+
+  // 5. Simulate sign-in.
+  signin::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForProfile(browser()->profile());
+  signin::MakeAccountAvailable(
+      identity_manager,
+      signin::AccountAvailabilityOptionsBuilder()
+          .AsPrimary(signin::ConsentLevel::kSignin)
+          .WithAccessPoint(signin_metrics::AccessPoint::kSearchAIModeBubble)
+          .Build("test@gmail.com"));
+
+  // Verify helper is destroyed because initiator was destroyed.
+  EXPECT_TRUE(base::test::RunUntil([&]() {
+    return SearchAiModePromoTabHelper::FromWebContents(new_contents) == nullptr;
+  }));
+}
+
+// Tests that the helper is destroyed if the user signs in from a different
+// access point.
+IN_PROC_BROWSER_TEST_F(SearchAiModePromoTabHelperBrowserTest,
+                       DestructsOnSigninFromDifferentAccessPoint) {
+  // 1. Navigate active tab to AI page.
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), ai_url_));
+  content::WebContents* source_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // 2. Open a new tab from the AI page.
+  content::WebContentsAddedObserver observer;
+  ASSERT_TRUE(content::ExecJs(source_contents,
+                              "window.open('" + target_url_.spec() + "')",
+                              content::EXECUTE_SCRIPT_DEFAULT_OPTIONS));
+  content::WebContents* new_contents = observer.GetWebContents();
+
+  // 3. Verify helper has been created.
+  auto* helper = SearchAiModePromoTabHelper::FromWebContents(new_contents);
+  ASSERT_TRUE(helper);
+
+  helper->SetSigninPromoControllerFactoryForTesting(base::BindRepeating(
+      &SearchAiModePromoTabHelperBrowserTest::CreateMockController,
+      base::Unretained(this), /*expected_calls_count=*/1));
+
+  // Ensure navigation is finished and promo logic runs.
+  ASSERT_TRUE(content::WaitForLoadStop(new_contents));
+
+  // 4. Simulate sign-in from a different access point.
+  signin::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForProfile(browser()->profile());
+  signin::MakeAccountAvailable(
+      identity_manager,
+      signin::AccountAvailabilityOptionsBuilder()
+          .AsPrimary(signin::ConsentLevel::kSignin)
+          .WithAccessPoint(signin_metrics::AccessPoint::kSettings)
+          .Build("other@gmail.com"));
+
+  // Verify helper is destroyed.
+  EXPECT_FALSE(SearchAiModePromoTabHelper::FromWebContents(new_contents));
+}
+
 }  // namespace contextual_tasks
diff --git a/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper_interactive_uitest.cc b/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper_interactive_uitest.cc
index 32c04f4..47b2959 100644
--- a/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper_interactive_uitest.cc
+++ b/chrome/browser/contextual_tasks/search_ai_mode_promo_tab_helper_interactive_uitest.cc
@@ -8,6 +8,7 @@
 #include "base/check_deref.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/autocomplete/aim_eligibility_service_factory.h"
 #include "chrome/browser/contextual_tasks/contextual_tasks_service_factory.h"
 #include "chrome/browser/contextual_tasks/contextual_tasks_ui_service_factory.h"
@@ -341,6 +342,52 @@
                [](SearchAIModeSignInPromoView* view) {
                  view->FireTimerForTesting();
                }),
-      WaitForHide(kSearchAIModeSignInPromoFrameViewId)));
+      WaitForHide(kSearchAIModeSignInPromoFrameViewId),
+      PollUntil(
+          [this]() {
+            return SearchAiModePromoTabHelper::FromWebContents(
+                       browser()->tab_strip_model()->GetActiveWebContents()) ==
+                   nullptr;
+          },
+          "Wait for tab helper to be destroyed")));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    SearchAiModePromoTabHelperInteractiveBubbleDismissalUiTest,
+    TabHelperDestroyedOnPromoDismissal) {
+  const GURL ai_url =
+      embedded_test_server()->GetURL(kGoogleHost, kSearchAimPath);
+  const GURL result_url =
+      embedded_test_server()->GetURL(kGoogleHost, kSearchResultRelativeUrl);
+
+  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kSourceTabId);
+  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kNewTabId);
+
+  RunTestSequence(InAnyContext(
+      InstrumentTab(kSourceTabId), NavigateWebContents(kSourceTabId, ai_url),
+      WaitForElementVisible(kSourceTabId, DeepQuery{"#link"}),
+      InstrumentNextTab(kNewTabId),
+      ClickElement(kSourceTabId, DeepQuery{"#link"}),
+      WaitForWebContentsNavigation(kNewTabId, result_url),
+      // Promo should be visible for a non-signed-in user initially.
+      WaitForShow(kSearchAIModeSignInPromoFrameViewId),
+      CheckResult(
+          [this]() {
+            return SearchAiModePromoTabHelper::FromWebContents(
+                       browser()->tab_strip_model()->GetActiveWebContents()) !=
+                   nullptr;
+          },
+          true),
+      // Dismiss the promo using the close button.
+      PressButton(views::BubbleFrameView::kCloseButtonElementId),
+      WaitForHide(kSearchAIModeSignInPromoFrameViewId),
+      // Verify the tab helper is destroyed.
+      PollUntil(
+          [this]() {
+            return SearchAiModePromoTabHelper::FromWebContents(
+                       browser()->tab_strip_model()->GetActiveWebContents()) ==
+                   nullptr;
+          },
+          "Wait for tab helper destruction")));
 }
 }  // namespace contextual_tasks
diff --git a/chrome/browser/contextual_tasks/search_ai_mode_signin_promo_controller_observer.h b/chrome/browser/contextual_tasks/search_ai_mode_signin_promo_controller_observer.h
new file mode 100644
index 0000000..cc6c0ae7
--- /dev/null
+++ b/chrome/browser/contextual_tasks/search_ai_mode_signin_promo_controller_observer.h
@@ -0,0 +1,20 @@
+// 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_CONTEXTUAL_TASKS_SEARCH_AI_MODE_SIGNIN_PROMO_CONTROLLER_OBSERVER_H_
+#define CHROME_BROWSER_CONTEXTUAL_TASKS_SEARCH_AI_MODE_SIGNIN_PROMO_CONTROLLER_OBSERVER_H_
+
+#include "base/observer_list_types.h"
+
+namespace contextual_tasks {
+
+class SearchAIModeSignInPromoControllerObserver : public base::CheckedObserver {
+ public:
+  // Called when the sign-in flow is aborted.
+  virtual void OnFlowAborted() {}
+};
+
+}  // namespace contextual_tasks
+
+#endif  // CHROME_BROWSER_CONTEXTUAL_TASKS_SEARCH_AI_MODE_SIGNIN_PROMO_CONTROLLER_OBSERVER_H_
diff --git a/chrome/browser/devtools/chrome_devtools_session.cc b/chrome/browser/devtools/chrome_devtools_session.cc
index 07560364..e66af0ed 100644
--- a/chrome/browser/devtools/chrome_devtools_session.cc
+++ b/chrome/browser/devtools/chrome_devtools_session.cc
@@ -99,10 +99,7 @@
       channel->GetClient()->IsTrusted()) {
     extensions_handler_ = std::make_unique<ExtensionsHandler>(
         &dispatcher_, agent_host->GetId(),
-        channel->GetClient()->AllowUnsafeOperations() &&
-            base::CommandLine::ForCurrentProcess()->HasSwitch(
-                ::switches::kEnableUnsafeExtensionDebugging) &&
-            agent_host->GetType() == content::DevToolsAgentHost::kTypeBrowser);
+        agent_host->GetType() == content::DevToolsAgentHost::kTypeBrowser);
   }
   if (IsDomainAvailableToUntrustedClient<EmulationHandler>() ||
       channel->GetClient()->IsTrusted()) {
diff --git a/chrome/browser/devtools/protocol/devtools_extensions_browsertest.cc b/chrome/browser/devtools/protocol/devtools_extensions_browsertest.cc
index c22b22e..3120a75 100644
--- a/chrome/browser/devtools/protocol/devtools_extensions_browsertest.cc
+++ b/chrome/browser/devtools/protocol/devtools_extensions_browsertest.cc
@@ -105,55 +105,8 @@
   extensions::ScopedTestMV2Enabler mv2_enabler_;
 };
 
-class DevToolsExtensionsProtocolWithUnsafeDebuggingTest
-    : public DevToolsExtensionsProtocolTest {
- public:
-  DevToolsExtensionsProtocolWithUnsafeDebuggingTest() {
-    scoped_feature_list_.InitAndEnableFeature(
-        extensions_features::kExtensionDisableUnsupportedDeveloper);
-  }
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    DevToolsExtensionsProtocolTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(::switches::kEnableUnsafeExtensionDebugging);
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest, CannotInstallExtension) {
-  ASSERT_FALSE(SendLoadUnpackedCommand("simple_background_page"));
-}
 
 IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest,
-                       CannotUninstallExtension) {
-  auto extension =
-      extensions::ExtensionBuilder("unpacked")
-          .SetLocation(extensions::mojom::ManifestLocation::kUnpacked)
-          .Build();
-  extensions::ExtensionRegistrar::Get(browser()->profile())
-      ->AddExtension(extension.get());
-
-  std::string id = extension.get()->id();
-  extensions::ExtensionRegistry* registry =
-      extensions::ExtensionRegistry::Get(browser()->profile());
-  const extensions::Extension* extension_before =
-      registry->GetInstalledExtension(id);
-  ASSERT_TRUE(extension_before);
-
-  base::DictValue params;
-  params.Set("id", id);
-  const base::DictValue* uninstall_result =
-      SendCommandSync("Extensions.uninstall", std::move(params));
-  ASSERT_FALSE(uninstall_result);
-
-  const extensions::Extension* extension_after =
-      registry->GetInstalledExtension(id);
-  ASSERT_TRUE(extension_after);
-}
-
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
                        CanInstallExtension) {
   const base::DictValue* result =
       SendLoadUnpackedCommand("simple_background_page");
@@ -172,7 +125,7 @@
                                                     browser()->profile()));
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest,
                        CanInstallExtensionAndEnableItForIncognito) {
   const base::DictValue* result =
       SendLoadUnpackedCommand("simple_background_page", true);
@@ -191,13 +144,12 @@
                                                    browser()->profile()));
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
-                       ThrowsOnWrongPath) {
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest, ThrowsOnWrongPath) {
   const base::DictValue* result = SendLoadUnpackedCommand("non-existent");
   ASSERT_FALSE(result);
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest,
                        InstalledExtensionIsNotEnabledInIncognito) {
   const base::DictValue* result = SendLoadUnpackedCommand(
       "simple_background_page", /*enable_in_incognito=*/false);
@@ -218,8 +170,7 @@
                                                     browser()->profile()));
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
-                       CanUninstallExtension) {
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest, CanUninstallExtension) {
   const base::DictValue* install_result =
       SendLoadUnpackedCommand("simple_background_page");
 
@@ -241,7 +192,7 @@
   ASSERT_FALSE(extension_after);
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest,
                        PRE_ExtensionMarkedAsInstalledViaCdp) {
   const base::DictValue* install_result =
       SendLoadUnpackedCommand("simple_background_page");
@@ -255,7 +206,7 @@
               extensions::Extension::INSTALLED_VIA_CDP);
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest,
                        ExtensionMarkedAsInstalledViaCdp) {
   base::RunLoop run_loop;
   extensions::ExtensionSystem::Get(browser()->profile())
@@ -279,7 +230,7 @@
   EXPECT_FALSE(found);
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest,
                        CannotUninstallNonUnpackedExtension) {
   auto extension =
       extensions::ExtensionBuilder("unpacked")
@@ -306,7 +257,7 @@
   ASSERT_TRUE(extension_after);
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest,
                        FailsToUninstallNonexistentExtension) {
   extensions::ExtensionRegistry* registry =
       extensions::ExtensionRegistry::Get(browser()->profile());
@@ -365,8 +316,7 @@
   return nullptr;
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
-                       CanGetStorageValues) {
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest, CanGetStorageValues) {
   ExtensionTestMessageListener activated_listener("WORKER_ACTIVATED");
 
   const base::DictValue* load_result =
@@ -431,7 +381,7 @@
   ASSERT_FALSE(get_result_3->FindDict("data")->contains("remove-on-clear"));
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest,
                        CanGetStorageValuesBackgroundPage) {
   const base::DictValue* load_result =
       SendLoadUnpackedCommand("background_page_storage_access");
@@ -455,7 +405,7 @@
                                  base::DictValue()));
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest,
                        CanGetStorageValuesContentScript) {
   const base::DictValue* load_result =
       SendLoadUnpackedCommand("simple_content_script");
@@ -481,7 +431,7 @@
                                  base::DictValue()));
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest,
                        CannotGetStorageValuesWithoutContentScript) {
   // Load an extension with no associated content scripts.
   const base::DictValue* load_result =
@@ -514,7 +464,7 @@
 
 // Test to ensure that the target associated with an extension service worker
 // cannot access data from the storage associated with another extension.
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest,
                        CannotGetStorageValuesUnrelatedTarget) {
   ExtensionTestMessageListener activated_listener("WORKER_ACTIVATED");
 
@@ -552,8 +502,7 @@
   ASSERT_EQ(*error()->FindString("message"), "Extension not found.");
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
-                       CanGetExtensions) {
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest, CanGetExtensions) {
   base::FilePath extensions_dir =
       base::PathService::CheckedGet(chrome::DIR_TEST_DATA)
           .AppendASCII("devtools")
@@ -602,7 +551,7 @@
             unpacked_path.AsUTF8Unsafe());
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest,
                        TriggerActionShowsSidePanel) {
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
   ExtensionTestMessageListener activated_listener("running");
@@ -630,7 +579,7 @@
       SidePanelEntry::Key(SidePanelEntry::Id::kExtension, extension->id())));
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest,
                        TriggerActionShowsPopup) {
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
 
@@ -662,7 +611,7 @@
   EXPECT_TRUE(action_view->IsShowingPopup());
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolWithUnsafeDebuggingTest,
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionsProtocolTest,
                        TriggerActionDispatchesEvent) {
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
 
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index be7751a..f5de95f 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -376,8 +376,6 @@
     "managed_toolbar_pin_mode.h",
     "management/management_util.cc",
     "management/management_util.h",
-    "manifest_v2_experiment_manager.cc",
-    "manifest_v2_experiment_manager.h",
     "menu_icon_loader.h",
     "menu_manager.cc",
     "menu_manager.h",
@@ -449,8 +447,6 @@
     "updater/extension_updater_switches.h",
     "user_script_listener.cc",
     "user_script_listener.h",
-    "webstore_install_helper.cc",
-    "webstore_install_helper.h",
     "webstore_reinstaller.cc",
     "webstore_reinstaller.h",
     "webstore_standalone_installer.cc",
@@ -509,6 +505,7 @@
     "//chrome/browser/gcm",
     "//chrome/browser/google",
     "//chrome/browser/history",
+    "//chrome/browser/image_fetcher",
     "//chrome/browser/infobars",
     "//chrome/browser/lifetime:termination_notification",
     "//chrome/browser/net",
@@ -1681,6 +1678,7 @@
     "//chrome/browser/download",
     "//chrome/browser/extensions",
     "//chrome/browser/external_protocol",
+    "//chrome/browser/image_fetcher",
     "//chrome/browser/media/webrtc",
     "//chrome/browser/prefetch",
     "//chrome/browser/profiles:profile_manager",
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_ai_util.cc b/chrome/browser/extensions/api/autofill_private/autofill_ai_util.cc
index 983bea0..84dacfa 100644
--- a/chrome/browser/extensions/api/autofill_private/autofill_ai_util.cc
+++ b/chrome/browser/extensions/api/autofill_private/autofill_ai_util.cc
@@ -108,6 +108,8 @@
       entity_instance_with_labels.wallet_entity_url =
           autofill::GetWalletManagementURL(entity_instance);
     }
+    entity_instance_with_labels.is_read_only =
+        entity_instance.are_attributes_read_only().value();
   }
 }
 
@@ -211,7 +213,8 @@
       /*use_date=*/base::Time::Now(),
       save_entity_to_wallet ? EntityInstance::RecordType::kServerWallet
                             : EntityInstance::RecordType::kLocal,
-      EntityInstance::AreAttributesReadOnly(false),
+      EntityInstance::AreAttributesReadOnly(
+          private_api_entity_instance.is_read_only.value_or(false)),
       /*frecency_override=*/"");
 }
 
@@ -281,6 +284,8 @@
   private_api_entity_instance.stored_in_wallet =
       (entity_instance.record_type() ==
        EntityInstance::RecordType::kServerWallet);
+  private_api_entity_instance.is_read_only =
+      entity_instance.are_attributes_read_only().value();
   return private_api_entity_instance;
 }
 
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_api_browsertest.cc b/chrome/browser/extensions/api/autofill_private/autofill_private_api_browsertest.cc
index f3a31bb..bef2bdb 100644
--- a/chrome/browser/extensions/api/autofill_private/autofill_private_api_browsertest.cc
+++ b/chrome/browser/extensions/api/autofill_private/autofill_private_api_browsertest.cc
@@ -413,6 +413,7 @@
   ASSERT_TRUE(RunAutofillSubtest("loadEmptyEntityInstancesList"));
   ASSERT_TRUE(RunAutofillSubtest("testExpectedLabelsAreGenerated"));
   ASSERT_TRUE(RunAutofillSubtest("shouldAuthenticateToView"));
+  ASSERT_TRUE(RunAutofillSubtest("verifyIsReadOnlyTrueRetained"));
   // Test that retrieving general entity type information works.
   ASSERT_TRUE(RunAutofillSubtest("getWritableEntityTypes"));
   ASSERT_TRUE(RunAutofillSubtest("getAllAttributeTypesForEntityTypeName"));
diff --git a/chrome/browser/extensions/api/braille_display_private/BUILD.gn b/chrome/browser/extensions/api/braille_display_private/BUILD.gn
index 5e9606e..6ac988a3 100644
--- a/chrome/browser/extensions/api/braille_display_private/BUILD.gn
+++ b/chrome/browser/extensions/api/braille_display_private/BUILD.gn
@@ -39,6 +39,7 @@
   deps = [
     "//build:chromeos_buildflags",
     "//chrome/browser/profiles:profile",
+    "//device/bluetooth/public/cpp",
   ]
 
   if (is_chromeos) {
diff --git a/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc b/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc
index d0c4ba4..85d6e07 100644
--- a/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc
+++ b/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc
@@ -15,6 +15,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS)
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
+#include "device/bluetooth/public/cpp/bluetooth_address.h"
 #include "extensions/browser/extensions_browser_client.h"
 #endif
 
@@ -190,8 +191,14 @@
   EXTENSION_FUNCTION_VALIDATE(args().size() >= 1);
   EXTENSION_FUNCTION_VALIDATE(args()[0].is_string());
   const std::string& address = args()[0].GetString();
+  // Validate that the address is a well-formed Bluetooth MAC address.
+  // CanonicalizeBluetoothAddress returns empty on invalid input.
+  std::string canonical = device::CanonicalizeBluetoothAddress(address);
+  if (canonical.empty()) {
+    return RespondNow(Error("Invalid Bluetooth address"));
+  }
   ash::AccessibilityManager::Get()->UpdateBluetoothBrailleDisplayAddress(
-      address);
+      canonical);
   return RespondNow(NoArguments());
 #else
   NOTREACHED();
diff --git a/chrome/browser/extensions/api/braille_display_private/braille_display_private_api_unittest.cc b/chrome/browser/extensions/api/braille_display_private/braille_display_private_api_unittest.cc
new file mode 100644
index 0000000..1a82dd2
--- /dev/null
+++ b/chrome/browser/extensions/api/braille_display_private/braille_display_private_api_unittest.cc
@@ -0,0 +1,137 @@
+// 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/extensions/api/braille_display_private/braille_display_private_api.h"
+
+#include "base/test/gtest_util.h"
+#include "chrome/browser/extensions/extension_service_test_base.h"
+#include "chrome/test/base/testing_profile.h"
+#include "device/bluetooth/public/cpp/bluetooth_address.h"
+#include "extensions/browser/api_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+namespace api {
+namespace braille_display_private {
+
+using BrailleDisplayPrivateApiUnittest = ExtensionServiceTestBase;
+
+// Verifies that updateBluetoothBrailleDisplayAddress rejects a shell injection
+// payload.
+TEST_F(BrailleDisplayPrivateApiUnittest,
+       UpdateBluetoothBrailleDisplayAddress_RejectsShellInjection) {
+  InitializeEmptyExtensionService();
+  auto function = base::MakeRefCounted<
+      BrailleDisplayPrivateUpdateBluetoothBrailleDisplayAddressFunction>();
+  EXPECT_EQ("Invalid Bluetooth address",
+            api_test_utils::RunFunctionAndReturnError(
+                function.get(), R"(["; touch /tmp/pwned; #"])", profile()));
+}
+
+// Verifies that updateBluetoothBrailleDisplayAddress rejects a command
+// substitution payload.
+TEST_F(BrailleDisplayPrivateApiUnittest,
+       UpdateBluetoothBrailleDisplayAddress_RejectsCommandSubstitution) {
+  InitializeEmptyExtensionService();
+  auto function = base::MakeRefCounted<
+      BrailleDisplayPrivateUpdateBluetoothBrailleDisplayAddressFunction>();
+  EXPECT_EQ("Invalid Bluetooth address",
+            api_test_utils::RunFunctionAndReturnError(
+                function.get(), R"--(["$(reboot)"])--", profile()));
+}
+
+// Verifies that a valid address with an appended command is rejected.
+TEST_F(BrailleDisplayPrivateApiUnittest,
+       UpdateBluetoothBrailleDisplayAddress_RejectsAppendedCommand) {
+  InitializeEmptyExtensionService();
+  auto function = base::MakeRefCounted<
+      BrailleDisplayPrivateUpdateBluetoothBrailleDisplayAddressFunction>();
+  EXPECT_EQ("Invalid Bluetooth address",
+            api_test_utils::RunFunctionAndReturnError(
+                function.get(), R"(["AA:BB:CC:DD:EE:FF; rm -rf /"])",
+                profile()));
+}
+
+// Verifies that an empty string is rejected.
+TEST_F(BrailleDisplayPrivateApiUnittest,
+       UpdateBluetoothBrailleDisplayAddress_RejectsEmptyString) {
+  InitializeEmptyExtensionService();
+  auto function = base::MakeRefCounted<
+      BrailleDisplayPrivateUpdateBluetoothBrailleDisplayAddressFunction>();
+  EXPECT_EQ("Invalid Bluetooth address",
+            api_test_utils::RunFunctionAndReturnError(function.get(),
+                                                      R"([""])", profile()));
+}
+
+// Verifies that a non-hex string is rejected.
+TEST_F(BrailleDisplayPrivateApiUnittest,
+       UpdateBluetoothBrailleDisplayAddress_RejectsNonHex) {
+  InitializeEmptyExtensionService();
+  auto function = base::MakeRefCounted<
+      BrailleDisplayPrivateUpdateBluetoothBrailleDisplayAddressFunction>();
+  EXPECT_EQ("Invalid Bluetooth address",
+            api_test_utils::RunFunctionAndReturnError(
+                function.get(), R"(["not-an-address"])", profile()));
+}
+
+// Verifies that a truncated address is rejected.
+TEST_F(BrailleDisplayPrivateApiUnittest,
+       UpdateBluetoothBrailleDisplayAddress_RejectsTruncatedAddress) {
+  InitializeEmptyExtensionService();
+  auto function = base::MakeRefCounted<
+      BrailleDisplayPrivateUpdateBluetoothBrailleDisplayAddressFunction>();
+  EXPECT_EQ("Invalid Bluetooth address",
+            api_test_utils::RunFunctionAndReturnError(
+                function.get(), R"(["AA:BB:CC:DD:EE"])", profile()));
+}
+
+// Verifies that an overlong address is rejected.
+TEST_F(BrailleDisplayPrivateApiUnittest,
+       UpdateBluetoothBrailleDisplayAddress_RejectsOverlongAddress) {
+  InitializeEmptyExtensionService();
+  auto function = base::MakeRefCounted<
+      BrailleDisplayPrivateUpdateBluetoothBrailleDisplayAddressFunction>();
+  EXPECT_EQ("Invalid Bluetooth address",
+            api_test_utils::RunFunctionAndReturnError(
+                function.get(), R"(["AA:BB:CC:DD:EE:FF:00"])", profile()));
+}
+
+// Verifies that CanonicalizeBluetoothAddress CHECKs on an invalid address,
+// mirroring the defense-in-depth CHECK in RestartBrltty.
+using BluetoothAddressDeathTest = testing::Test;
+
+TEST_F(BluetoothAddressDeathTest, InvalidAddressCHECKFails) {
+  EXPECT_CHECK_DEATH({
+    std::string canonical =
+        device::CanonicalizeBluetoothAddress("; touch /tmp/pwned; #");
+    CHECK(!canonical.empty());
+  });
+}
+
+// Verifies that valid Bluetooth MAC addresses pass validation via
+// CanonicalizeBluetoothAddress (the same function used by the API and
+// RestartBrltty).
+TEST(BluetoothAddressValidationTest, AcceptsColonSeparated) {
+  EXPECT_FALSE(
+      device::CanonicalizeBluetoothAddress("AA:BB:CC:DD:EE:FF").empty());
+}
+
+TEST(BluetoothAddressValidationTest, AcceptsLowercase) {
+  EXPECT_FALSE(
+      device::CanonicalizeBluetoothAddress("aa:bb:cc:dd:ee:ff").empty());
+}
+
+TEST(BluetoothAddressValidationTest, AcceptsDashSeparated) {
+  EXPECT_FALSE(
+      device::CanonicalizeBluetoothAddress("AA-BB-CC-DD-EE-FF").empty());
+}
+
+TEST(BluetoothAddressValidationTest, AcceptsNoSeparators) {
+  EXPECT_FALSE(
+      device::CanonicalizeBluetoothAddress("AABBCCDDEEFF").empty());
+}
+
+}  // namespace braille_display_private
+}  // namespace api
+}  // namespace extensions
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
index b56d4378..4b8eb46 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
@@ -39,7 +39,6 @@
 #include "chrome/browser/extensions/extension_service_test_with_install.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/extensions/external_provider_manager.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/extensions/signin_test_util.h"
 #include "chrome/browser/extensions/sync/account_extension_tracker.h"
 #include "chrome/browser/extensions/sync/extension_sync_data.h"
@@ -73,6 +72,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_observer.h"
 #include "extensions/browser/extension_util.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/browser/mock_external_provider.h"
 #include "extensions/browser/permissions/permissions_test_util.h"
 #include "extensions/browser/permissions/permissions_updater.h"
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc b/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc
index a51d439..3d908a7e 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_apitest.cc
@@ -16,7 +16,6 @@
 #include "chrome/browser/extensions/api/developer_private/developer_private_functions.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/common/chrome_paths.h"
@@ -30,6 +29,7 @@
 #include "extensions/browser/api_test_utils.h"
 #include "extensions/browser/browsertest_util.h"
 #include "extensions/browser/extension_host_test_helper.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/browser/mv2_experiment_stage.h"
 #include "extensions/browser/offscreen_document_host.h"
 #include "extensions/buildflags/buildflags.h"
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_functions.cc b/chrome/browser/extensions/api/developer_private/developer_private_functions.cc
index ddf0ac06..5273e98 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_functions.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_functions.cc
@@ -34,7 +34,6 @@
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/extensions/install_verifier_factory.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/extensions/sync/account_extension_tracker.h"
 #include "chrome/browser/extensions/sync/extension_sync_util.h"
 #include "chrome/browser/extensions/updater/extension_updater.h"
@@ -60,6 +59,7 @@
 #include "extensions/browser/file_highlighter.h"
 #include "extensions/browser/install_verifier.h"
 #include "extensions/browser/management_policy.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/browser/path_util.h"
 #include "extensions/browser/permissions/permissions_updater.h"
 #include "extensions/browser/permissions/scripting_permissions_modifier.h"
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc b/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
index 0f55a475..0dee6b0 100644
--- a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
+++ b/chrome/browser/extensions/api/developer_private/extension_info_generator.cc
@@ -24,7 +24,6 @@
 #include "chrome/browser/extensions/extension_safety_check_utils.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_util.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/extensions/shared_module_service_factory.h"
 #include "chrome/browser/extensions/sync/account_extension_tracker.h"
 #include "chrome/browser/profiles/profile.h"
@@ -46,6 +45,7 @@
 #include "extensions/browser/extension_util.h"
 #include "extensions/browser/icon_util.h"
 #include "extensions/browser/image_loader.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/browser/path_util.h"
 #include "extensions/browser/permissions/site_permissions_helper.h"
 #include "extensions/browser/shared_module_service.h"
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc b/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc
index 68b59694..fcc5573 100644
--- a/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc
+++ b/chrome/browser/extensions/api/developer_private/extension_info_generator_unittest.cc
@@ -29,7 +29,6 @@
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_service_test_with_install.h"
 #include "chrome/browser/extensions/extension_util.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/extensions/signin_test_util.h"
 #include "chrome/browser/extensions/sync/account_extension_tracker.h"
 #include "chrome/browser/extensions/sync/extension_sync_util.h"
@@ -50,6 +49,7 @@
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registrar.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/browser/mv2_experiment_stage.h"
 #include "extensions/browser/permissions/permissions_test_util.h"
 #include "extensions/browser/permissions/permissions_updater.h"
diff --git a/chrome/browser/extensions/api/developer_private/profile_info_generator.cc b/chrome/browser/extensions/api/developer_private/profile_info_generator.cc
index 6db06e8..16100c3 100644
--- a/chrome/browser/extensions/api/developer_private/profile_info_generator.cc
+++ b/chrome/browser/extensions/api/developer_private/profile_info_generator.cc
@@ -6,13 +6,13 @@
 
 #include "build/build_config.h"
 #include "chrome/browser/extensions/extension_management.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/supervised_user/supervised_user_browser_utils.h"
 #include "chrome/common/pref_names.h"
 #include "components/policy/core/common/policy_pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/buildflags/buildflags.h"
 
 static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
diff --git a/chrome/browser/extensions/api/management/chrome_management_api_delegate_non_android.cc b/chrome/browser/extensions/api/management/chrome_management_api_delegate_non_android.cc
index f01e9e8..eca9581c 100644
--- a/chrome/browser/extensions/api/management/chrome_management_api_delegate_non_android.cc
+++ b/chrome/browser/extensions/api/management/chrome_management_api_delegate_non_android.cc
@@ -17,12 +17,12 @@
 #include "chrome/browser/extensions/api/management/chrome_management_api_delegate.h"
 #include "chrome/browser/extensions/chrome_extension_function_details.h"
 #include "chrome/browser/extensions/launch_util.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/dialogs/browser_dialogs.h"
 #include "chrome/browser/ui/extensions/extensions_dialogs.h"
 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
@@ -60,6 +60,7 @@
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/browser/mv2_experiment_stage.h"
 #include "extensions/common/api/management.h"
 #include "extensions/common/extension.h"
@@ -300,8 +301,10 @@
     ManagementCreateAppShortcutFunction* function,
     const Extension* extension,
     std::string* error) const {
-  Browser* browser = chrome::FindBrowserWithProfile(
-      Profile::FromBrowserContext(function->browser_context()));
+  BrowserWindowInterface* browser =
+      ProfileBrowserCollection::GetForProfile(
+          Profile::FromBrowserContext(function->browser_context()))
+          ->GetLastActiveBrowser();
   if (!browser) {
     // Shouldn't happen if we have user gesture.
     *error = extension_management_api_constants::kNoBrowserToCreateShortcut;
@@ -309,7 +312,7 @@
   }
 
   chrome::ShowCreateChromeAppShortcutsDialog(
-      browser->window()->GetNativeWindow(), browser->profile(), extension,
+      browser->GetWindow()->GetNativeWindow(), browser->GetProfile(), extension,
       base::BindOnce(
           &ManagementCreateAppShortcutFunction::OnCloseShortcutPrompt,
           function));
diff --git a/chrome/browser/extensions/api/webstore_private/extension_install_status.cc b/chrome/browser/extensions/api/webstore_private/extension_install_status.cc
index f121e3d..8e354e46 100644
--- a/chrome/browser/extensions/api/webstore_private/extension_install_status.cc
+++ b/chrome/browser/extensions/api/webstore_private/extension_install_status.cc
@@ -8,14 +8,12 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/extensions/extension_management.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/policy/cloud/extension_install_policy_service.h"
 #include "chrome/browser/policy/cloud/extension_install_policy_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/supervised_user/supervised_user_browser_utils.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/grit/generated_resources.h"
 #include "components/crx_file/id_util.h"
 #include "components/policy/core/common/cloud/cloud_policy_client_types.h"
 #include "components/prefs/pref_service.h"
@@ -24,11 +22,13 @@
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/managed_installation_mode.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/buildflags/buildflags.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_urls.h"
 #include "extensions/common/manifest_constants.h"
 #include "extensions/common/permissions/permission_set.h"
+#include "extensions/strings/grit/extensions_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
 static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
index b9500af..e064198d 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
@@ -32,7 +32,6 @@
 #include "chrome/browser/extensions/extension_allowlist_factory.h"
 #include "chrome/browser/extensions/extension_management.h"
 #include "chrome/browser/extensions/install_tracker_factory.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/policy/policy_ui_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_observer.h"
@@ -43,7 +42,6 @@
 #include "chrome/browser/ui/extensions/extensions_dialogs.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/grit/generated_resources.h"
 #include "components/crx_file/id_util.h"
 #include "components/policy/core/common/cloud/cloud_policy_manager.h"
 #include "components/prefs/pref_service.h"
@@ -67,6 +65,7 @@
 #include "extensions/browser/extensions_browser_client.h"
 #include "extensions/browser/install_approval.h"
 #include "extensions/browser/install_tracker.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/browser/mv2_experiment_stage.h"
 #include "extensions/browser/pref_names.h"
 #include "extensions/browser/scoped_active_install.h"
@@ -77,6 +76,7 @@
 #include "extensions/common/manifest_constants.h"
 #include "extensions/common/manifest_handlers/permissions_parser.h"
 #include "extensions/common/permissions/permission_set.h"
+#include "extensions/strings/grit/extensions_strings.h"
 #include "net/base/load_flags.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
@@ -486,12 +486,11 @@
   scoped_active_install_ =
       std::make_unique<ScopedActiveInstall>(tracker, install_data);
 
-  network::mojom::URLLoaderFactory* loader_factory = nullptr;
+  scoped_refptr<network::SharedURLLoaderFactory> loader_factory = nullptr;
   if (!icon_url.is_empty()) {
     loader_factory = browser_context()
                          ->GetDefaultStoragePartition()
-                         ->GetURLLoaderFactoryForBrowserProcess()
-                         .get();
+                         ->GetURLLoaderFactoryForBrowserProcess();
   }
 
   auto helper = base::MakeRefCounted<WebstoreInstallHelper>(
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.h b/chrome/browser/extensions/api/webstore_private/webstore_private_api.h
index e358970..764e68a 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.h
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.h
@@ -15,7 +15,6 @@
 #include "build/build_config.h"
 #include "chrome/browser/extensions/api/webstore_private/extension_install_status.h"
 #include "chrome/browser/extensions/extension_install_prompt.h"
-#include "chrome/browser/extensions/webstore_install_helper.h"
 #include "chrome/browser/supervised_user/supervised_user_extensions_metrics_recorder.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/common/extensions/api/webstore_private.h"
@@ -24,6 +23,7 @@
 #include "extensions/browser/active_install_data.h"
 #include "extensions/browser/extension_function.h"
 #include "extensions/browser/supervised_user_extensions_delegate.h"
+#include "extensions/browser/webstore_install_helper.h"
 #include "extensions/browser/webstore_installer.h"
 #include "extensions/buildflags/buildflags.h"
 #include "third_party/skia/include/core/SkBitmap.h"
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.cc b/chrome/browser/extensions/chrome_extensions_browser_client.cc
index 667603d..da1b390 100644
--- a/chrome/browser/extensions/chrome_extensions_browser_client.cc
+++ b/chrome/browser/extensions/chrome_extensions_browser_client.cc
@@ -57,12 +57,14 @@
 #include "chrome/browser/extensions/install_tracker_factory.h"
 #include "chrome/browser/extensions/install_verifier_factory.h"
 #include "chrome/browser/extensions/pref_mapping.h"
+#include "chrome/browser/extensions/profile_util.h"
 #include "chrome/browser/extensions/shared_module_service_factory.h"
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/extensions/updater/chrome_update_client_config.h"
 #include "chrome/browser/extensions/updater/extension_updater.h"
 #include "chrome/browser/extensions/user_script_listener.h"
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
+#include "chrome/browser/image_fetcher/image_decoder_impl.h"
 #include "chrome/browser/media/webrtc/media_device_salt_service_factory.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/prefetch/pref_names.h"
@@ -324,6 +326,16 @@
       .ApplyProfileSelection(Profile::FromBrowserContext(context));
 }
 
+content::BrowserContext* ChromeExtensionsBrowserClient::
+    GetContextRedirectedToOriginalWithoutAshInternals(
+        content::BrowserContext* context) {
+  return ProfileSelections::Builder()
+      .WithRegular(ProfileSelection::kRedirectedToOriginal)
+      .WithGuest(ProfileSelection::kRedirectedToOriginal)
+      .Build()
+      .ApplyProfileSelection(Profile::FromBrowserContext(context));
+}
+
 content::BrowserContext* ChromeExtensionsBrowserClient::GetContextOwnInstance(
     content::BrowserContext* context) {
   return ProfileSelections::Builder()
@@ -1185,4 +1197,15 @@
       Profile::FromBrowserContext(context), download);
 }
 
+std::unique_ptr<image_fetcher::ImageDecoder>
+ChromeExtensionsBrowserClient::CreateImageDecoder() {
+  return std::make_unique<ImageDecoderImpl>();
+}
+
+bool ChromeExtensionsBrowserClient::CanUseNonComponentExtensions(
+    content::BrowserContext* context) {
+  return profile_util::ProfileCanUseNonComponentExtensions(
+      Profile::FromBrowserContext(context));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.h b/chrome/browser/extensions/chrome_extensions_browser_client.h
index baf9645..105da042 100644
--- a/chrome/browser/extensions/chrome_extensions_browser_client.h
+++ b/chrome/browser/extensions/chrome_extensions_browser_client.h
@@ -103,6 +103,8 @@
       content::BrowserContext* context) override;
   content::BrowserContext* GetContextRedirectedToOriginal(
       content::BrowserContext* context) override;
+  content::BrowserContext* GetContextRedirectedToOriginalWithoutAshInternals(
+      content::BrowserContext* context) override;
   content::BrowserContext* GetContextOwnInstance(
       content::BrowserContext* context) override;
   content::BrowserContext* GetContextForOriginalOnly(
@@ -317,6 +319,8 @@
   scoped_refptr<CrxInstaller> CreateCrxInstallerFromDownloadItem(
       content::BrowserContext* context,
       const download::DownloadItem& download) override;
+  std::unique_ptr<image_fetcher::ImageDecoder> CreateImageDecoder() override;
+  bool CanUseNonComponentExtensions(content::BrowserContext* context) override;
 
   static void set_did_chrome_update_for_testing(bool did_update);
 
diff --git a/chrome/browser/extensions/extension_management.h b/chrome/browser/extensions/extension_management.h
index 44881ed..2ef19a1 100644
--- a/chrome/browser/extensions/extension_management.h
+++ b/chrome/browser/extensions/extension_management.h
@@ -99,6 +99,11 @@
       int manifest_version,
       const std::string& extension_id,
       Manifest::Type manifest_type) override;
+  bool IsAllowedManifestVersion(int manifest_version,
+                                const std::string& extension_id,
+                                Manifest::Type manifest_type) override;
+  bool IsAllowedManifestVersion(const Extension* extension) override;
+
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
@@ -152,11 +157,6 @@
   bool IsAllowedManifestType(Manifest::Type manifest_type,
                              const std::string& extension_id) const;
 
-  bool IsAllowedManifestVersion(int manifest_version,
-                                const std::string& extension_id,
-                                Manifest::Type manifest_type);
-  bool IsAllowedManifestVersion(const Extension* extension);
-
   bool IsAllowedByUnpublishedAvailabilityPolicy(const Extension* extension);
 
   // Returns false if the extension is loaded as unpacked and the developer mode
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index e20eeb5a..25d58cae 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -52,7 +52,6 @@
 #include "chrome/browser/extensions/external_provider_manager.h"
 #include "chrome/browser/extensions/install_verifier_factory.h"
 #include "chrome/browser/extensions/installed_loader.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/extensions/omaha_attributes_handler.h"
 #include "chrome/browser/extensions/profile_util.h"
 #include "chrome/browser/extensions/sync/extension_sync_service.h"
@@ -95,6 +94,7 @@
 #include "extensions/browser/install_flag.h"
 #include "extensions/browser/install_verifier.h"
 #include "extensions/browser/management_policy.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/browser/mv2_experiment_stage.h"
 #include "extensions/browser/pending_extension_manager.h"
 #include "extensions/browser/permissions/permissions_updater.h"
diff --git a/chrome/browser/extensions/keyed_services/chrome_browser_context_keyed_service_factories.cc b/chrome/browser/extensions/keyed_services/chrome_browser_context_keyed_service_factories.cc
index f215993..6de7ff8 100644
--- a/chrome/browser/extensions/keyed_services/chrome_browser_context_keyed_service_factories.cc
+++ b/chrome/browser/extensions/keyed_services/chrome_browser_context_keyed_service_factories.cc
@@ -26,12 +26,12 @@
 #include "chrome/browser/extensions/forced_extensions/install_stage_tracker_factory.h"
 #include "chrome/browser/extensions/install_tracker_factory.h"
 #include "chrome/browser/extensions/install_verifier_factory.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/extensions/menu_manager_factory.h"
 #include "chrome/browser/extensions/shared_module_service_factory.h"
 #include "chrome/browser/extensions/sync/account_extension_tracker.h"
 #include "chrome/browser/extensions/sync/extension_sync_service_factory.h"
 #include "chrome/browser/extensions/updater/extension_updater_factory.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/browser/permissions/permissions_updater.h"
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/browser/extensions/manifest_v2_experiment_manager_browsertest.cc b/chrome/browser/extensions/manifest_v2_experiment_manager_browsertest.cc
index 4ebdc835..58a41577 100644
--- a/chrome/browser/extensions/manifest_v2_experiment_manager_browsertest.cc
+++ b/chrome/browser/extensions/manifest_v2_experiment_manager_browsertest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 
 #include "base/one_shot_event.h"
 #include "base/strings/stringprintf.h"
diff --git a/chrome/browser/extensions/manifest_v2_experiment_manager_unittest.cc b/chrome/browser/extensions/manifest_v2_experiment_manager_unittest.cc
index 6c5983f2..b809a1f 100644
--- a/chrome/browser/extensions/manifest_v2_experiment_manager_unittest.cc
+++ b/chrome/browser/extensions/manifest_v2_experiment_manager_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 
 #include "base/memory/raw_ptr.h"
 #include "base/test/metrics/histogram_tester.h"
diff --git a/chrome/browser/extensions/mv2_deprecation_impact_checker_unittest.cc b/chrome/browser/extensions/mv2_deprecation_impact_checker_unittest.cc
index 955fdffb..00bbb1a7 100644
--- a/chrome/browser/extensions/mv2_deprecation_impact_checker_unittest.cc
+++ b/chrome/browser/extensions/mv2_deprecation_impact_checker_unittest.cc
@@ -8,9 +8,9 @@
 #include "base/test/values_test_util.h"
 #include "chrome/browser/extensions/extension_management_internal.h"
 #include "chrome/browser/extensions/extension_service_test_base.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/browser/mv2_experiment_stage.h"
 #include "extensions/browser/pref_names.h"
 #include "extensions/common/extension_builder.h"
diff --git a/chrome/browser/extensions/scoped_test_mv2_enabler.cc b/chrome/browser/extensions/scoped_test_mv2_enabler.cc
index 257aaf1..800166aa 100644
--- a/chrome/browser/extensions/scoped_test_mv2_enabler.cc
+++ b/chrome/browser/extensions/scoped_test_mv2_enabler.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/extensions/scoped_test_mv2_enabler.h"
 
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 
 namespace extensions {
 
diff --git a/chrome/browser/extensions/scoped_test_mv2_enabler_browsertest.cc b/chrome/browser/extensions/scoped_test_mv2_enabler_browsertest.cc
index b2a46743..bce7c9d5 100644
--- a/chrome/browser/extensions/scoped_test_mv2_enabler_browsertest.cc
+++ b/chrome/browser/extensions/scoped_test_mv2_enabler_browsertest.cc
@@ -7,12 +7,12 @@
 #include "base/one_shot_event.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/test/browser_test.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_features.h"
 #include "extensions/test/test_extension_dir.h"
diff --git a/chrome/browser/extensions/standard_management_policy_provider.cc b/chrome/browser/extensions/standard_management_policy_provider.cc
index 2522196..4894779 100644
--- a/chrome/browser/extensions/standard_management_policy_provider.cc
+++ b/chrome/browser/extensions/standard_management_policy_provider.cc
@@ -9,9 +9,9 @@
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/extensions/extension_management.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/grit/generated_resources.h"
 #include "extensions/browser/managed_installation_mode.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/buildflags/buildflags.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
diff --git a/chrome/browser/extensions/webstore_standalone_installer.cc b/chrome/browser/extensions/webstore_standalone_installer.cc
index 96aa1feb..1ddd51d14 100644
--- a/chrome/browser/extensions/webstore_standalone_installer.cc
+++ b/chrome/browser/extensions/webstore_standalone_installer.cc
@@ -268,8 +268,7 @@
   // The helper will call us back via OnWebstoreParseSuccess() or
   // OnWebstoreParseFailure().
   helper->Start(profile_->GetDefaultStoragePartition()
-                    ->GetURLLoaderFactoryForBrowserProcess()
-                    .get());
+                    ->GetURLLoaderFactoryForBrowserProcess());
 }
 
 void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
diff --git a/chrome/browser/extensions/webstore_standalone_installer.h b/chrome/browser/extensions/webstore_standalone_installer.h
index 806e8c4..a2a27b0 100644
--- a/chrome/browser/extensions/webstore_standalone_installer.h
+++ b/chrome/browser/extensions/webstore_standalone_installer.h
@@ -15,13 +15,13 @@
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
 #include "chrome/browser/extensions/extension_install_prompt.h"
-#include "chrome/browser/extensions/webstore_install_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_observer.h"
 #include "chrome/common/extensions/webstore_install_result.h"
 #include "extensions/browser/active_install_data.h"
 #include "extensions/browser/cws_item_service.pb.h"
 #include "extensions/browser/webstore_data_fetcher_delegate.h"
+#include "extensions/browser/webstore_install_helper.h"
 #include "extensions/browser/webstore_installer.h"
 #include "extensions/buildflags/buildflags.h"
 #include "third_party/skia/include/core/SkBitmap.h"
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 3c6c6925..21897af3 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -759,6 +759,11 @@
     "expiry_milestone": 160
   },
   {
+    "name": "autofill-ai-no-filling-icons-experiment",
+    "owners": [ "brunobraga@google.com", "tchudakov@google.com" ],
+    "expiry_milestone": 160
+  },
+  {
     "name": "autofill-ai-reauth-required",
     "owners": [ "leozhao@google.com", "tmartino@google.com", "bling-transactions@google.com" ],
     "expiry_milestone": 160
@@ -6998,16 +7003,6 @@
     "expiry_milestone": 140
   },
   {
-    "name": "omnibox-mobile-parity-update",
-    "owners": [ "ender@google.com", "pnoland@google.com", "chrome-mobile-search@google.com"],
-    "expiry_milestone": 150
-  },
-  {
-    "name": "omnibox-mobile-parity-update-v2",
-    "owners": [ "ender@google.com", "hanxi@google.com", "pnoland@google.com", "chrome-mobile-search@google.com", "clank-start@google.com"],
-    "expiry_milestone": 150
-  },
-  {
     "name": "omnibox-most-visited-tiles-horizontal-render-group",
     "owners": [ "stkhapugin@chromium.org", "chrome-mobile-search@google.com"],
     "expiry_milestone": 140
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index edcbb3e..2a20079 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -811,6 +811,11 @@
 inline constexpr char kAutofillAiDedupeEntitiesDescription[] =
     "Enables periodic deduplication of Autofill AI entities.";
 
+inline constexpr char kAutofillAiNoFillingIconsExperimentName[] =
+    "Autofill AI no filling icons experiment";
+inline constexpr char kAutofillAiNoFillingIconsExperimentDescription[] =
+    "If enabled, Autofill AI filling suggestions do not have an icon.";
+
 inline constexpr char kAutofillAiReauthRequiredName[] =
     "Autofill AI re-authentication required";
 inline constexpr char kAutofillAiReauthRequiredDescription[] =
diff --git a/chrome/browser/net/cookie_use_counter_browsertest.cc b/chrome/browser/net/cookie_use_counter_browsertest.cc
index 86efdbd..4ce0410 100644
--- a/chrome/browser/net/cookie_use_counter_browsertest.cc
+++ b/chrome/browser/net/cookie_use_counter_browsertest.cc
@@ -227,15 +227,8 @@
   EXPECT_EQ(GetIframeContent(), "shadowed=a; not_shadowed=b");
 }
 
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
-#define MAYBE_PartitionedCookiePresentV3_CountOnce \
-  DISABLED_PartitionedCookiePresentV3_CountOnce
-#else
-#define MAYBE_PartitionedCookiePresentV3_CountOnce \
-  PartitionedCookiePresentV3_CountOnce
-#endif
 IN_PROC_BROWSER_TEST_F(CookieUseCounterBrowserTest,
-                       MAYBE_PartitionedCookiePresentV3_CountOnce) {
+                       PartitionedCookiePresentV3_CountOnce) {
   ukm::TestAutoSetUkmRecorder ukm_recorder;
 
   SetCookie(
diff --git a/chrome/browser/password_manager/actor_login/internal/BUILD.gn b/chrome/browser/password_manager/actor_login/internal/BUILD.gn
index dd4d8b6..dd301b2 100644
--- a/chrome/browser/password_manager/actor_login/internal/BUILD.gn
+++ b/chrome/browser/password_manager/actor_login/internal/BUILD.gn
@@ -13,8 +13,6 @@
     "actor_login_metrics_helper.h",
     "actor_login_siwg_controller.cc",
     "actor_login_siwg_controller.h",
-    "siwg_button_finder.cc",
-    "siwg_button_finder.h",
   ]
 
   visibility = [
@@ -33,7 +31,6 @@
     "//chrome/browser/ui/browser_window",
     "//chrome/browser/webid",
     "//chrome/common:buildflags",
-    "//components/autofill/content/common/mojom",
     "//components/password_manager/content/browser",
     "//components/password_manager/core/browser",
     "//components/password_manager/core/browser/actor_login:common",
@@ -75,7 +72,6 @@
       "actor_login_duplicate_permission_cleaner_unittest.cc",
       "actor_login_federated_credentials_fetcher_unittest.cc",
       "actor_login_siwg_controller_unittest.cc",
-      "siwg_button_finder_unittest.cc",
     ]
 
     deps = [
diff --git a/chrome/browser/password_manager/actor_login/internal/actor_login_delegate_impl.h b/chrome/browser/password_manager/actor_login/internal/actor_login_delegate_impl.h
index 719817b..72adb5b 100644
--- a/chrome/browser/password_manager/actor_login/internal/actor_login_delegate_impl.h
+++ b/chrome/browser/password_manager/actor_login/internal/actor_login_delegate_impl.h
@@ -162,7 +162,6 @@
   // and click the SiwG button. After the prototype, the click will be done
   // through `ExecutionEngine`.
   // Scoped to one `AttemptLogin` request.
-  // TODO(crbug.com/479505793): Implement the click without heuristics.
   std::unique_ptr<ActorLoginSiwgController> siwg_controller_;
 
   // Track the currently acting task to know when we can remove the
diff --git a/chrome/browser/password_manager/actor_login/internal/actor_login_delegate_impl_unittest.cc b/chrome/browser/password_manager/actor_login/internal/actor_login_delegate_impl_unittest.cc
index 9cbf086..ca1bc0f 100644
--- a/chrome/browser/password_manager/actor_login/internal/actor_login_delegate_impl_unittest.cc
+++ b/chrome/browser/password_manager/actor_login/internal/actor_login_delegate_impl_unittest.cc
@@ -1622,112 +1622,4 @@
   std::move(captured_callback).Run(/*success=*/false);
 }
 
-TEST_F(ActorLoginDelegateImplTest,
-       NoButtonClickSuccessfulFederatedLoginClearsConflictingPermissions) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures(
-      /*enabled_features=*/
-      {password_manager::features::kActorLogin,
-       password_manager::features::kActorLoginConflictingPermissionCleanup},
-      /*disabled_features=*/{
-          password_manager::features::kActorLoginFederatedClickFromActor});
-
-  // Setup mock cleaning service
-  auto* mock_cleaning_service =
-      static_cast<MockActorLoginPermissionCleaningService*>(
-          ActorLoginPermissionCleaningServiceFactory::GetInstance()
-              ->SetTestingFactoryAndUse(
-                  profile(),
-                  base::BindRepeating([](content::BrowserContext* context)
-                                          -> std::unique_ptr<KeyedService> {
-                    return std::make_unique<
-                        NiceMock<MockActorLoginPermissionCleaningService>>();
-                  })));
-  GURL url = GURL(kTestUrl);
-  url::Origin origin = url::Origin::Create(url);
-  SetUpConflictingPermissions(url, kTestUsername);
-
-  // Set up a signin form on the page to force the getter into calling
-  // the hooked fake fetcher.
-  const autofill::FormData form_data = CreateSigninFormData(url);
-  form_managers_.push_back(
-      CreateFormManagerWithParsedForm(origin, form_data, mock_driver_));
-
-  // Mock driver methods that are called by `GetCredentials` to check
-  // whether there is a signin form on the page.
-  ON_CALL(mock_driver_, GetLastCommittedOrigin())
-      .WillByDefault(ReturnRef(origin));
-  ON_CALL(mock_driver_, IsInPrimaryMainFrame).WillByDefault(Return(true));
-  ON_CALL(mock_driver_, IsNestedWithinFencedFrame).WillByDefault(Return(false));
-  ON_CALL(mock_driver_, CheckViewAreaVisible)
-      .WillByDefault(WithArg<1>(&PostResponse<true>));
-  SetUpActorCredentialFillerDeps();
-
-  EXPECT_CALL(mock_form_cache_, GetFormManagers())
-      .WillRepeatedly(Return(base::span(form_managers_)));
-
-  // Call `GetCredentials` first to find conflicting permissions.
-  base::test::TestFuture<CredentialsOrError> get_creds_future;
-  delegate_->GetCredentials(/*has_sign_in_with_google_button=*/false,
-                            mqls_logger(), get_creds_future.GetCallback());
-
-  // Wait until the async visibility checks complete and the fetcher registers
-  // as a consumer.
-  ASSERT_TRUE(
-      base::test::RunUntil([this]() { return form_fetcher_.HasConsumers(); }));
-
-  form_fetcher_.NotifyFetchCompleted();
-  ASSERT_TRUE(get_creds_future.Get().has_value());
-
-  // Setup ActorKeyedServiceFake
-  auto* actor_service = static_cast<actor::ActorKeyedServiceFake*>(
-      actor::ActorKeyedServiceFactory::GetInstance()->SetTestingFactoryAndUse(
-          profile(), base::BindRepeating([](content::BrowserContext* context)
-                                             -> std::unique_ptr<KeyedService> {
-            return std::make_unique<actor::ActorKeyedServiceFake>(
-                Profile::FromBrowserContext(context));
-          })));
-
-  actor::TaskId task_id = actor_service->CreateTaskForTesting();
-  actor::ActorTask* task = actor_service->GetTask(task_id);
-  content::WebContents* test_contents = tab_strip_model_->GetWebContentsAt(0);
-  base::RunLoop loop;
-  task->AddTab(tabs::TabInterface::GetFromContents(test_contents)->GetHandle(),
-               /*stop_task_on_detach=*/true,
-               base::BindLambdaForTesting(
-                   [&](actor::mojom::ActionResultPtr result) { loop.Quit(); }));
-  loop.Run();
-
-  Credential credential = CreateTestCredential(u"username", url, origin);
-  credential.type = CredentialType::kFederated;
-  FederationDetail federation_detail;
-  federation_detail.idp_origin =
-      url::Origin::Create(GURL("https://accounts.google.com"));
-  federation_detail.account_id = "12345";
-  credential.federation_detail = federation_detail;
-
-  base::test::TestFuture<LoginStatusResultOrError> attempt_login_future;
-  delegate_->AttemptLogin(credential, /*should_store_permission=*/true,
-                          mqls_logger(), base::TimeTicks::Now(),
-                          attempt_login_future.GetCallback(),
-                          /*action_sequence_delegate=*/nullptr);
-
-  auto* request =
-      content::webid::FederatedEmbedderLoginRequest::Get(test_contents);
-  ASSERT_TRUE(request);
-
-  EXPECT_CALL(*mock_cleaning_service,
-              ClearConflictingPermissions(Eq(credential), Eq(std::nullopt), _));
-
-  // Because there is no action_sequence_delegate passed in, this will call
-  // `OnAttemptLoginCompleted` directly.
-  request->OnFederatedResultReceived(
-      content::webid::FederatedLoginResult::kSuccess);
-
-  ASSERT_TRUE(attempt_login_future.Wait());
-  ASSERT_TRUE(attempt_login_future.Get().has_value());
-  EXPECT_EQ(attempt_login_future.Get().value(),
-            LoginStatusResult::kSuccessFederated);
-}
-
 }  // namespace actor_login
diff --git a/chrome/browser/password_manager/actor_login/internal/actor_login_siwg_controller.cc b/chrome/browser/password_manager/actor_login/internal/actor_login_siwg_controller.cc
index 5f9d2a67f..c6b7373 100644
--- a/chrome/browser/password_manager/actor_login/internal/actor_login_siwg_controller.cc
+++ b/chrome/browser/password_manager/actor_login/internal/actor_login_siwg_controller.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/password_manager/actor_login/internal/actor_login_siwg_controller.h"
 
-#include "base/barrier_callback.h"
 #include "base/containers/to_vector.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
@@ -14,12 +13,11 @@
 #include "chrome/common/actor.mojom.h"
 #include "chrome/common/actor/action_result.h"
 #include "chrome/common/chrome_render_frame.mojom.h"
-#include "components/autofill/content/browser/content_autofill_driver.h"
-#include "components/autofill/content/common/mojom/autofill_agent.mojom.h"
 #include "components/password_manager/core/browser/features/password_features.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/webid/identity_credential_source.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
@@ -59,19 +57,6 @@
   }
 }
 
-// Finds the local root of a given RenderFrameHost.
-content::RenderFrameHost* GetLocalRoot(content::RenderFrameHost* rfh) {
-  content::RenderFrameHost* local_root = rfh;
-  while (local_root && local_root->GetParent()) {
-    if (local_root->GetRenderWidgetHost() !=
-        local_root->GetParent()->GetRenderWidgetHost()) {
-      break;
-    }
-    local_root = local_root->GetParent();
-  }
-  return local_root;
-}
-
 LoginStatusResult FromFederatedLoginResult(
     content::webid::FederatedLoginResult result) {
   switch (result) {
@@ -169,26 +154,12 @@
   }
   if (!source->SelectAccount(credential_.federation_detail->idp_origin,
                              credential_.federation_detail->account_id)) {
-    if (base::FeatureList::IsEnabled(
-            password_manager::features::kActorLoginFederatedClickFromActor)) {
-      federated_attempt_login_details_.set_button_click_required(true);
-      std::move(on_finished_callback_)
-          .Run(LoginStatusResult::kRequiresButtonClick);
-    } else {
-      ClickSiwgButton();
-    }
+    federated_attempt_login_details_.set_button_click_required(true);
+    std::move(on_finished_callback_)
+        .Run(LoginStatusResult::kRequiresButtonClick);
   }
 }
 
-void ActorLoginSiwgController::ClickSiwgButton() {
-  get_page_content_provider_.Run(
-      web_contents(),
-      optimization_guide::ActionableAIPageContentOptions(
-          /*on_critical_path=*/false),
-      base::BindOnce(&ActorLoginSiwgController::OnPageContentReceived,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
 void ActorLoginSiwgController::OnFederatedLoginResultReceived(
     std::unique_ptr<ActorLoginMetricsHelper> metrics_helper,
     content::webid::FederatedLoginResult result) {
@@ -244,119 +215,4 @@
   mqls_logger_->AddAttemptLoginDetails(federated_attempt_login_details_);
 }
 
-void ActorLoginSiwgController::OnPageContentReceived(
-    optimization_guide::AIPageContentResultOrError content) {
-  if (!content.has_value()) {
-    if (on_finished_callback_) {
-      std::move(on_finished_callback_)
-          // TODO(crbug.com/478799141): add new status for SiwG failure.
-          .Run(base::unexpected(ActorLoginError::kFillingNotAllowed));
-    }
-    return;
-  }
-
-  // Move the page content directly into the finder.
-  siwg_finder_ = std::make_unique<SiwgButtonFinder>(std::move(content->proto));
-
-  // This will find 0 or 1 buttons per frame and click each one found. If we
-  // ever want to productionize this code, we should add some logic to choose
-  // between buttons in different frames.
-  std::vector<autofill::ContentAutofillDriver*> drivers;
-  web_contents()->GetPrimaryMainFrame()->ForEachRenderFrameHost(
-      [&](content::RenderFrameHost* rfh) {
-        if (autofill::ContentAutofillDriver* driver =
-                autofill::ContentAutofillDriver::GetForRenderFrameHost(rfh)) {
-          drivers.push_back(driver);
-        }
-      });
-
-  auto all_frames_scanned_barrier =
-      base::BarrierCallback<FrameSiwgButtonCandidates>(
-          drivers.size(),
-          base::BindOnce(&ActorLoginSiwgController::OnAllFramesScanned,
-                         weak_ptr_factory_.GetWeakPtr()));
-
-  for (autofill::ContentAutofillDriver* driver : drivers) {
-    driver->GetAutofillAgent()->FindPotentialSiwgButtons(
-        base::BindOnce(&ActorLoginSiwgController::OnPotentialSiwgButtonsFound,
-                       weak_ptr_factory_.GetWeakPtr(),
-                       driver->render_frame_host()->GetGlobalId(),
-                       all_frames_scanned_barrier));
-  }
-}
-
-void ActorLoginSiwgController::OnPotentialSiwgButtonsFound(
-    content::GlobalRenderFrameHostId rfh_id,
-    base::OnceCallback<void(FrameSiwgButtonCandidates)> barrier_callback,
-    std::vector<autofill::mojom::SiwgButtonDataPtr> buttons) {
-  std::move(barrier_callback).Run(std::make_pair(rfh_id, std::move(buttons)));
-}
-
-void ActorLoginSiwgController::OnAllFramesScanned(
-    std::vector<FrameSiwgButtonCandidates> results) {
-  CHECK(siwg_finder_);
-  CHECK(on_finished_callback_);
-
-  for (const auto& [rfh_id, buttons] : results) {
-    content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(rfh_id);
-    if (!rfh) {
-      // Frame went away.
-      continue;
-    }
-    std::optional<SiwgButtonFinder::SiwgButton> button =
-        siwg_finder_->FindButton(rfh, buttons);
-    if (button) {
-      ClickButton(rfh, button->dom_node_id, std::move(button->observed_target));
-      // Ensure we only click one button.
-      // ClickButton is the terminal state of this flow - it will resolve
-      // on_finished_callback_ after the click result is received.
-      return;
-    }
-  }
-
-  std::move(on_finished_callback_)
-      // TODO(crbug.com/478799141): add new status for SiwG failure.
-      .Run(base::unexpected(ActorLoginError::kFillingNotAllowed));
-}
-
-void ActorLoginSiwgController::ClickButton(
-    content::RenderFrameHost* rfh,
-    int dom_node_id,
-    actor::mojom::ObservedToolTargetPtr observed_target) {
-  CHECK(!base::FeatureList::IsEnabled(
-      password_manager::features::kActorLoginFederatedClickFromActor));
-  GetLocalRoot(rfh)->GetRemoteAssociatedInterfaces()->GetInterface(
-      &chrome_render_frame_);
-
-  auto invocation = actor::mojom::ToolInvocation::New();
-  invocation->execution_id = base::UnguessableToken::Create();
-
-  auto click = actor::mojom::ClickAction::New();
-  click->type = actor::mojom::ClickType::kLeft;
-  click->count = actor::mojom::ClickCount::kSingle;
-  invocation->action = actor::mojom::ToolAction::NewClick(std::move(click));
-  invocation->target = actor::mojom::ToolTarget::NewDomNodeId(dom_node_id);
-  invocation->observed_target = std::move(observed_target);
-
-  chrome_render_frame_->InvokeTool(
-      std::move(invocation),
-      base::BindOnce(&ActorLoginSiwgController::OnClickFinished,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
-void ActorLoginSiwgController::OnClickFinished(
-    actor::mojom::ActionResultPtr result) {
-  // If the flow already finished (e.g. federated login completed before click
-  // returned), we don't need to do anything.
-  if (!on_finished_callback_) {
-    return;
-  }
-
-  if (result->code != actor::mojom::ActionResultCode::kOk) {
-    std::move(on_finished_callback_)
-        // TODO(crbug.com/478799141): add new status for SiwG failure.
-        .Run(base::unexpected(ActorLoginError::kFillingNotAllowed));
-  }
-}
-
 }  // namespace actor_login
diff --git a/chrome/browser/password_manager/actor_login/internal/actor_login_siwg_controller.h b/chrome/browser/password_manager/actor_login/internal/actor_login_siwg_controller.h
index 18137528..4417a7c 100644
--- a/chrome/browser/password_manager/actor_login/internal/actor_login_siwg_controller.h
+++ b/chrome/browser/password_manager/actor_login/internal/actor_login_siwg_controller.h
@@ -11,10 +11,8 @@
 
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/password_manager/actor_login/internal/actor_login_metrics_helper.h"
-#include "chrome/browser/password_manager/actor_login/internal/siwg_button_finder.h"
 #include "chrome/common/actor.mojom-forward.h"
 #include "chrome/common/chrome_render_frame.mojom.h"
-#include "components/autofill/core/common/mojom/autofill_types.mojom-forward.h"
 #include "components/optimization_guide/content/browser/page_content_proto_provider.h"
 #include "components/password_manager/core/browser/actor_login/actor_login_permission_service.h"
 #include "components/password_manager/core/browser/actor_login/actor_login_quality_logger_interface.h"
@@ -25,7 +23,6 @@
 #include "mojo/public/cpp/bindings/associated_remote.h"
 
 namespace content {
-class RenderFrameHost;
 class WebContents;
 
 namespace webid {
@@ -96,26 +93,7 @@
   void OnButtonClickCompleted(bool success);
 
  private:
-  void OnPageContentReceived(
-      optimization_guide::AIPageContentResultOrError content);
 
-  using FrameSiwgButtonCandidates =
-      std::pair<content::GlobalRenderFrameHostId,
-                std::vector<autofill::mojom::SiwgButtonDataPtr>>;
-
-  void OnPotentialSiwgButtonsFound(
-      content::GlobalRenderFrameHostId rfh_id,
-      base::OnceCallback<void(FrameSiwgButtonCandidates)>
-          all_frames_scanned_barrier,
-      std::vector<autofill::mojom::SiwgButtonDataPtr> buttons);
-
-  void OnAllFramesScanned(std::vector<FrameSiwgButtonCandidates> results);
-
-  void ClickButton(content::RenderFrameHost* rfh,
-                   int dom_node_id,
-                   actor::mojom::ObservedToolTargetPtr observed_target);
-
-  void OnClickFinished(actor::mojom::ActionResultPtr result);
 
   void OnFederatedLoginResultReceived(
       std::unique_ptr<ActorLoginMetricsHelper> metrics_helper,
@@ -124,7 +102,7 @@
   void LogFederatedLoginResult(content::webid::FederatedLoginResult result);
 
   GetPageContentProvider get_page_content_provider_;
-  std::unique_ptr<SiwgButtonFinder> siwg_finder_;
+
   // Invoked once the actions taken by this class to advance the login are
   // complete. The login itself may still be in progress.
   LoginStatusResultOrErrorReply on_finished_callback_;
diff --git a/chrome/browser/password_manager/actor_login/internal/actor_login_siwg_controller_unittest.cc b/chrome/browser/password_manager/actor_login/internal/actor_login_siwg_controller_unittest.cc
index fb3c3ef..e29bb2e 100644
--- a/chrome/browser/password_manager/actor_login/internal/actor_login_siwg_controller_unittest.cc
+++ b/chrome/browser/password_manager/actor_login/internal/actor_login_siwg_controller_unittest.cc
@@ -20,17 +20,10 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
-#include "chrome/browser/autofill/mock_autofill_agent.h"
 #include "chrome/browser/password_manager/actor_login/internal/actor_login_metrics_helper.h"
 #include "chrome/common/actor.mojom.h"
 #include "chrome/common/chrome_render_frame.mojom.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
-#include "components/autofill/content/browser/content_autofill_driver.h"
-#include "components/autofill/content/browser/test_autofill_client_injector.h"
-#include "components/autofill/content/browser/test_autofill_driver_injector.h"
-#include "components/autofill/content/browser/test_content_autofill_client.h"
-#include "components/autofill/content/common/mojom/autofill_agent.mojom.h"
-#include "components/autofill/core/common/mojom/autofill_types.mojom.h"
 #include "components/optimization_guide/proto/features/actor_login.pb.h"
 #include "components/optimization_guide/proto/features/common_quality_data.pb.h"
 #include "components/password_manager/core/browser/actor_login/test/mock_actor_login_permission_service.h"
@@ -145,236 +138,7 @@
 
 }  // namespace
 
-class ActorLoginSiwgControllerTest : public ChromeRenderViewHostTestHarness {
- public:
-  void SetUp() override {
-    ChromeRenderViewHostTestHarness::SetUp();
 
-    feature_list_.InitAndDisableFeature(
-        password_manager::features::kActorLoginFederatedClickFromActor);
-
-    // Navigate to a URL so we have a valid last committed URL.
-    NavigateAndCommit(GURL("https://example.com/login"));
-
-    mock_autofill_agent_.BindForTesting(web_contents()->GetPrimaryMainFrame());
-
-    blink::AssociatedInterfaceProvider* remote_interfaces =
-        web_contents()->GetPrimaryMainFrame()->GetRemoteAssociatedInterfaces();
-    remote_interfaces->OverrideBinderForTesting(
-        chrome::mojom::ChromeRenderFrame::Name_,
-        base::BindRepeating(&MockChromeRenderFrame::BindPendingReceiver,
-                            base::Unretained(&mock_chrome_render_frame_)));
-  }
-
-  void TearDown() override { ChromeRenderViewHostTestHarness::TearDown(); }
-
- protected:
-  base::test::ScopedFeatureList feature_list_;
-  base::HistogramTester histogram_tester_;
-  autofill::TestAutofillClientInjector<autofill::TestContentAutofillClient>
-      autofill_client_injector_;
-  autofill::TestAutofillDriverInjector<autofill::ContentAutofillDriver>
-      autofill_driver_injector_;
-  StrictMock<autofill::MockAutofillAgent> mock_autofill_agent_;
-  StrictMock<MockChromeRenderFrame> mock_chrome_render_frame_;
-  StrictMock<MockActorLoginPermissionService> mock_permission_service_;
-
-  static void SaveCallback(
-      optimization_guide::OnAIPageContentDone* last_callback,
-      content::WebContents* web_contents,
-      blink::mojom::AIPageContentOptionsPtr options,
-      optimization_guide::OnAIPageContentDone callback) {
-    *last_callback = std::move(callback);
-  }
-};
-
-TEST_F(ActorLoginSiwgControllerTest, ButtonFound_ClickSucceeded) {
-  base::HistogramTester histogram_tester;
-  base::RunLoop run_loop;
-  base::MockCallback<LoginStatusResultOrErrorReply> finished_callback;
-  optimization_guide::OnAIPageContentDone page_content_callback;
-
-  auto metrics_helper_owned =
-      std::make_unique<ActorLoginMetricsHelper>(ukm::kInvalidSourceId);
-
-  Credential credential;
-  credential.federation_detail = FederationDetail();
-
-  ActorLoginSiwgController controller(
-      web_contents(), credential,
-      base::BindRepeating(&ActorLoginSiwgControllerTest::SaveCallback,
-                          &page_content_callback),
-      /*should_store_permission=*/false, mock_permission_service_,
-      finished_callback.Get(), /*action_sequence_delegate=*/nullptr,
-      /*mqls_logger=*/nullptr,
-      /*attempt_login_tool_start_time=*/base::TimeTicks::Now());
-
-  controller.StartFederatedLogin(std::move(metrics_helper_owned));
-
-  // 1. Simulate Page Content Received with a SiwG button.
-  optimization_guide::proto::AnnotatedPageContent page_content;
-  auto* root = page_content.mutable_root_node();
-  root->mutable_content_attributes()->set_common_ancestor_dom_node_id(1);
-  root->mutable_content_attributes()
-      ->mutable_interaction_info()
-      ->add_clickability_reasons(
-          optimization_guide::proto::CLICKABILITY_REASON_CLICKABLE_CONTROL);
-  auto* child = root->add_children_nodes();
-  child->mutable_content_attributes()->set_common_ancestor_dom_node_id(2);
-
-  // 2. Expect FindPotentialSiwgButtons on the agent.
-  EXPECT_CALL(mock_autofill_agent_, FindPotentialSiwgButtons)
-      .WillOnce(WithArg<0>(
-          [&](autofill::mojom::AutofillAgent::FindPotentialSiwgButtonsCallback
-                  callback) {
-            std::vector<autofill::mojom::SiwgButtonDataPtr> buttons;
-            auto button = autofill::mojom::SiwgButtonData::New();
-            button->dom_node_id = 2;  // Matches child node
-            button->text = u"Sign in with Google";
-            buttons.push_back(std::move(button));
-            std::move(callback).Run(std::move(buttons));
-          }));
-
-  // 3. Expect InvokeTool (Click) on the frame.
-  EXPECT_CALL(mock_chrome_render_frame_, InvokeTool)
-      .WillOnce(WithArg<1>(
-          [&](chrome::mojom::ChromeRenderFrame::InvokeToolCallback callback) {
-            auto result = actor::mojom::ActionResult::New();
-            result->code = actor::mojom::ActionResultCode::kOk;
-            std::move(callback).Run(std::move(result));
-
-            // Manually trigger the federated login completion callback.
-            auto* request = content::webid::FederatedEmbedderLoginRequest::Get(
-                web_contents());
-            ASSERT_TRUE(request);
-            request->OnFederatedResultReceived(
-                content::webid::FederatedLoginResult::kSuccess);
-          }));
-
-  // 4. Verify Success callback.
-  EXPECT_CALL(finished_callback,
-              Run(base::test::ValueIs(LoginStatusResult::kSuccessFederated)))
-      .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
-
-  // Trigger the flow manually from OnPageContentReceived since capturing relies
-  // on services we didn't mock.
-  optimization_guide::AIPageContentResult result;
-  result.proto = std::move(page_content);
-  std::move(page_content_callback).Run(std::move(result));
-
-  run_loop.Run();
-
-  histogram_tester.ExpectUniqueSample("Actor.Login.Federated.LoginResult",
-                                      ActorLoginFederatedLoginResult::kSuccess,
-                                      1);
-}
-
-TEST_F(ActorLoginSiwgControllerTest, ButtonFound_ClickFailed) {
-  base::RunLoop run_loop;
-  base::MockCallback<LoginStatusResultOrErrorReply> finished_callback;
-  optimization_guide::OnAIPageContentDone page_content_callback;
-  Credential credential;
-  credential.federation_detail = FederationDetail();
-
-  ActorLoginSiwgController controller(
-      web_contents(), credential,
-      base::BindRepeating(&ActorLoginSiwgControllerTest::SaveCallback,
-                          &page_content_callback),
-      /*should_store_permission=*/false, mock_permission_service_,
-      finished_callback.Get(), /*action_sequence_delegate=*/nullptr,
-      /*mqls_logger=*/nullptr,
-      /*attempt_login_tool_start_time=*/base::TimeTicks::Now());
-
-  controller.StartFederatedLogin(/*metrics_helper=*/nullptr);
-
-  // 1. Simulate Page Content Received with a SiwG button.
-  optimization_guide::proto::AnnotatedPageContent page_content;
-  auto* root = page_content.mutable_root_node();
-  root->mutable_content_attributes()->set_common_ancestor_dom_node_id(1);
-  root->mutable_content_attributes()
-      ->mutable_interaction_info()
-      ->add_clickability_reasons(
-          optimization_guide::proto::CLICKABILITY_REASON_CLICKABLE_CONTROL);
-  auto* child = root->add_children_nodes();
-  child->mutable_content_attributes()->set_common_ancestor_dom_node_id(2);
-
-  // 2. Expect FindPotentialSiwgButtons.
-  EXPECT_CALL(mock_autofill_agent_, FindPotentialSiwgButtons)
-      .WillOnce(WithArg<0>(
-          [&](autofill::mojom::AutofillAgent::FindPotentialSiwgButtonsCallback
-                  callback) {
-            std::vector<autofill::mojom::SiwgButtonDataPtr> buttons;
-            auto button = autofill::mojom::SiwgButtonData::New();
-            button->dom_node_id = 2;
-            button->text = u"Sign in with Google";
-            buttons.push_back(std::move(button));
-            std::move(callback).Run(std::move(buttons));
-          }));
-
-  // 3. Expect InvokeTool and simulate failure.
-  EXPECT_CALL(mock_chrome_render_frame_, InvokeTool)
-      .WillOnce(WithArg<1>(
-          [&](chrome::mojom::ChromeRenderFrame::InvokeToolCallback callback) {
-            auto result = actor::mojom::ActionResult::New();
-            result->code = actor::mojom::ActionResultCode::kElementDisabled;
-            std::move(callback).Run(std::move(result));
-          }));
-
-  // 4. Verify Failure callback.
-  EXPECT_CALL(finished_callback,
-              Run(base::test::ErrorIs(ActorLoginError::kFillingNotAllowed)))
-      .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
-
-  optimization_guide::AIPageContentResult result;
-  result.proto = std::move(page_content);
-  std::move(page_content_callback).Run(std::move(result));
-
-  run_loop.Run();
-}
-
-TEST_F(ActorLoginSiwgControllerTest, NoButtonsFound) {
-  base::RunLoop run_loop;
-  base::MockCallback<LoginStatusResultOrErrorReply> finished_callback;
-  optimization_guide::OnAIPageContentDone page_content_callback;
-  Credential credential;
-  credential.federation_detail = FederationDetail();
-
-  ActorLoginSiwgController controller(
-      web_contents(), credential,
-      base::BindRepeating(&ActorLoginSiwgControllerTest::SaveCallback,
-                          &page_content_callback),
-      /*should_store_permission=*/false, mock_permission_service_,
-      finished_callback.Get(), /*action_sequence_delegate=*/nullptr,
-      /*mqls_logger=*/nullptr,
-      /*attempt_login_tool_start_time=*/base::TimeTicks::Now());
-
-  controller.StartFederatedLogin(/*metrics_helper=*/nullptr);
-
-  // No buttons in the page content.
-  optimization_guide::proto::AnnotatedPageContent page_content;
-
-  // Expect FindPotentialSiwgButtons but return empty.
-  EXPECT_CALL(mock_autofill_agent_, FindPotentialSiwgButtons)
-      .WillOnce(WithArg<0>(
-          [&](autofill::mojom::AutofillAgent::FindPotentialSiwgButtonsCallback
-                  callback) {
-            std::vector<autofill::mojom::SiwgButtonDataPtr> buttons;
-            std::move(callback).Run(std::move(buttons));
-          }));
-
-  // Do NOT expect InvokeTool.
-  EXPECT_CALL(mock_chrome_render_frame_, InvokeTool).Times(0);
-
-  EXPECT_CALL(finished_callback,
-              Run(base::test::ErrorIs(ActorLoginError::kFillingNotAllowed)))
-      .WillOnce(base::test::RunClosure(run_loop.QuitClosure()));
-
-  optimization_guide::AIPageContentResult result;
-  result.proto = std::move(page_content);
-  std::move(page_content_callback).Run(std::move(result));
-
-  run_loop.Run();
-}
 
 using AttemptLoginDetails =
     optimization_guide::proto::ActorLoginQuality_AttemptLoginDetails;
@@ -395,10 +159,9 @@
                         expected.button_click_succeeded()));
 }
 
-class ActorLoginSiwgControllerDelegateClickTest
-    : public ChromeRenderViewHostTestHarness {
+class ActorLoginSiwgControllerTest : public ChromeRenderViewHostTestHarness {
  public:
-  ActorLoginSiwgControllerDelegateClickTest()
+  ActorLoginSiwgControllerTest()
       : ChromeRenderViewHostTestHarness(
             base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
 
@@ -422,7 +185,7 @@
   MockActorLoginQualityLogger mock_mqls_logger_;
 };
 
-TEST_F(ActorLoginSiwgControllerDelegateClickTest, DelegatesClick) {
+TEST_F(ActorLoginSiwgControllerTest, DelegatesClick) {
   base::HistogramTester histogram_tester;
   base::MockCallback<LoginStatusResultOrErrorReply> finished_callback;
   StrictMock<MockActionSequenceDelegate> action_sequence_delegate;
@@ -487,7 +250,7 @@
                                       1);
 }
 
-TEST_F(ActorLoginSiwgControllerDelegateClickTest, StoresPermissionOnSuccess) {
+TEST_F(ActorLoginSiwgControllerTest, StoresPermissionOnSuccess) {
   base::MockCallback<LoginStatusResultOrErrorReply> finished_callback;
   StrictMock<MockActionSequenceDelegate> action_sequence_delegate;
   auto metrics_helper_owned =
@@ -557,8 +320,7 @@
   outcome_run_loop.Run();
 }
 
-TEST_F(ActorLoginSiwgControllerDelegateClickTest,
-       DoesNotStorePermissionOnFailure) {
+TEST_F(ActorLoginSiwgControllerTest, DoesNotStorePermissionOnFailure) {
   base::MockCallback<LoginStatusResultOrErrorReply> finished_callback;
   StrictMock<MockActionSequenceDelegate> action_sequence_delegate;
   auto metrics_helper_owned =
diff --git a/chrome/browser/password_manager/actor_login/internal/siwg_button_finder.cc b/chrome/browser/password_manager/actor_login/internal/siwg_button_finder.cc
deleted file mode 100644
index 05b05fc..0000000
--- a/chrome/browser/password_manager/actor_login/internal/siwg_button_finder.cc
+++ /dev/null
@@ -1,237 +0,0 @@
-// 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/password_manager/actor_login/internal/siwg_button_finder.h"
-
-#include "base/containers/fixed_flat_set.h"
-#include "base/containers/map_util.h"
-#include "base/no_destructor.h"
-#include "base/strings/strcat.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/render_widget_host_view.h"
-#include "third_party/blink/public/mojom/content_extraction/ai_page_content.mojom.h"
-#include "third_party/re2/src/re2/re2.h"
-#include "ui/gfx/geometry/point_conversions.h"
-#include "ui/gfx/geometry/rect.h"
-
-namespace actor_login {
-
-namespace {
-
-// Helper function to create `ObservedToolTarget` mojom struct from
-// `ContentAttributes`.
-// This is duplicated code from `actor::PageTool`. The code is only needed for
-// the E2E prototype and will be removed after.
-// TODO(crbug.com/480920277): Remove this code after the click is handled by the
-// `ExecutionEngine`.
-actor::mojom::ObservedToolTargetPtr ToMojoObservedToolTarget(
-    const optimization_guide::proto::ContentAttributes* attributes,
-    content::RenderFrameHost* rfh) {
-  if (!attributes) {
-    return nullptr;
-  }
-  auto observed_target = actor::mojom::ObservedToolTarget::New();
-  observed_target->node_attribute =
-      blink::mojom::AIPageContentAttributes::New();
-
-  if (attributes->has_common_ancestor_dom_node_id()) {
-    observed_target->node_attribute->dom_node_id =
-        attributes->common_ancestor_dom_node_id();
-  }
-
-  if (attributes->has_geometry()) {
-    observed_target->node_attribute->geometry =
-        blink::mojom::AIPageContentGeometry::New();
-    const auto& geometry = attributes->geometry();
-    const auto& outer_box = geometry.outer_bounding_box();
-    gfx::Rect outer_rect(outer_box.x(), outer_box.y(), outer_box.width(),
-                         outer_box.height());
-
-    const auto& visible_box = geometry.visible_bounding_box();
-    gfx::Rect visible_rect(visible_box.x(), visible_box.y(),
-                           visible_box.width(), visible_box.height());
-
-    if (rfh && rfh->GetView()) {
-      const gfx::Point outer_box_origin_point = gfx::ToRoundedPoint(
-          rfh->GetView()->TransformRootPointToViewCoordSpace(
-              gfx::PointF(outer_rect.origin())));
-      outer_rect.set_origin(outer_box_origin_point);
-
-      const gfx::Point visible_box_origin_point = gfx::ToRoundedPoint(
-          rfh->GetView()->TransformRootPointToViewCoordSpace(
-              gfx::PointF(visible_rect.origin())));
-      visible_rect.set_origin(visible_box_origin_point);
-    }
-
-    observed_target->node_attribute->geometry->outer_bounding_box = outer_rect;
-    observed_target->node_attribute->geometry->visible_bounding_box =
-        visible_rect;
-  }
-  return observed_target;
-}
-
-bool MatchesButtonAttributes(const RE2& regex,
-                             const autofill::mojom::SiwgButtonData& button) {
-  return RE2::PartialMatch(base::UTF16ToUTF8(button.text), regex) ||
-         RE2::PartialMatch(base::UTF16ToUTF8(button.id_attribute), regex) ||
-         RE2::PartialMatch(base::UTF16ToUTF8(button.class_attribute), regex) ||
-         RE2::PartialMatch(base::UTF16ToUTF8(button.aria_label), regex) ||
-         RE2::PartialMatch(base::UTF16ToUTF8(button.href_attribute), regex);
-}
-
-bool IsClickable(const optimization_guide::proto::ContentNode* node) {
-  return !node->content_attributes()
-              .interaction_info()
-              .clickability_reasons()
-              .empty();
-}
-
-}  // namespace
-
-SiwgButtonFinder::SiwgButtonFinder(
-    optimization_guide::proto::AnnotatedPageContent page_content)
-    : page_content_(std::move(page_content)) {
-  BuildContentNodeMaps(page_content_.root_node());
-}
-
-SiwgButtonFinder::~SiwgButtonFinder() = default;
-
-SiwgButtonFinder::SiwgButton::SiwgButton() = default;
-SiwgButtonFinder::SiwgButton::SiwgButton(
-    int dom_node_id,
-    actor::mojom::ObservedToolTargetPtr observed_target)
-    : dom_node_id(dom_node_id), observed_target(std::move(observed_target)) {}
-SiwgButtonFinder::SiwgButton::~SiwgButton() = default;
-SiwgButtonFinder::SiwgButton::SiwgButton(SiwgButton&&) = default;
-SiwgButtonFinder::SiwgButton& SiwgButtonFinder::SiwgButton::operator=(
-    SiwgButton&&) = default;
-
-void SiwgButtonFinder::BuildContentNodeMaps(
-    const optimization_guide::proto::ContentNode& node) {
-  dom_node_id_to_content_node_[node.content_attributes()
-                                   .common_ancestor_dom_node_id()] = &node;
-  for (const optimization_guide::proto::ContentNode& child :
-       node.children_nodes()) {
-    parent_map_[&child] = &node;
-    BuildContentNodeMaps(child);
-  }
-}
-
-std::optional<int> SiwgButtonFinder::FindClosestClickableAncestor(
-    const optimization_guide::proto::ContentNode& node) {
-  const optimization_guide::proto::ContentNode* current_node = &node;
-  while (current_node) {
-    if (IsClickable(current_node)) {
-      return current_node->content_attributes().common_ancestor_dom_node_id();
-    }
-    current_node = base::FindPtrOrNull(parent_map_, current_node);
-  }
-
-  return std::nullopt;
-}
-
-std::optional<int> SiwgButtonFinder::FindGoogleSdkButton(
-    const GURL& button_frame_url,
-    const std::vector<autofill::mojom::SiwgButtonDataPtr>& buttons) {
-  // Google-recommended implementation of SiwG button uses a specific iframe.
-  // The button element in this case is the <div> with role="button".
-  // This is the highest confidence match.
-  if (base::StartsWith(button_frame_url.spec(),
-                       "https://accounts.google.com/gsi/button")) {
-    for (const auto& button : buttons) {
-      if (base::ToLowerASCII(button->tag_name) == u"div" &&
-          base::ToLowerASCII(button->role) == u"button") {
-        return button->dom_node_id;
-      }
-    }
-  }
-  return std::nullopt;
-}
-
-std::optional<SiwgButtonFinder::SiwgButton> SiwgButtonFinder::FindButton(
-    content::RenderFrameHost* rfh,
-    const std::vector<autofill::mojom::SiwgButtonDataPtr>& buttons) {
-  if (std::optional<int> button_id =
-          FindGoogleSdkButton(rfh->GetLastCommittedURL(), buttons)) {
-    return SiwgButton{*button_id, ToMojoObservedToolTarget(
-                                      GetContentAttributes(*button_id), rfh)};
-  }
-
-  // Layer 1: High confidence, direct matching on button labels.
-  // These are literal strings and can be translated in the future.
-  static constexpr auto kHighConfidenceMatches =
-      base::MakeFixedFlatSet<std::string_view>(
-          {"continue with google", "sign in with google", "sign up with google",
-           "log in with google", "login with google", "sign up using google",
-           "sign in using google", "log in using google", "use google account",
-           "sign in via google$"});
-
-  // Layer 2: Broad regex matching for various attributes.
-  // This is a wider regex to capture more cases, including developer-set ids
-  // and classes. Less precise and cannot be translated easily.
-  static const base::NoDestructor<RE2> kGoogleSigninButtonRegexMatcher([] {
-    const std::vector<std::string_view> kSigninActionAtomics = {
-        "sign.?in", "sign.?up", "log.?in",      "login",
-        "auth",     "social",   "registration", "continue"};
-    const std::string kSigninActionRegex =
-        "(?:" + base::JoinString(kSigninActionAtomics, "|") + ")";
-    return base::StrCat({"(?i)(?:google[-_.\\s]*.*", kSigninActionRegex,
-                         ")|(?:", kSigninActionRegex,
-                         ".*[-_.\\s]*(?:with[-_.\\s]*)?google)"});
-  }());
-
-  // Layer 3: Anti-matching to exclude false positives.
-  // Explicitly filter out things related to other Google products.
-  static const base::NoDestructor<RE2> kGoogleProductExclusionRegexMatcher(
-      "(?i)classroom");
-
-  for (const autofill::mojom::SiwgButtonDataPtr& button : buttons) {
-    const optimization_guide::proto::ContentNode* node =
-        base::FindPtrOrNull(dom_node_id_to_content_node_, button->dom_node_id);
-    if (!node) {
-      continue;
-    }
-
-    // High confidence direct matching on button text.
-    if (kHighConfidenceMatches.contains(base::TrimWhitespaceASCII(
-            base::UTF16ToUTF8(button->text), base::TRIM_ALL))) {
-      if (std::optional<int> ancestor_id =
-              FindClosestClickableAncestor(*node)) {
-        return SiwgButton{
-            *ancestor_id,
-            ToMojoObservedToolTarget(GetContentAttributes(*ancestor_id), rfh)};
-      }
-    }
-
-    // Regex matching on all attributes.
-    if (MatchesButtonAttributes(*kGoogleSigninButtonRegexMatcher, *button)) {
-      // Anti-matching to exclude false positives.
-      if (MatchesButtonAttributes(*kGoogleProductExclusionRegexMatcher,
-                                  *button)) {
-        continue;
-      }
-
-      if (std::optional<int> ancestor_id =
-              FindClosestClickableAncestor(*node)) {
-        return SiwgButton{
-            *ancestor_id,
-            ToMojoObservedToolTarget(GetContentAttributes(*ancestor_id), rfh)};
-      }
-    }
-  }
-
-  return std::nullopt;
-}
-
-const optimization_guide::proto::ContentAttributes*
-SiwgButtonFinder::GetContentAttributes(int dom_node_id) const {
-  const optimization_guide::proto::ContentNode* node =
-      base::FindPtrOrNull(dom_node_id_to_content_node_, dom_node_id);
-  return node ? &node->content_attributes() : nullptr;
-}
-
-}  // namespace actor_login
diff --git a/chrome/browser/password_manager/actor_login/internal/siwg_button_finder.h b/chrome/browser/password_manager/actor_login/internal/siwg_button_finder.h
deleted file mode 100644
index 1753515..0000000
--- a/chrome/browser/password_manager/actor_login/internal/siwg_button_finder.h
+++ /dev/null
@@ -1,80 +0,0 @@
-// 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_PASSWORD_MANAGER_ACTOR_LOGIN_INTERNAL_SIWG_BUTTON_FINDER_H_
-#define CHROME_BROWSER_PASSWORD_MANAGER_ACTOR_LOGIN_INTERNAL_SIWG_BUTTON_FINDER_H_
-
-#include <map>
-#include <optional>
-#include <string>
-#include <vector>
-
-#include "base/containers/flat_map.h"
-#include "base/memory/raw_ptr.h"
-#include "chrome/common/actor.mojom.h"
-#include "components/autofill/content/common/mojom/autofill_agent.mojom.h"
-#include "components/optimization_guide/proto/features/common_quality_data.pb.h"
-#include "url/gurl.h"
-
-namespace content {
-class RenderFrameHost;
-}  // namespace content
-
-namespace actor_login {
-
-class SiwgButtonFinder {
- public:
-  explicit SiwgButtonFinder(
-      optimization_guide::proto::AnnotatedPageContent page_content);
-  ~SiwgButtonFinder();
-
-  SiwgButtonFinder(const SiwgButtonFinder&) = delete;
-  SiwgButtonFinder& operator=(const SiwgButtonFinder&) = delete;
-
-  struct SiwgButton {
-    SiwgButton();
-    SiwgButton(int dom_node_id,
-               actor::mojom::ObservedToolTargetPtr observed_target);
-    ~SiwgButton();
-    SiwgButton(SiwgButton&&);
-    SiwgButton& operator=(SiwgButton&&);
-
-    int dom_node_id;
-    // Button's observed target, used for the TOCTOU check.
-    actor::mojom::ObservedToolTargetPtr observed_target;
-  };
-
-  // Returns the most likely interactable SiwG button if found.
-  std::optional<SiwgButton> FindButton(
-      content::RenderFrameHost* rfh,
-      const std::vector<autofill::mojom::SiwgButtonDataPtr>& buttons);
-
- private:
-  void BuildContentNodeMaps(const optimization_guide::proto::ContentNode& node);
-
-  std::optional<int> FindClosestClickableAncestor(
-      const optimization_guide::proto::ContentNode& node);
-
-  std::optional<int> FindGoogleSdkButton(
-      const GURL& button_frame_url,
-      const std::vector<autofill::mojom::SiwgButtonDataPtr>& buttons);
-
-  const optimization_guide::proto::ContentAttributes* GetContentAttributes(
-      int dom_node_id) const;
-
-  optimization_guide::proto::AnnotatedPageContent page_content_;
-  // Lookup table for the nodes in the page content. The nodes are owned by the
-  // `page_content_`.
-  base::flat_map<int32_t, raw_ptr<const optimization_guide::proto::ContentNode>>
-      dom_node_id_to_content_node_;
-  // Lookup table for the parent node of a given node. The nodes are owned by
-  // the `page_content_`.
-  base::flat_map<raw_ptr<const optimization_guide::proto::ContentNode>,
-                 raw_ptr<const optimization_guide::proto::ContentNode>>
-      parent_map_;
-};
-
-}  // namespace actor_login
-
-#endif  // CHROME_BROWSER_PASSWORD_MANAGER_ACTOR_LOGIN_INTERNAL_SIWG_BUTTON_FINDER_H_
diff --git a/chrome/browser/password_manager/actor_login/internal/siwg_button_finder_unittest.cc b/chrome/browser/password_manager/actor_login/internal/siwg_button_finder_unittest.cc
deleted file mode 100644
index 0519191..0000000
--- a/chrome/browser/password_manager/actor_login/internal/siwg_button_finder_unittest.cc
+++ /dev/null
@@ -1,147 +0,0 @@
-// 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/password_manager/actor_login/internal/siwg_button_finder.h"
-
-#include <vector>
-
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/test/base/chrome_render_view_host_test_harness.h"
-#include "components/autofill/content/common/mojom/autofill_agent.mojom.h"
-#include "components/optimization_guide/proto/features/common_quality_data.pb.h"
-#include "content/public/browser/web_contents.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace actor_login {
-namespace {
-
-using autofill::mojom::SiwgButtonData;
-using autofill::mojom::SiwgButtonDataPtr;
-using optimization_guide::proto::AnnotatedPageContent;
-using optimization_guide::proto::ContentNode;
-
-SiwgButtonDataPtr CreateButtonData(int dom_node_id,
-                                   const std::string& text = "",
-                                   const std::string& tag_name = "",
-                                   const std::string& role = "",
-                                   const std::string& aria_label = "") {
-  auto button = SiwgButtonData::New();
-  button->dom_node_id = dom_node_id;
-  button->text = base::UTF8ToUTF16(text);
-  button->tag_name = base::UTF8ToUTF16(tag_name);
-  button->role = base::UTF8ToUTF16(role);
-  button->aria_label = base::UTF8ToUTF16(aria_label);
-  return button;
-}
-
-ContentNode CreateContentNode(int dom_node_id, bool is_interactable) {
-  ContentNode node;
-  node.mutable_content_attributes()->set_common_ancestor_dom_node_id(
-      dom_node_id);
-  if (is_interactable) {
-    node.mutable_content_attributes()
-        ->mutable_interaction_info()
-        ->add_clickability_reasons(
-            optimization_guide::proto::CLICKABILITY_REASON_CLICKABLE_CONTROL);
-  }
-  return node;
-}
-
-class SiwgButtonFinderTest : public ChromeRenderViewHostTestHarness {};
-
-TEST_F(SiwgButtonFinderTest, FindButton_HighConfidenceMatch) {
-  AnnotatedPageContent page_content;
-  // Root node (interactable container) -> Button node
-  *page_content.mutable_root_node() = CreateContentNode(1, true);
-  *page_content.mutable_root_node()->add_children_nodes() =
-      CreateContentNode(2, false);
-
-  SiwgButtonFinder finder(std::move(page_content));
-
-  std::vector<SiwgButtonDataPtr> buttons;
-  buttons.push_back(CreateButtonData(2, "Sign in with Google"));
-
-  NavigateAndCommit(GURL("https://example.com"));
-  std::optional<SiwgButtonFinder::SiwgButton> result =
-      finder.FindButton(main_rfh(), buttons);
-  ASSERT_TRUE(result.has_value());
-  EXPECT_EQ(result->dom_node_id, 1);
-}
-
-TEST_F(SiwgButtonFinderTest, FindButton_NoMatch) {
-  AnnotatedPageContent page_content;
-  *page_content.mutable_root_node() = CreateContentNode(1, true);
-  *page_content.mutable_root_node()->add_children_nodes() =
-      CreateContentNode(2, false);
-
-  SiwgButtonFinder finder(std::move(page_content));
-
-  std::vector<SiwgButtonDataPtr> buttons;
-  buttons.push_back(CreateButtonData(2, "Random Button"));
-
-  NavigateAndCommit(GURL("https://example.com"));
-  EXPECT_FALSE(finder.FindButton(main_rfh(), buttons).has_value());
-}
-
-TEST_F(SiwgButtonFinderTest, FindButton_NoInteractableAncestor) {
-  AnnotatedPageContent page_content;
-  *page_content.mutable_root_node() = CreateContentNode(1, false);
-  *page_content.mutable_root_node()->add_children_nodes() =
-      CreateContentNode(2, false);
-
-  SiwgButtonFinder finder(std::move(page_content));
-
-  std::vector<SiwgButtonDataPtr> buttons;
-  buttons.push_back(CreateButtonData(2, "Sign in with Google"));
-
-  NavigateAndCommit(GURL("https://example.com"));
-  EXPECT_FALSE(finder.FindButton(main_rfh(), buttons).has_value());
-}
-
-TEST_F(SiwgButtonFinderTest, FindButton_GoogleSdkIframe) {
-  AnnotatedPageContent page_content;
-  // Content structure doesn't matter much for this path as it returns button ID
-  // directly.
-  *page_content.mutable_root_node() = CreateContentNode(1, true);
-
-  SiwgButtonFinder finder(std::move(page_content));
-
-  std::vector<SiwgButtonDataPtr> buttons;
-  // Matches: div + role="button"
-  buttons.push_back(CreateButtonData(10, "", "div", "button"));
-  // Mismatch
-  buttons.push_back(CreateButtonData(11, "", "span", "button"));
-
-  // URL matches the specific Google SDK iframe URL.
-  NavigateAndCommit(
-      GURL("https://accounts.google.com/gsi/button?client_id=..."));
-  std::optional<SiwgButtonFinder::SiwgButton> result =
-      finder.FindButton(main_rfh(), buttons);
-  ASSERT_TRUE(result.has_value());
-  EXPECT_EQ(result->dom_node_id, 10);
-}
-
-TEST_F(SiwgButtonFinderTest, FindButton_AttributeMatch) {
-  AnnotatedPageContent page_content;
-  *page_content.mutable_root_node() = CreateContentNode(1, true);
-  *page_content.mutable_root_node()->add_children_nodes() =
-      CreateContentNode(2, false);
-
-  SiwgButtonFinder finder(std::move(page_content));
-
-  std::vector<SiwgButtonDataPtr> buttons;
-  // "sign in" regex match in aria-label
-  buttons.push_back(
-      CreateButtonData(2, "Icon", "button", "", "Sign in with Google"));
-
-  NavigateAndCommit(GURL("https://example.com"));
-  std::optional<SiwgButtonFinder::SiwgButton> result =
-      finder.FindButton(main_rfh(), buttons);
-  ASSERT_TRUE(result.has_value());
-  EXPECT_EQ(result->dom_node_id, 1);
-}
-
-}  // namespace
-}  // namespace actor_login
diff --git a/chrome/browser/password_manager/password_change/password_change_from_checkup_delegate.cc b/chrome/browser/password_manager/password_change/password_change_from_checkup_delegate.cc
index b70ed7d7..a470f41 100644
--- a/chrome/browser/password_manager/password_change/password_change_from_checkup_delegate.cc
+++ b/chrome/browser/password_manager/password_change/password_change_from_checkup_delegate.cc
@@ -14,8 +14,8 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/values.h"
-#include "build/branding_buildflags.h"
 #include "chrome/browser/actor/actor_keyed_service.h"
+#include "chrome/browser/actor/enterprise_policy_checker.h"
 #include "chrome/browser/actor/execution_engine.h"
 #include "chrome/browser/glic/public/glic_invoke_options.h"
 #include "chrome/browser/glic/public/glic_keyed_service.h"
@@ -46,6 +46,28 @@
   return url.is_valid() && url.SchemeIsHTTPOrHTTPS();
 }
 
+void CreateDummyTaskAndTiedTab(glic::GlicKeyedService* glic_service,
+                               content::WebContents* web_contents) {
+  if (!glic_service || !web_contents) {
+    return;
+  }
+  actor::ActorKeyedService* actor_service = actor::ActorKeyedService::Get(
+      Profile::FromBrowserContext(web_contents->GetBrowserContext()));
+  if (!actor_service) {
+    return;
+  }
+  actor::TaskId dummy_task_id = actor_service->CreateTask(
+      actor::TaskSourceInfo(actor::TaskSourceInfo::Client::kGlic, std::nullopt),
+      reinterpret_cast<const actor::EnterprisePolicyChecker*>(
+          &glic_service->actor_policy_checker()));
+  actor::ActorTask* dummy_task = actor_service->GetTask(dummy_task_id);
+  tabs::TabInterface* actuation_tab =
+      tabs::TabInterface::MaybeGetFromContents(web_contents);
+  CHECK(dummy_task && actuation_tab);
+  dummy_task->AddTab(actuation_tab->GetHandle(), /*stop_task_on_detach=*/true,
+                     base::DoNothing());
+}
+
 bool IsSameOrigin(const std::u16string& credential_source_site_or_app,
                   const GURL& credential_target_url) {
   GURL source_url(credential_source_site_or_app);
@@ -216,8 +238,6 @@
   }
 
   // Invoking it in a new tab ensures that the settings page is not shared.
-  // It also expects that the actor uses the current tab instead of attempting
-  // to open a new one for completing the flow.
   glic_service->InvokeWithAutoSubmit(
       glic::InvokeWithAutoSubmitPasskeyProvider::GetPassKey(),
       std::move(options));
@@ -226,6 +246,7 @@
       Profile::FromBrowserContext(new_contents->GetBrowserContext()));
   if (actor_service) {
     actuation_web_contents_ = new_contents->GetWeakPtr();
+    CreateDummyTaskAndTiedTab(glic_service, new_contents);
     actor_task_state_subscription_ =
         actor_service->AddTaskStateChangedCallback(base::BindRepeating(
             &PasswordChangeFromCheckupDelegate::OnFindFormTaskStateChanged,
@@ -283,10 +304,7 @@
   if (!find_form_task_id_) {
     if (task.GetTabs().contains(actuation_tab->GetHandle())) {
       find_form_task_id_ = task.id();
-      task.GetExecutionEngine().PreHandleCredentialSelectionDialog(
-          base::BindOnce(
-              &PasswordChangeFromCheckupDelegate::AutoSelectCredential,
-              weak_ptr_factory_.GetWeakPtr()));
+      RegisterAutoSelectCredential(task);
     } else {
       return;
     }
@@ -306,6 +324,8 @@
 
   if (new_state == actor::ActorTask::State::kFinished) {
     actor_task_state_subscription_ = {};
+    CreateDummyTaskAndTiedTab(GetGlicService(), actuation_web_contents_.get());
+
     auto* client = ChromePasswordManagerClient::FromWebContents(
         actuation_web_contents_.get());
     if (!client) {
@@ -352,7 +372,6 @@
 void PasswordChangeFromCheckupDelegate::OnChangePasswordFormSubmitted(
     ChangePasswordFormFillingSubmissionHelper::SubmissionResult result) {
   submission_helper_.reset();
-
   // If the form submission failed, do not trigger a verification task.
   if (!result.has_value()) {
     return;
@@ -383,51 +402,35 @@
     return;
   }
 
-  glic::GlicInvokeOptions options(glic::Target(tab_interface),
-                                  glic::mojom::InvocationSource::kSharedTab);
-  options.prompts.push_back(std::move(post_submission_prompt));
-  options.additional_context = glic::mojom::AdditionalContext::New();
-  sessions::SessionTabHelper* session_tab_helper =
-      sessions::SessionTabHelper::FromWebContents(
-          actuation_web_contents_.get());
-  if (session_tab_helper) {
-    options.additional_context->tab_id = session_tab_helper->session_id().id();
-  }
-
-  glic_service->InvokeWithAutoSubmit(
-      glic::InvokeWithAutoSubmitPasskeyProvider::GetPassKey(),
-      std::move(options));
-
-  actor::ActorKeyedService* actor_service =
-      actor::ActorKeyedService::Get(Profile::FromBrowserContext(
-          actuation_web_contents_->GetBrowserContext()));
-  if (actor_service) {
-    actor_task_state_subscription_ =
-        actor_service->AddTaskStateChangedCallback(base::BindRepeating(
-            &PasswordChangeFromCheckupDelegate::OnVerificationTaskStateChanged,
-            base::Unretained(this)));
-  }
-
-  // If no task is created after 5 seconds, we assume that it was a successful
-  // change since and no extra steps are needed.
-  verification_timer_.Start(
-      FROM_HERE, base::Seconds(5),
-      base::BindOnce(&PasswordChangeFromCheckupDelegate::OnVerificationTimeout,
-                     weak_ptr_factory_.GetWeakPtr()));
+  glic_service->CloseAndShutdown(
+      actuation_web_contents_->GetPrimaryMainFrame());
+  base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&PasswordChangeFromCheckupDelegate::InvokeVerificationFlow,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     std::move(post_submission_prompt)),
+      base::Seconds(1));
 }
 
 void PasswordChangeFromCheckupDelegate::OnVerificationTaskStateChanged(
     actor::ActorTask& task) {
   const actor::ActorTask::State new_state = task.GetState();
-  if (!verification_task_id_) {
-    verification_task_id_ = task.id();
-    verification_task_created_ = true;
-    // A task was created, so stopping the timer to not trigger
-    // the password being saved.
-    verification_timer_.Stop();
+  tabs::TabInterface* actuation_tab =
+      tabs::TabInterface::MaybeGetFromContents(actuation_web_contents_.get());
+  if (!actuation_tab) {
     return;
   }
 
+  if (!verification_task_id_) {
+    if (task.GetTabs().contains(actuation_tab->GetHandle())) {
+      verification_task_id_ = task.id();
+      verification_task_created_ = true;
+      verification_timer_.Stop();
+    } else {
+      return;
+    }
+  }
+
   // Ignore unrelated tasks.
   if (verification_task_id_ && *verification_task_id_ != task.id()) {
     return;
@@ -453,3 +456,59 @@
     saved_form_manager_.reset();
   }
 }
+void PasswordChangeFromCheckupDelegate::RegisterAutoSelectCredential(
+    actor::ActorTask& task) {
+  task.GetExecutionEngine().PreHandleCredentialSelectionDialog(
+      base::BindOnce(&PasswordChangeFromCheckupDelegate::AutoSelectCredential,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void PasswordChangeFromCheckupDelegate::InvokeVerificationFlow(
+    std::string post_submission_prompt) {
+  glic::GlicKeyedService* glic_service = GetGlicService();
+  if (!glic_service || !actuation_web_contents_) {
+    return;
+  }
+
+  tabs::TabInterface* tab_interface =
+      tabs::TabInterface::MaybeGetFromContents(actuation_web_contents_.get());
+  if (!tab_interface) {
+    return;
+  }
+
+  glic::GlicInvokeOptions options(
+      glic::Target(tab_interface, glic::NewConversation()),
+      glic::mojom::InvocationSource::kSharedTab);
+  options.prompts.push_back(std::move(post_submission_prompt));
+  options.additional_context = glic::mojom::AdditionalContext::New();
+  sessions::SessionTabHelper* session_tab_helper =
+      sessions::SessionTabHelper::FromWebContents(
+          actuation_web_contents_.get());
+  if (session_tab_helper) {
+    options.additional_context->tab_id = session_tab_helper->session_id().id();
+  }
+
+  glic_service->InvokeWithAutoSubmit(
+      glic::InvokeWithAutoSubmitPasskeyProvider::GetPassKey(),
+      std::move(options));
+
+  actor::ActorKeyedService* actor_service =
+      actor::ActorKeyedService::Get(Profile::FromBrowserContext(
+          actuation_web_contents_->GetBrowserContext()));
+  if (actor_service) {
+    actor_task_state_subscription_ =
+        actor_service->AddTaskStateChangedCallback(base::BindRepeating(
+            &PasswordChangeFromCheckupDelegate::OnVerificationTaskStateChanged,
+            base::Unretained(this)));
+  }
+
+  // TODO(crbug.com/485620841): Replace this timeout signal with
+  // InvokeWithUpdates once fully functional. Currently this assumes that if no
+  // task is created within 90 seconds, Bluedog was not triggered which means no
+  // extra steps are required for completion of the password change flow and
+  // assumes success.
+  verification_timer_.Start(
+      FROM_HERE, base::Seconds(90),
+      base::BindOnce(&PasswordChangeFromCheckupDelegate::OnVerificationTimeout,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
diff --git a/chrome/browser/password_manager/password_change/password_change_from_checkup_delegate.h b/chrome/browser/password_manager/password_change/password_change_from_checkup_delegate.h
index a1198bbf..fa60c92 100644
--- a/chrome/browser/password_manager/password_change/password_change_from_checkup_delegate.h
+++ b/chrome/browser/password_manager/password_change/password_change_from_checkup_delegate.h
@@ -67,6 +67,8 @@
   void OnVerificationTaskStateChanged(actor::ActorTask& task);
   void OnVerificationTimeout();
   void HandleMaybeSuccessfulPasswordChange();
+  void RegisterAutoSelectCredential(actor::ActorTask& task);
+  void InvokeVerificationFlow(std::string post_submission_prompt);
 
   base::WeakPtr<content::WebContents> originator_;
   base::WeakPtr<content::WebContents> actuation_web_contents_;
diff --git a/chrome/browser/password_manager/web_app_profile_switcher_interactive_uitest.cc b/chrome/browser/password_manager/web_app_profile_switcher_interactive_uitest.cc
index fc22004..6704c31 100644
--- a/chrome/browser/password_manager/web_app_profile_switcher_interactive_uitest.cc
+++ b/chrome/browser/password_manager/web_app_profile_switcher_interactive_uitest.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/profiles/profile_test_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
 #include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
@@ -101,7 +100,8 @@
 
   // Create a second profile.
   Profile* second_profile = CreateAdditionalProfile();
-  ASSERT_FALSE(chrome::FindBrowserWithProfile(second_profile));
+  ASSERT_FALSE(ProfileBrowserCollection::GetForProfile(second_profile)
+                   ->GetLastActiveBrowser());
   // Confirm that the profile has no installed app.
   ASSERT_FALSE(web_app::FindInstalledAppWithUrlInScope(second_profile,
                                                        GURL(kTestWebUIAppURL)));
@@ -118,8 +118,11 @@
   EXPECT_EQ(new_web_contents->GetVisibleURL(), GURL(kTestWebUIAppURL));
 
   // Check that the new WebContents belong to the second profile.
-  Browser* new_browser = chrome::FindBrowserWithProfile(second_profile);
-  ASSERT_TRUE(new_browser);
+  BrowserWindowInterface* new_browser_window =
+      ProfileBrowserCollection::GetForProfile(second_profile)
+          ->GetLastActiveBrowser();
+  ASSERT_TRUE(new_browser_window);
+  Browser* new_browser = new_browser_window->GetBrowserForMigrationOnly();
   EXPECT_EQ(new_browser->tab_strip_model()->GetActiveWebContents(),
             new_web_contents);
 
@@ -144,7 +147,8 @@
   // Create a second profile and install the app for it.
   Profile* second_profile = CreateAdditionalProfile();
   InstallAppForProfile(second_profile, GetTestWebAppInstallInfo());
-  ASSERT_FALSE(chrome::FindBrowserWithProfile(second_profile));
+  ASSERT_FALSE(ProfileBrowserCollection::GetForProfile(second_profile)
+                   ->GetLastActiveBrowser());
 
   // Verify that the app is launched for the second profile.
   ui_test_utils::AllBrowserTabAddedWaiter waiter;
@@ -158,7 +162,9 @@
   // Check that the new Browser belong to the second profile and Password
   // Manager is opened.
   ASSERT_TRUE(new_browser);
-  EXPECT_EQ(chrome::FindBrowserWithProfile(second_profile), new_browser);
+  EXPECT_EQ(ProfileBrowserCollection::GetForProfile(second_profile)
+                ->GetLastActiveBrowser(),
+            new_browser);
   EXPECT_EQ(
       GURL(kTestWebUIAppURL),
       new_browser->tab_strip_model()->GetActiveWebContents()->GetVisibleURL());
diff --git a/chrome/browser/permissions/permission_element_browsertest.cc b/chrome/browser/permissions/permission_element_browsertest.cc
index 1955831..936e1c7 100644
--- a/chrome/browser/permissions/permission_element_browsertest.cc
+++ b/chrome/browser/permissions/permission_element_browsertest.cc
@@ -388,10 +388,6 @@
 
 IN_PROC_BROWSER_TEST_F(PermissionElementBrowserTest,
                        DoubleClickDoesNotTriggerTwoRequests) {
-  permissions::PermissionRequestManager::FromWebContents(web_contents())
-      ->set_auto_response_for_test(
-          permissions::PermissionRequestManager::AutoResponseType::DISMISS);
-
   permissions::PermissionRequestObserver observer1(web_contents());
 
   // Click the element twice.
@@ -404,8 +400,16 @@
   // request.
   observer1.Wait();
   EXPECT_TRUE(observer1.request_shown());
+
+  // Dismiss the prompt.
+  auto* permission_request_manager =
+      permissions::PermissionRequestManager::FromWebContents(web_contents());
+  permission_request_manager->Dismiss(/*prompt_options=*/std::monostate());
+  permission_request_manager->FinalizeCurrentRequests();
   WaitForDismissEvent("microphone");
 
+  permission_request_manager->set_auto_response_for_test(
+      permissions::PermissionRequestManager::AutoResponseType::DISMISS);
   // Verify that no duplicate "microphone" requests or dismiss events are
   // created.
   permissions::PermissionRequestObserver observer2(web_contents());
diff --git a/chrome/browser/permissions/prediction_service/language_detection_observer.cc b/chrome/browser/permissions/prediction_service/language_detection_observer.cc
index ef8c12e..ba635c3f 100644
--- a/chrome/browser/permissions/prediction_service/language_detection_observer.cc
+++ b/chrome/browser/permissions/prediction_service/language_detection_observer.cc
@@ -77,9 +77,10 @@
 void LanguageDetectionObserver::OnTimeout() {
   VLOG(1) << "[PermissionsAIv4] LanguageDetectionObserver::OnTimeout";
   RecordLanguageDetectionStatus(LanguageDetectionStatus::kNoResultDueToTimeout);
-  if (on_english_detected_callback_) {
+  if (fallback_callback_) {
     std::move(fallback_callback_).Run();
   }
+  on_english_detected_callback_.Reset();
   RemoveAsObserver();
 }
 
@@ -96,6 +97,7 @@
            "English";
     RecordLanguageDetectionStatus(
         LanguageDetectionStatus::kDelayedDetectedEnglish);
+    fallback_callback_.Reset();
     std::move(on_english_detected_callback_).Run();
   } else if (on_english_detected_callback_) {
     VLOG(1)
@@ -103,6 +105,7 @@
            "NOT English";
     RecordLanguageDetectionStatus(
         LanguageDetectionStatus::kDelayedDetectedNotEnglish);
+    on_english_detected_callback_.Reset();
     std::move(fallback_callback_).Run();
   }
   timeout_timer_.Stop();
diff --git a/chrome/browser/prefs/pref_service_browsertest.cc b/chrome/browser/prefs/pref_service_browsertest.cc
index 320a562..827af52 100644
--- a/chrome/browser/prefs/pref_service_browsertest.cc
+++ b/chrome/browser/prefs/pref_service_browsertest.cc
@@ -22,6 +22,7 @@
 #include "chrome/test/base/chrome_test_utils.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/ui_test_utils.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/gfx/geometry/rect.h"
@@ -47,6 +48,7 @@
 #define MAYBE_Test Test
 #endif
 IN_PROC_BROWSER_TEST_F(PreservedWindowPlacement, MAYBE_Test) {
+  ui_test_utils::WaitUntilBrowserBecomeActive(browser());
   gfx::Rect bounds = browser()->window()->GetBounds();
   gfx::Rect expected_bounds(window_frame);
   ASSERT_EQ(expected_bounds.ToString(), bounds.ToString());
diff --git a/chrome/browser/profiles/avatar_menu_browsertest.cc b/chrome/browser/profiles/avatar_menu_browsertest.cc
index 835abf4..f6b10c0 100644
--- a/chrome/browser/profiles/avatar_menu_browsertest.cc
+++ b/chrome/browser/profiles/avatar_menu_browsertest.cc
@@ -18,6 +18,8 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/profiles/profile_picker.h"
 #include "chrome/browser/ui/profiles/profile_ui_test_utils.h"
@@ -108,10 +110,11 @@
 
   // A new browser is opened.
   EXPECT_EQ(chrome::GetBrowserCount(profile), 1U);
-  Browser* new_browser = chrome::FindBrowserWithProfile(profile);
+  BrowserWindowInterface* new_browser =
+      ProfileBrowserCollection::GetForProfile(profile)->GetLastActiveBrowser();
   ASSERT_TRUE(new_browser);
   content::WebContents* web_contents =
-      new_browser->tab_strip_model()->GetActiveWebContents();
+      new_browser->GetTabStripModel()->GetActiveWebContents();
   EXPECT_EQ(web_contents->GetVisibleURL(),
             chrome::GetSettingsUrl(chrome::kManageProfileSubPage));
 }
diff --git a/chrome/browser/resources/side_panel/read_anything/app/app.css b/chrome/browser/resources/side_panel/read_anything/app/app.css
index 65de13f..f96a441 100644
--- a/chrome/browser/resources/side_panel/read_anything/app/app.css
+++ b/chrome/browser/resources/side_panel/read_anything/app/app.css
@@ -29,16 +29,14 @@
 #lineFocus {
   position: absolute;
   top: var(--line-focus-y);
-  left: var(--sp-card-block-padding);
-  right: var(--sp-card-block-padding);
+  left: var(--line-focus-side-padding);
+  right: var(--line-focus-side-padding);
   height: var(--line-focus-height);
   background-color: var(--line-focus-bg);
   display: var(--line-focus-display);
   /* Darkens the area around this element when it is a window. Does nothing when
      it is a line. */
   box-shadow: var(--line-focus-shadow);
-  /* Clip path ensures the box shadow doesn't go beyond the content window. */
-  clip-path: inset(var(--line-focus-clip-top) 0 var(--line-focus-clip-bottom) 0 round 12px);
   pointer-events: none;
   z-index: 9997;
 }
diff --git a/chrome/browser/resources/side_panel/read_anything/app/app.html.ts b/chrome/browser/resources/side_panel/read_anything/app/app.html.ts
index 8221381..c8d3b18 100644
--- a/chrome/browser/resources/side_panel/read_anything/app/app.html.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app/app.html.ts
@@ -10,9 +10,10 @@
   // clang-format off
   return html`<!--_html_template_start_-->
 <div id="appFlexParent" class="${this.getImmersiveClass_()}">
-<!-- Overlay to prevent cursor from interacting with background elements when
- the settings menu is open. -->
-<div id="settingsOverlay" class="settings-overlay"></div>
+  <!-- Overlay to prevent cursor from interacting with background elements when
+  the settings menu is open. -->
+  <div id="settingsOverlay" class="settings-overlay"></div>
+  <div id="lineFocus" ?hidden="${!this.computeHasContent()}"></div>
   <div id="toolbar-container">
     <read-anything-toolbar
         .presentationState="${this.presentationState_}"
@@ -60,7 +61,6 @@
   </div>
   <div id="containerParent" class="sp-card"
       ?hidden="${!this.computeHasContent()}">
-    <div id="lineFocus"></div>
     <div id="containerScroller" class="sp-scroller"
         @scroll="${this.onContainerScroll_}"
         @scrollend="${this.onContainerScrollend_}"
diff --git a/chrome/browser/resources/side_panel/read_anything/app/app.ts b/chrome/browser/resources/side_panel/read_anything/app/app.ts
index 7b0a66e..95dfb9b4 100644
--- a/chrome/browser/resources/side_panel/read_anything/app/app.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app/app.ts
@@ -475,7 +475,7 @@
 
     this.styleUpdater_.setLineFocusPos(
         this.lineFocusController_.getTop(),
-        this.lineFocusController_.getHeight(), this.$.containerParent);
+        this.lineFocusController_.getHeight());
   }
 
   onNeedScrollForLineFocus(scrollDiff: number, instant: boolean = false): void {
@@ -622,7 +622,7 @@
       this.lineFocusController_.restoreFromPrefs(
           chrome.readingMode.lastNonDisabledLineFocus,
           chrome.readingMode.isLineFocusOn, this.$.container,
-          this.$.containerParent.clientHeight);
+          this.$.appFlexParent.clientHeight);
       this.setLineFocus_();
     }
     // TODO: crbug.com/40927698 - Remove this call. Using this.settingsPrefs_
@@ -686,7 +686,7 @@
     if (chrome.readingMode.isLineFocusEnabled) {
       this.lineFocusController_.onStyleChange(
           event.detail.data, this.$.container,
-          this.$.containerParent.clientHeight);
+          this.$.appFlexParent.clientHeight);
       this.lineFocusStyle_ =
           this.lineFocusController_.getCurrentLineFocusStyle();
       this.setLineFocus_();
@@ -698,7 +698,7 @@
     if (chrome.readingMode.isLineFocusEnabled) {
       this.lineFocusController_.onMovementChange(
           event.detail.data, this.$.container,
-          this.$.containerParent.clientHeight);
+          this.$.appFlexParent.clientHeight);
       this.lineFocusMovement_ =
           this.lineFocusController_.getCurrentLineFocusMovement();
       this.setLineFocus_();
@@ -739,7 +739,7 @@
         this.styleUpdater_.setPaddingForLineFocus(padding);
       }
       this.lineFocusController_.onTextLocationsChange(
-          this.$.container, this.$.containerParent.clientHeight);
+          this.$.container, this.$.appFlexParent.clientHeight);
     }
   }
 
@@ -774,7 +774,7 @@
     } else if (
         chrome.readingMode.isLineFocusEnabled && isLineFocusShortcut(e)) {
       this.lineFocusController_.toggle(
-          this.$.container, this.$.containerParent.offsetHeight);
+          this.$.container, this.$.appFlexParent.offsetHeight);
       this.styleUpdater_.setLineFocusStyle(
           this.lineFocusController_.getCurrentLineFocusType());
     }
diff --git a/chrome/browser/resources/side_panel/read_anything/app/app_style_updater.ts b/chrome/browser/resources/side_panel/read_anything/app/app_style_updater.ts
index 621ce22..e6c5b44 100644
--- a/chrome/browser/resources/side_panel/read_anything/app/app_style_updater.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app/app_style_updater.ts
@@ -59,6 +59,8 @@
 const LINE_FOCUS_BG_LINE_DEFAULT = 'var(--color-sys-state-focus-ring)';
 const LINE_FOCUS_BG_LINE_CUSTOM = 'var(--color-read-anything-line-focus';
 const LINE_FOCUS_BG_WINDOW = 'none';
+const LINE_FOCUS_SIDE_PADDING_LINE = 'var(--sp-card-block-padding)';
+const LINE_FOCUS_SIDE_PADDING_WINDOW = '0';
 
 // Suffixes used in combination with the color vars above to get the color
 // values for the current theme.
@@ -101,16 +103,10 @@
     return padding ? parseInt(padding) : 0;
   }
 
-  setLineFocusPos(y: number, height: number|null, container: HTMLElement) {
-    const containerTop = container.offsetTop;
-    const containerHeight = container.offsetHeight;
+  setLineFocusPos(y: number, height: number|null) {
     this.setStyle_('--line-focus-y', `${y}px`);
-    this.setStyle_('--line-focus-clip-top', `-${y - containerTop}px`);
     if (height) {
       this.setStyle_('--line-focus-height', `${height}px`);
-      this.setStyle_(
-          '--line-focus-clip-bottom',
-          `${- (containerHeight - y - height + containerTop)}px`);
     }
   }
 
@@ -133,6 +129,10 @@
     this.setStyle_(
         '--line-focus-bg', isWindow ? LINE_FOCUS_BG_WINDOW : lineFocusBgLine);
     this.setStyle_('--line-focus-display', 'block');
+    this.setStyle_(
+        '--line-focus-side-padding',
+        isWindow ? LINE_FOCUS_SIDE_PADDING_WINDOW :
+                   LINE_FOCUS_SIDE_PADDING_LINE);
   }
 
   setLineFocusHeight() {
diff --git a/chrome/browser/resources/side_panel/read_anything/app/read_anything_toolbar.css b/chrome/browser/resources/side_panel/read_anything/app/read_anything_toolbar.css
index ab9ccbd..476e281 100644
--- a/chrome/browser/resources/side_panel/read_anything/app/read_anything_toolbar.css
+++ b/chrome/browser/resources/side_panel/read_anything/app/read_anything_toolbar.css
@@ -62,6 +62,8 @@
   height: fit-content;
   margin-inline-end: var(--audio-controls-right-margin);
   padding: 4px var(--audio-controls-right-padding) 4px 4px;
+  position: relative;
+  z-index: 9998;
 }
 
 #audio-controls.audio-background-immersive-enabled {
@@ -205,6 +207,9 @@
   height: 20px;
   margin: 6px 8px;
   width: 0;
+  /* Ensure this stays above the line focus scrim. */
+  position: relative;
+  z-index: 9998;
 }
 
 .spinner {
diff --git a/chrome/browser/resources/side_panel/read_anything/app/toolbar_styles_shared.css b/chrome/browser/resources/side_panel/read_anything/app/toolbar_styles_shared.css
index 1747f82..f39e788 100644
--- a/chrome/browser/resources/side_panel/read_anything/app/toolbar_styles_shared.css
+++ b/chrome/browser/resources/side_panel/read_anything/app/toolbar_styles_shared.css
@@ -18,3 +18,9 @@
 cr-icon-button.active {
   background-color: var(--cr-active-background-color);
 }
+
+.toolbar-button {
+  /* Ensure buttons stay above the line focus scrim. */
+  position: relative;
+  z-index: 9998;
+}
diff --git a/chrome/browser/resources/side_panel/read_anything/content/line_focus_controller.ts b/chrome/browser/resources/side_panel/read_anything/content/line_focus_controller.ts
index bbb0042..0f86336 100644
--- a/chrome/browser/resources/side_panel/read_anything/content/line_focus_controller.ts
+++ b/chrome/browser/resources/side_panel/read_anything/content/line_focus_controller.ts
@@ -502,7 +502,7 @@
     const currentLineFocus = this.getCurrentLineFocusStyle();
     assert(!!currentLineFocus);
     this.model_.setMinY(container.offsetTop);
-    this.model_.setMaxY(this.model_.getMinY() + height);
+    this.model_.setMaxY(height);
 
     const range = document.createRange();
     range.selectNodeContents(container);
@@ -585,7 +585,7 @@
   }
 
   private setCenterY_() {
-    this.setY_((this.model_.getMinY() + this.model_.getMaxY()) / 2);
+    this.setY_(this.model_.getMaxY() / 2);
   }
 
   static getInstance(): LineFocusController {
diff --git a/chrome/browser/safe_browsing/safe_browsing_pref_change_handler.cc b/chrome/browser/safe_browsing/safe_browsing_pref_change_handler.cc
index bea38f0..9841aa1 100644
--- a/chrome/browser/safe_browsing/safe_browsing_pref_change_handler.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_pref_change_handler.cc
@@ -20,8 +20,9 @@
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN) || \
     BUILDFLAG(IS_MAC)
-#include "chrome/browser/ui/browser_finder.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/public/profile_browser_collection.h"
 #include "chrome/browser/ui/toasts/api/toast_id.h"
 #include "chrome/browser/ui/toasts/toast_controller.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -113,7 +114,10 @@
   if (safe_browsing::IsSafeBrowsingPolicyManaged(*profile_->GetPrefs())) {
     return;
   }
-  Browser* const browser = chrome::FindBrowserWithProfile(profile_);
+  ProfileBrowserCollection* const collection =
+      ProfileBrowserCollection::GetForProfile(profile_);
+  BrowserWindowInterface* const browser =
+      collection ? collection->GetLastActiveBrowser() : nullptr;
   if (!browser) {
     return;
   }
@@ -124,7 +128,7 @@
   ToastController* const controller =
       toast_controller_for_testing_
           ? static_cast<ToastController*>(toast_controller_for_testing_)
-          : browser->browser_window_features()->toast_controller();
+          : browser->GetFeatures().toast_controller();
   if (!controller) {
     return;
   }
diff --git a/chrome/browser/safe_browsing/security_settings_bundle_toast_helper.cc b/chrome/browser/safe_browsing/security_settings_bundle_toast_helper.cc
index dd31bd2..46bdbe3 100644
--- a/chrome/browser/safe_browsing/security_settings_bundle_toast_helper.cc
+++ b/chrome/browser/safe_browsing/security_settings_bundle_toast_helper.cc
@@ -7,9 +7,9 @@
 #include "base/functional/bind.h"
 #include "base/metrics/histogram_functions.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/toasts/api/toast_id.h"
 #include "chrome/browser/ui/toasts/toast_controller.h"
 #include "components/prefs/pref_service.h"
@@ -91,11 +91,14 @@
   if (toast_controller_for_testing_) {
     return toast_controller_for_testing_;
   }
-  Browser* browser = chrome::FindBrowserWithProfile(profile_);
+  ProfileBrowserCollection* const collection =
+      ProfileBrowserCollection::GetForProfile(profile_);
+  BrowserWindowInterface* browser =
+      collection ? collection->GetLastActiveBrowser() : nullptr;
   if (!browser) {
     return nullptr;
   }
-  return browser->browser_window_features()->toast_controller();
+  return browser->GetFeatures().toast_controller();
 }
 
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/tailored_security/chrome_tailored_security_service.cc b/chrome/browser/safe_browsing/tailored_security/chrome_tailored_security_service.cc
index f586d7b..08cd712e 100644
--- a/chrome/browser/safe_browsing/tailored_security/chrome_tailored_security_service.cc
+++ b/chrome/browser/safe_browsing/tailored_security/chrome_tailored_security_service.cc
@@ -11,7 +11,8 @@
 #include "chrome/browser/safe_browsing/generated_security_settings_bundle_pref.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/sync_service_factory.h"
-#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/core/browser/tailored_security_service/tailored_security_notification_result.h"
 #include "components/safe_browsing/core/browser/tailored_security_service/tailored_security_service_util.h"
@@ -132,7 +133,10 @@
                      base::Unretained(this)),
       /*is_requested_by_synced_esb=*/false);
 #else
-  BrowserWindowInterface* browser = chrome::FindBrowserWithProfile(profile_);
+  ProfileBrowserCollection* const collection =
+      ProfileBrowserCollection::GetForProfile(profile_);
+  BrowserWindowInterface* browser =
+      collection ? collection->GetLastActiveBrowser() : nullptr;
   if (!browser) {
     if (is_enabled) {
       RecordEnabledNotificationResult(
@@ -194,7 +198,12 @@
   } else {
     disabled_notice_handle_ = std::move(messaging_priority_handle);
   }
-  DisplayDesktopDialog(chrome::FindBrowserWithProfile(profile_), is_enabled);
+  ProfileBrowserCollection* const collection =
+      ProfileBrowserCollection::GetForProfile(profile_);
+  BrowserWindowInterface* browser =
+      collection ? collection->GetLastActiveBrowser() : nullptr;
+  DisplayDesktopDialog(
+      browser ? browser->GetBrowserForMigrationOnly() : nullptr, is_enabled);
 }
 
 void ChromeTailoredSecurityService::ReleaseEnabledQueueHandle() {
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index 7ec443b10..6af5a25e 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -70,6 +70,7 @@
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
 #include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/startup/startup_tab.h"
 #include "chrome/browser/ui/startup/startup_types.h"
 #include "chrome/browser/ui/tabs/split_tab_metrics.h"
@@ -2416,7 +2417,9 @@
   removed_observer.Wait();
 
   // The second profile should have no browsers anymore at this point.
-  ASSERT_EQ(chrome::FindBrowserWithProfile(second_profile), nullptr);
+  ASSERT_EQ(ProfileBrowserCollection::GetForProfile(second_profile)
+                ->GetLastActiveBrowser(),
+            nullptr);
   ASSERT_EQ(chrome::GetTotalBrowserCount(), 2u);
 
   // Clean up now stale pointers.
diff --git a/chrome/browser/share/BUILD.gn b/chrome/browser/share/BUILD.gn
index b9deb48..0cdbcf3 100644
--- a/chrome/browser/share/BUILD.gn
+++ b/chrome/browser/share/BUILD.gn
@@ -10,22 +10,38 @@
 }
 
 source_set("share") {
+  public = [
+    "default_ranking.h",
+    "share_attempt.h",
+    "share_history.h",
+    "share_metrics.h",
+    "share_ranking.h",
+  ]
+
+  public_deps = [
+    "//base",
+    "//chrome/browser/share/proto",
+    "//components/leveldb_proto",
+    "//ui/base",
+    "//url",
+  ]
+}
+
+source_set("impl") {
   sources = [
     "default_ranking.cc",
-    "default_ranking.h",
     "share_attempt.cc",
-    "share_attempt.h",
     "share_history.cc",
-    "share_history.h",
     "share_metrics.cc",
-    "share_metrics.h",
     "share_ranking.cc",
-    "share_ranking.h",
   ]
 
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
-  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
+  public_deps = [
+    ":share",
+    "//chrome/browser:browser_public_dependencies",
+  ]
 
   deps = [
     "//base",
@@ -37,6 +53,7 @@
     "//components/shared_highlighting/core/common",
     "//content/public/browser",
     "//skia",
+    "//ui/base",
     "//ui/gfx",
     "//ui/snapshot",
   ]
@@ -61,6 +78,57 @@
   }
 }
 
+source_set("test_support") {
+  testonly = true
+
+  sources = [
+    "fake_share_history.cc",
+    "fake_share_history.h",
+  ]
+
+  deps = [
+    ":share",
+    "//base",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [
+    "share_history_unittest.cc",
+    "share_ranking_unittest.cc",
+  ]
+
+  deps = [
+    ":share",
+    ":test_support",
+    "//base",
+    "//base/test:test_support",
+    "//chrome/test:test_support",
+    "//components/leveldb_proto:test_support",
+    "//content/test:test_support",
+    "//testing/gtest",
+  ]
+}
+
+source_set("browser_tests") {
+  testonly = true
+  defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+
+  sources = [ "qr_code_generator_pixeltest.cc" ]
+
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//chrome/test:test_support",
+    "//components/qr_code_generator:bitmap_generator",
+    "//content/public/common",
+    "//content/test:test_support",
+    "//ui/base:test_support",
+  ]
+}
+
 if (is_android) {
   android_library("java") {
     srcjar_deps = [ ":jni_headers" ]
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridge.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridge.java
index 65106d8..0242e04 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridge.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridge.java
@@ -54,16 +54,6 @@
     }
 
     /**
-     * Deletes the entry associated with the GUID.
-     *
-     * @param profile Profile of the user to delete entry for.
-     * @param guid The GUID to delete the entry for.
-     */
-    public static void deleteEntry(Profile profile, String guid) {
-        SendTabToSelfAndroidBridgeJni.get().deleteEntry(profile, guid);
-    }
-
-    /**
      * Marks the entry associated with the GUID as opened.
      *
      * @param profile Profile of the user to mark entry for.
@@ -115,8 +105,6 @@
                 String title,
                 CommitConfirmationCallback commitConfirmation);
 
-        void deleteEntry(@JniType("Profile*") Profile profile, String guid);
-
         void markEntryOpened(@JniType("Profile*") Profile profile, String guid);
 
         void dismissEntry(@JniType("Profile*") Profile profile, String guid);
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java
index aca72db..2fc8a7f 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridgeTest.java
@@ -62,14 +62,6 @@
 
     @Test
     @SmallTest
-    public void testDeleteEntry() {
-        String guid = "guid";
-        SendTabToSelfAndroidBridge.deleteEntry(mProfile, guid);
-        verify(mNativeMock).deleteEntry(eq(mProfile), eq(guid));
-    }
-
-    @Test
-    @SmallTest
     public void testMarkEntryOpened() {
         String guid = "guid";
         SendTabToSelfAndroidBridge.markEntryOpened(mProfile, guid);
diff --git a/chrome/browser/sharing/one_time_tokens/one_time_token_sharing_handler.cc b/chrome/browser/sharing/one_time_tokens/one_time_token_sharing_handler.cc
index cc0c988..4ebcb65 100644
--- a/chrome/browser/sharing/one_time_tokens/one_time_token_sharing_handler.cc
+++ b/chrome/browser/sharing/one_time_tokens/one_time_token_sharing_handler.cc
@@ -6,6 +6,7 @@
 
 #include "base/metrics/histogram_functions.h"
 #include "chrome/browser/autofill/gmail_otp_backend_factory.h"
+#include "components/one_time_tokens/core/browser/encrypted_message_reference.h"
 #include "components/one_time_tokens/core/browser/gmail_otp_backend.h"
 #include "components/sharing_message/proto/one_time_token_backend_notification.pb.h"
 #include "components/sharing_message/proto/sharing_message.pb.h"
@@ -46,7 +47,7 @@
     return OneTimeTokenValidationResult::kEmptyEncryptedMessageReference;
   }
   gmail_otp_backend_->OnIncomingOneTimeTokenBackendTickle(
-      one_time_tokens::GmailOtpBackend::EncryptedMessageReference(
+      one_time_tokens::EncryptedMessageReference(
           notification.gmail_one_time_password()
               .encrypted_message_reference()));
   return OneTimeTokenValidationResult::kSuccess;
diff --git a/chrome/browser/sharing/one_time_tokens/one_time_token_sharing_handler_unittest.cc b/chrome/browser/sharing/one_time_tokens/one_time_token_sharing_handler_unittest.cc
index 3685f6d..f1587a1 100644
--- a/chrome/browser/sharing/one_time_tokens/one_time_token_sharing_handler_unittest.cc
+++ b/chrome/browser/sharing/one_time_tokens/one_time_token_sharing_handler_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
+#include "components/one_time_tokens/core/browser/encrypted_message_reference.h"
 #include "components/one_time_tokens/core/browser/gmail_otp_backend.h"
 #include "components/sharing_message/proto/sharing_message.pb.h"
 #include "components/sharing_message/sharing_message_handler.h"
@@ -23,12 +24,11 @@
               (base::Time expiration, Callback callback),
               (override));
 
-  MOCK_METHOD(
-      void,
-      OnIncomingOneTimeTokenBackendTickle,
-      (const one_time_tokens::GmailOtpBackend::EncryptedMessageReference&
-           encrypted_message_reference),
-      (override));
+  MOCK_METHOD(void,
+              OnIncomingOneTimeTokenBackendTickle,
+              (const one_time_tokens::EncryptedMessageReference&
+                   encrypted_message_reference),
+              (override));
 };
 
 MATCHER_P(OneTimeTokenTickleHasMessageReference,
diff --git a/chrome/browser/shortcuts/shortcut_launch_browsertest.cc b/chrome/browser/shortcuts/shortcut_launch_browsertest.cc
index af00d9d..f6bc7d6 100644
--- a/chrome/browser/shortcuts/shortcut_launch_browsertest.cc
+++ b/chrome/browser/shortcuts/shortcut_launch_browsertest.cc
@@ -10,9 +10,9 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_test_util.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -69,7 +69,8 @@
           /*cur_dir=*/{}, command_line, /*ignore_profile_picker=*/false));
 
   BrowserWindowInterface* browser =
-      chrome::FindBrowserWithProfile(&other_profile);
+      ProfileBrowserCollection::GetForProfile(&other_profile)
+          ->GetLastActiveBrowser();
   ASSERT_TRUE(browser);
 
   content::WebContents* web_contents =
diff --git a/chrome/browser/signin/chrome_signin_helper.cc b/chrome/browser/signin/chrome_signin_helper.cc
index 7e28ea9..02da846 100644
--- a/chrome/browser/signin/chrome_signin_helper.cc
+++ b/chrome/browser/signin/chrome_signin_helper.cc
@@ -419,7 +419,7 @@
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 
 void ProcessDiceHeader(
-    const DiceResponseParams& dice_params,
+    DiceResponseParams dice_params,
     const content::WebContents::Getter& web_contents_getter) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
@@ -438,7 +438,8 @@
   DiceResponseHandler* dice_response_handler =
       DiceResponseHandlerFactory::GetForProfile(profile);
   dice_response_handler->ProcessDiceHeader(
-      dice_params, ProcessDiceHeaderDelegateImpl::Create(web_contents));
+      std::move(dice_params),
+      ProcessDiceHeaderDelegateImpl::Create(web_contents));
 }
 #endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
diff --git a/chrome/browser/signin/dice_response_handler.cc b/chrome/browser/signin/dice_response_handler.cc
index 931f5d7a..6e2639b 100644
--- a/chrome/browser/signin/dice_response_handler.cc
+++ b/chrome/browser/signin/dice_response_handler.cc
@@ -119,22 +119,20 @@
     bool mtls_token_binding,
     SigninClient* signin_client,
     AccountReconcilor* account_reconcilor,
-    std::unique_ptr<ProcessDiceHeaderDelegate> delegate,
     base::expected<raw_ref<BindingKeyRegistrationTokenHelper>,
                    TokenBindingOutcome> registration_token_helper_or_error,
-    DiceResponseHandler* dice_response_handler)
+    DiceSigninSession* session)
     : gaia_id_(gaia_id),
       email_(email),
       authorization_code_(authorization_code),
       mtls_token_binding_(mtls_token_binding),
-      delegate_(std::move(delegate)),
-      dice_response_handler_(dice_response_handler),
+      session_(session),
       signin_client_(signin_client),
       timeout_closure_(
           base::BindOnce(&DiceResponseHandler::DiceTokenFetcher::OnTimeout,
                          base::Unretained(this))),
       should_enable_sync_(false) {
-  DCHECK(dice_response_handler_);
+  CHECK(session_);
   account_reconcilor_lock_ =
       std::make_unique<AccountReconcilor::Lock>(account_reconcilor);
   if (registration_token_helper_or_error.has_value()) {
@@ -153,7 +151,7 @@
   RecordDiceFetchTokenResult(kFetchTimeout);
   gaia_auth_fetcher_.reset();
   timeout_closure_.Cancel();
-  dice_response_handler_->OnTokenExchangeFailure(
+  session_->OnTokenExchangeFailure(
       this, GoogleServiceAuthError::CreateRequestCanceled());
   // |this| may be deleted at this point.
 }
@@ -175,9 +173,9 @@
   }
   base::UmaHistogramEnumeration(kDiceTokenBindingOutcomeHistogram,
                                 token_binding_outcome_);
-  dice_response_handler_->OnTokenExchangeSuccess(
-      this, result.refresh_token, result.is_under_advanced_protection,
-      wrapped_binding_key_);
+  session_->OnTokenExchangeSuccess(this, result.refresh_token,
+                                   result.is_under_advanced_protection,
+                                   wrapped_binding_key_);
   // |this| may be deleted at this point.
 }
 
@@ -186,7 +184,7 @@
   RecordDiceFetchTokenResult(kFetchFailure);
   gaia_auth_fetcher_.reset();
   timeout_closure_.Cancel();
-  dice_response_handler_->OnTokenExchangeFailure(this, error);
+  session_->OnTokenExchangeFailure(this, error);
   // |this| may be deleted at this point.
 }
 
@@ -235,6 +233,148 @@
   StartTokenFetch();
 }
 
+// DiceSigninSession
+
+DiceResponseHandler::DiceSigninSession::DiceSigninSession(
+    DiceResponseHandler* handler,
+    std::unique_ptr<ProcessDiceHeaderDelegate> delegate,
+    signin::DiceResponseParams::SigninInfo signin_info)
+    : handler_(handler),
+      delegate_(std::move(delegate)),
+      signin_info_(std::move(signin_info)) {
+  CHECK(handler_);
+  CHECK(delegate_);
+}
+
+DiceResponseHandler::DiceSigninSession::~DiceSigninSession() = default;
+
+void DiceResponseHandler::DiceSigninSession::StartTokenFetch() {
+  const auto* initiator = signin_info_.GetInitiator();
+  CHECK(initiator);
+
+  // The user is signing in, which means that account fetching will shortly be
+  // triggered.
+  //
+  // Notify identity manager. This will trigger pre-connecting the network
+  // socket to the AccountCapabilities endpoint, in parallel with the LST and
+  // access token requests (instead of waiting for these to complete).
+  handler_->identity_manager_->PrepareForAddingNewAccount();
+
+  base::expected<raw_ref<BindingKeyRegistrationTokenHelper>,
+                 TokenBindingOutcome>
+      registration_token_helper_or_error =
+          handler_->MaybeGetBindingRegistrationTokenHelper(
+              initiator->supported_algorithms_for_token_binding);
+
+  token_fetcher_ = std::make_unique<DiceTokenFetcher>(
+      initiator->account_info.gaia_id, initiator->account_info.email,
+      initiator->authorization_code, initiator->mtls_token_binding,
+      handler_->signin_client_, handler_->account_reconcilor_,
+      registration_token_helper_or_error, this);
+}
+
+void DiceResponseHandler::DiceSigninSession::OnTokenExchangeSuccess(
+    DiceTokenFetcher* fetcher,
+    const std::string& refresh_token,
+    bool is_under_advanced_protection,
+    const std::vector<uint8_t>& wrapped_binding_key) {
+  const std::string& email = fetcher->email();
+  const GaiaId& gaia_id = fetcher->gaia_id();
+  // Log is consumed by E2E tests. Please CC potassium-engprod@google.com if you
+  // have to change this log.
+  VLOG(1) << "[Dice] OAuth success for email " << email;
+  CoreAccountId account_id =
+      handler_->identity_manager_->PickAccountIdForAccount(gaia_id, email);
+  bool is_new_account =
+      !handler_->identity_manager_->HasAccountWithRefreshToken(account_id);
+
+  handler_->identity_manager_->GetAccountsMutator()->AddOrUpdateAccount(
+      gaia_id, email, refresh_token, is_under_advanced_protection,
+      delegate_->GetAccessPoint(),
+      signin_metrics::SourceForRefreshTokenOperation::
+          kDiceResponseHandler_Signin,
+      signin::TokenBindingInfo(wrapped_binding_key,
+                               fetcher->mtls_token_binding()));
+
+  handler_->about_signin_internals_->OnRefreshTokenReceived(
+      base::StringPrintf("Successful (%s)", account_id.ToString().c_str()));
+
+  delegate_->HandleTokenExchangeSuccess(account_id, is_new_account);
+
+  if (fetcher->should_enable_sync()) {
+    delegate_->CompleteChromeSignInAfterGaiaSignin(
+        handler_->identity_manager_->FindExtendedAccountInfoByAccountId(
+            account_id));
+  }
+
+  CHECK_EQ(token_fetcher_.get(), fetcher);
+  token_fetcher_.reset();
+  handler_->DeleteSession(this);
+}
+
+void DiceResponseHandler::DiceSigninSession::OnTokenExchangeFailure(
+    DiceTokenFetcher* fetcher,
+    const GoogleServiceAuthError& error) {
+  const std::string& email = fetcher->email();
+  CoreAccountId account_id =
+      handler_->identity_manager_->PickAccountIdForAccount(fetcher->gaia_id(),
+                                                           email);
+  handler_->about_signin_internals_->OnRefreshTokenReceived(
+      base::StringPrintf("Failure (%s)", account_id.ToString().c_str()));
+
+  delegate_->HandleTokenExchangeFailure(email, error);
+  VLOG(1) << "Initiator fetch failed. Aborting session.";
+
+  CHECK_EQ(token_fetcher_.get(), fetcher);
+  token_fetcher_.reset();
+  handler_->DeleteSession(this);
+}
+
+bool DiceResponseHandler::DiceSigninSession::IsFetchingForAccount(
+    const CoreAccountId& account_id) const {
+  if (!token_fetcher_) {
+    return false;
+  }
+  return handler_->identity_manager_->PickAccountIdForAccount(
+             token_fetcher_->gaia_id(), token_fetcher_->email()) == account_id;
+}
+
+bool DiceResponseHandler::DiceSigninSession::CancelFetchForAccount(
+    const CoreAccountId& account_id) {
+  if (!token_fetcher_) {
+    return false;
+  }
+  CoreAccountId fetcher_account_id =
+      handler_->identity_manager_->PickAccountIdForAccount(
+          token_fetcher_->gaia_id(), token_fetcher_->email());
+  if (fetcher_account_id == account_id) {
+    token_fetcher_.reset();
+    handler_->DeleteSession(this);
+    return true;
+  }
+  return false;
+}
+
+bool DiceResponseHandler::DiceSigninSession::MarkEnableSyncIfFetching(
+    const GaiaId& gaia_id,
+    const std::string& email) {
+  if (token_fetcher_ && token_fetcher_->gaia_id() == gaia_id) {
+    DCHECK(gaia::AreEmailsSame(token_fetcher_->email(), email));
+    token_fetcher_->set_should_enable_sync(true);
+    return true;
+  }
+  return false;
+}
+
+bool DiceResponseHandler::DiceSigninSession::IsFetchingFor(
+    const GaiaId& gaia_id,
+    const std::string& email,
+    const std::string& authorization_code) const {
+  return token_fetcher_ && token_fetcher_->gaia_id() == gaia_id &&
+         token_fetcher_->email() == email &&
+         token_fetcher_->authorization_code() == authorization_code;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // DiceResponseHandler
 ////////////////////////////////////////////////////////////////////////////////
@@ -260,7 +400,7 @@
 DiceResponseHandler::~DiceResponseHandler() = default;
 
 void DiceResponseHandler::ProcessDiceHeader(
-    const signin::DiceResponseParams& dice_params,
+    signin::DiceResponseParams dice_params,
     std::unique_ptr<ProcessDiceHeaderDelegate> delegate) {
   if (!dice_params.IsValid()) {
     return;
@@ -269,19 +409,10 @@
   CHECK(delegate);
   switch (dice_params.user_intention()) {
     case signin::DiceAction::SIGNIN: {
-      const signin::DiceResponseParams::SigninInfo* signin_info =
+      signin::DiceResponseParams::SigninInfo* signin_info =
           dice_params.signin_info();
       CHECK(signin_info);
-      const signin::DiceResponseParams::SigninInfo::SigninAccount* initiator =
-          signin_info->GetInitiator();
-      CHECK(initiator);
-      const signin::DiceResponseParams::AccountInfo& info =
-          initiator->account_info;
-      ProcessDiceSigninHeader(
-          info.gaia_id, info.email, initiator->authorization_code,
-          initiator->no_authorization_code,
-          initiator->supported_algorithms_for_token_binding,
-          initiator->mtls_token_binding, std::move(delegate));
+      ProcessDiceSigninHeader(std::move(*signin_info), std::move(delegate));
       return;
     }
     case signin::DiceAction::ENABLE_SYNC: {
@@ -307,35 +438,19 @@
   NOTREACHED();
 }
 
-size_t DiceResponseHandler::GetPendingDiceTokenFetchersCountForTesting() const {
-  return token_fetchers_.size();
-}
-
-void DiceResponseHandler::OnTimeoutUnlockReconcilor() {
-  lock_.reset();
-}
-
-void DiceResponseHandler::SetTaskRunner(
-    scoped_refptr<base::SequencedTaskRunner> task_runner) {
-  task_runner_ = std::move(task_runner);
-}
-
-void DiceResponseHandler::SetRegistrationTokenHelperFactoryForTesting(
-    RegistrationTokenHelperFactory factory) {
-  CHECK(
-      switches::IsChromeRefreshTokenBindingEnabled(signin_client_->GetPrefs()));
-  registration_token_helper_factory_ = std::move(factory);
-}
-
 void DiceResponseHandler::ProcessDiceSigninHeader(
-    const GaiaId& gaia_id,
-    const std::string& email,
-    const std::string& authorization_code,
-    bool no_authorization_code,
-    const std::string& supported_algorithms_for_token_binding,
-    bool mtls_token_binding,
+    signin::DiceResponseParams::SigninInfo signin_info,
     std::unique_ptr<ProcessDiceHeaderDelegate> delegate) {
-  if (no_authorization_code) {
+  VLOG(1) << "Start processing Dice signin response";
+  RecordDiceResponseHeader(kSignin);
+
+  delegate->OnDiceSigninHeaderReceived();
+
+  const signin::DiceResponseParams::SigninInfo::SigninAccount* initiator =
+      signin_info.GetInitiator();
+  CHECK(initiator);
+
+  if (initiator->no_authorization_code) {
     lock_ = std::make_unique<AccountReconcilor::Lock>(account_reconcilor_);
     about_signin_internals_->OnRefreshTokenReceived(
         "Missing authorization code due to OAuth outage in Dice.");
@@ -354,39 +469,49 @@
     return;
   }
 
-  DCHECK(!gaia_id.empty());
-  DCHECK(!email.empty());
-  DCHECK(!authorization_code.empty());
-  VLOG(1) << "Start processing Dice signin response";
-  RecordDiceResponseHeader(kSignin);
-
-  delegate->OnDiceSigninHeaderReceived();
-
-  for (auto it = token_fetchers_.begin(); it != token_fetchers_.end(); ++it) {
-    if ((it->get()->gaia_id() == gaia_id) && (it->get()->email() == email) &&
-        (it->get()->authorization_code() == authorization_code)) {
+  const signin::DiceResponseParams::AccountInfo& info = initiator->account_info;
+  for (const auto& current_session : sessions_) {
+    if (current_session->IsFetchingFor(info.gaia_id, info.email,
+                                       initiator->authorization_code)) {
       RecordDiceFetchTokenResult(kFetchAbort);
-      return;  // There is already a request in flight with the same parameters.
+      // TODO(crbug.com/320650263): Aborting here ignores potential new
+      // secondary accounts in the header. This is fine for now as we only
+      // fetch the initiator account, but will need to be addressed for
+      // concurrent fetching.
+      return;  // There is already a request in flight with the same
+               // parameters.
     }
   }
 
-  // The user is signing in, which means that account fetching will shortly be
-  // triggered.
-  //
-  // Notify identity manager. This will trigger pre-connecting the network
-  // socket to the AccountCapabilities endpoint, in parallel with the LST and
-  // access token requests (instead of waiting for these to complete).
-  identity_manager_->PrepareForAddingNewAccount();
+  auto session = std::make_unique<DiceSigninSession>(this, std::move(delegate),
+                                                     std::move(signin_info));
+  sessions_.push_back(std::move(session));
+  sessions_.back()->StartTokenFetch();
+}
 
-  base::expected<raw_ref<BindingKeyRegistrationTokenHelper>, TokenBindingOutcome>
-      registration_token_helper_or_error =
-          MaybeGetBindingRegistrationTokenHelper(
-              supported_algorithms_for_token_binding);
+size_t DiceResponseHandler::GetPendingDiceTokenFetchersCountForTesting() const {
+  size_t count = 0;
+  for (const auto& session : sessions_) {
+    count += session->GetPendingDiceTokenFetchersCountForTesting();  // IN-TEST
+  }
+  return count;
+}
 
-  token_fetchers_.push_back(std::make_unique<DiceTokenFetcher>(
-      gaia_id, email, authorization_code, mtls_token_binding, signin_client_,
-      account_reconcilor_, std::move(delegate),
-      registration_token_helper_or_error, this));
+void DiceResponseHandler::OnTimeoutUnlockReconcilor() {
+  lock_.reset();
+}
+
+void DiceResponseHandler::SetTaskRunner(
+    scoped_refptr<base::SequencedTaskRunner> task_runner) {
+  task_runner_ = std::move(task_runner);
+}
+
+void DiceResponseHandler::
+    SetRegistrationTokenHelperFactoryForTesting(  // IN-TEST
+        RegistrationTokenHelperFactory factory) {
+  CHECK(
+      switches::IsChromeRefreshTokenBindingEnabled(signin_client_->GetPrefs()));
+  registration_token_helper_factory_ = std::move(factory);
 }
 
 void DiceResponseHandler::ProcessEnableSyncHeader(
@@ -395,14 +520,8 @@
     std::unique_ptr<ProcessDiceHeaderDelegate> delegate) {
   VLOG(1) << "Start processing Dice enable sync response";
   RecordDiceResponseHeader(kEnableSync);
-  for (auto it = token_fetchers_.begin(); it != token_fetchers_.end(); ++it) {
-    DiceTokenFetcher* fetcher = it->get();
-    if (fetcher->gaia_id() == gaia_id) {
-      DCHECK(gaia::AreEmailsSame(fetcher->email(), email));
-      // If there is a fetch in progress for a resfresh token for the given
-      // account, then simply mark it to enable sync after the refresh token is
-      // available.
-      fetcher->set_should_enable_sync(true);
+  for (const auto& session : sessions_) {
+    if (session->MarkEnableSyncIfFetching(gaia_id, email)) {
       return;  // There is already a request in flight with the same parameters.
     }
   }
@@ -446,18 +565,20 @@
     }
 
     // If a token fetch is in flight for the same account, cancel it.
-    for (auto it = token_fetchers_.begin(); it != token_fetchers_.end(); ++it) {
-      CoreAccountId token_fetcher_account_id =
-          identity_manager_->PickAccountIdForAccount(it->get()->gaia_id(),
-                                                     it->get()->email());
-      if (token_fetcher_account_id == signed_out_account) {
-        token_fetchers_.erase(it);
-        break;
-      }
+    // Note: This might lead to the session being deleted synchronously inside
+    // `CancelFetchForAccount()`, invalidating the iterator. This is safe as we
+    // do not use the iterator after this call.
+    auto it =
+        std::find_if(sessions_.begin(), sessions_.end(),
+                     [&signed_out_account](const auto& session) {
+                       return session->IsFetchingForAccount(signed_out_account);
+                     });
+    if (it != sessions_.end()) {
+      (*it)->CancelFetchForAccount(signed_out_account);
     }
   }
 
-  if (token_fetchers_.empty()) {
+  if (sessions_.empty()) {
     registration_token_helper_.reset();
   }
 
@@ -466,69 +587,17 @@
   }
 }
 
-void DiceResponseHandler::DeleteTokenFetcher(DiceTokenFetcher* token_fetcher) {
-  size_t delete_count =
-      std::erase_if(token_fetchers_, [token_fetcher](const auto& current) {
-        return current.get() == token_fetcher;
-      });
+void DiceResponseHandler::DeleteSession(DiceSigninSession* session) {
+  size_t delete_count = std::erase_if(
+      sessions_,
+      [session](const auto& current) { return current.get() == session; });
   CHECK_EQ(delete_count, 1U);
 
-  if (token_fetchers_.empty()) {
+  if (sessions_.empty()) {
     registration_token_helper_.reset();
   }
 }
 
-void DiceResponseHandler::OnTokenExchangeSuccess(
-    DiceTokenFetcher* token_fetcher,
-    const std::string& refresh_token,
-    bool is_under_advanced_protection,
-    const std::vector<uint8_t>& wrapped_binding_key) {
-  const std::string& email = token_fetcher->email();
-  const GaiaId& gaia_id = token_fetcher->gaia_id();
-
-  // Log is consumed by E2E tests. Please CC potassium-engprod@google.com if you
-  // have to change this log.
-  VLOG(1) << "[Dice] OAuth success for email " << email;
-  bool should_enable_sync = token_fetcher->should_enable_sync();
-  CoreAccountId account_id =
-      identity_manager_->PickAccountIdForAccount(gaia_id, email);
-  bool is_new_account =
-      !identity_manager_->HasAccountWithRefreshToken(account_id);
-  const bool mtls_token_binding = token_fetcher->mtls_token_binding();
-
-  identity_manager_->GetAccountsMutator()->AddOrUpdateAccount(
-      gaia_id, email, refresh_token, is_under_advanced_protection,
-      token_fetcher->delegate()->GetAccessPoint(),
-      signin_metrics::SourceForRefreshTokenOperation::
-          kDiceResponseHandler_Signin,
-      signin::TokenBindingInfo(wrapped_binding_key, mtls_token_binding));
-
-  about_signin_internals_->OnRefreshTokenReceived(
-      base::StringPrintf("Successful (%s)", account_id.ToString().c_str()));
-  token_fetcher->delegate()->HandleTokenExchangeSuccess(account_id,
-                                                        is_new_account);
-  if (should_enable_sync) {
-    token_fetcher->delegate()->CompleteChromeSignInAfterGaiaSignin(
-        identity_manager_->FindExtendedAccountInfoByAccountId(account_id));
-  }
-
-  DeleteTokenFetcher(token_fetcher);
-}
-
-void DiceResponseHandler::OnTokenExchangeFailure(
-    DiceTokenFetcher* token_fetcher,
-    const GoogleServiceAuthError& error) {
-  const std::string& email = token_fetcher->email();
-  const GaiaId& gaia_id = token_fetcher->gaia_id();
-  CoreAccountId account_id =
-      identity_manager_->PickAccountIdForAccount(gaia_id, email);
-  about_signin_internals_->OnRefreshTokenReceived(
-      base::StringPrintf("Failure (%s)", account_id.ToString().c_str()));
-  token_fetcher->delegate()->HandleTokenExchangeFailure(email, error);
-
-  DeleteTokenFetcher(token_fetcher);
-}
-
 base::expected<raw_ref<BindingKeyRegistrationTokenHelper>,
                DiceResponseHandler::TokenBindingOutcome>
 DiceResponseHandler::MaybeGetBindingRegistrationTokenHelper(
@@ -573,5 +642,6 @@
   // list may mismatch `supported_algorithms`. We ignore this because it's more
   // important to reuse the same key.
   CHECK(registration_token_helper_);
-  return raw_ref<BindingKeyRegistrationTokenHelper>(*registration_token_helper_);
+  return raw_ref<BindingKeyRegistrationTokenHelper>(
+      *registration_token_helper_);
 }
diff --git a/chrome/browser/signin/dice_response_handler.h b/chrome/browser/signin/dice_response_handler.h
index 93518db..b979573 100644
--- a/chrome/browser/signin/dice_response_handler.h
+++ b/chrome/browser/signin/dice_response_handler.h
@@ -126,7 +126,7 @@
   ~DiceResponseHandler() override;
 
   // Must be called when receiving a Dice response header.
-  void ProcessDiceHeader(const signin::DiceResponseParams& dice_params,
+  void ProcessDiceHeader(signin::DiceResponseParams dice_params,
                          std::unique_ptr<ProcessDiceHeaderDelegate> delegate);
 
   // Returns the number of pending DiceTokenFetchers. Exposed for testing.
@@ -140,6 +140,8 @@
       RegistrationTokenHelperFactory factory);
 
  private:
+  class DiceSigninSession;
+
   // Helper class to fetch a refresh token from an authorization code.
   class DiceTokenFetcher : public GaiaAuthConsumer {
    public:
@@ -150,10 +152,9 @@
         bool mtls_token_binding,
         SigninClient* signin_client,
         AccountReconcilor* account_reconcilor,
-        std::unique_ptr<ProcessDiceHeaderDelegate> delegate,
         base::expected<raw_ref<BindingKeyRegistrationTokenHelper>,
                        TokenBindingOutcome> registration_token_helper_or_error,
-        DiceResponseHandler* dice_response_handler);
+        DiceSigninSession* session);
 
     DiceTokenFetcher(const DiceTokenFetcher&) = delete;
     DiceTokenFetcher& operator=(const DiceTokenFetcher&) = delete;
@@ -170,7 +171,6 @@
       should_enable_sync_ = should_enable_sync;
     }
     bool mtls_token_binding() const { return mtls_token_binding_; }
-    ProcessDiceHeaderDelegate* delegate() { return delegate_.get(); }
 
    private:
     // Called by |timeout_closure_| when the request times out.
@@ -195,8 +195,7 @@
     const std::string email_;
     const std::string authorization_code_;
     const bool mtls_token_binding_ = false;
-    const std::unique_ptr<ProcessDiceHeaderDelegate> delegate_;
-    const raw_ptr<DiceResponseHandler> dice_response_handler_ = nullptr;
+    const raw_ptr<DiceSigninSession> session_ = nullptr;
     const raw_ptr<SigninClient> signin_client_ = nullptr;
     base::CancelableOnceClosure timeout_closure_;
     bool should_enable_sync_ = false;
@@ -208,17 +207,60 @@
     std::vector<uint8_t> wrapped_binding_key_;
   };
 
-  // Deletes the token fetcher.
-  void DeleteTokenFetcher(DiceTokenFetcher* token_fetcher);
+  // Manages a session of concurrent token fetches for a single Dice header.
+  class DiceSigninSession {
+   public:
+    DiceSigninSession(DiceResponseHandler* handler,
+                      std::unique_ptr<ProcessDiceHeaderDelegate> delegate,
+                      signin::DiceResponseParams::SigninInfo signin_info);
+    ~DiceSigninSession();
+
+    // Starts fetching tokens for accounts.
+    void StartTokenFetch();
+
+    // Called by DiceTokenFetcher on success.
+    void OnTokenExchangeSuccess(
+        DiceTokenFetcher* fetcher,
+        const std::string& refresh_token,
+        bool is_under_advanced_protection,
+        const std::vector<uint8_t>& wrapped_binding_key);
+    // Called by DiceTokenFetcher on failure.
+    void OnTokenExchangeFailure(DiceTokenFetcher* fetcher,
+                                const GoogleServiceAuthError& error);
+
+    ProcessDiceHeaderDelegate* delegate() { return delegate_.get(); }
+
+    // Exposed for testing.
+    size_t GetPendingDiceTokenFetchersCountForTesting() const {
+      return token_fetcher_ ? 1 : 0;
+    }
+
+    bool IsFetchingForAccount(const CoreAccountId& account_id) const;
+
+    // Note: This might lead to the session being deleted synchronously inside
+    // handler_->DeleteSession(this).
+    bool CancelFetchForAccount(const CoreAccountId& account_id);
+
+    bool MarkEnableSyncIfFetching(const GaiaId& gaia_id,
+                                  const std::string& email);
+
+    bool IsFetchingFor(const GaiaId& gaia_id,
+                       const std::string& email,
+                       const std::string& authorization_code) const;
+
+   private:
+    const raw_ptr<DiceResponseHandler> handler_;
+    std::unique_ptr<ProcessDiceHeaderDelegate> delegate_;
+    signin::DiceResponseParams::SigninInfo signin_info_;
+    std::unique_ptr<DiceTokenFetcher> token_fetcher_;
+  };
+
+  // Deletes the session.
+  void DeleteSession(DiceSigninSession* session);
 
   // Process the Dice signin action.
   void ProcessDiceSigninHeader(
-      const GaiaId& gaia_id,
-      const std::string& email,
-      const std::string& authorization_code,
-      bool no_authorization_code,
-      const std::string& supported_algorithms_for_token_binding,
-      bool mtls_token_binding,
+      signin::DiceResponseParams::SigninInfo signin_info,
       std::unique_ptr<ProcessDiceHeaderDelegate> delegate);
 
   // Process the Dice enable sync action.
@@ -232,14 +274,6 @@
       const std::vector<signin::DiceResponseParams::AccountInfo>&
           account_infos);
 
-  // Called after exchanging an OAuth 2.0 authorization code for a refresh token
-  // after DiceAction::SIGNIN.
-  void OnTokenExchangeSuccess(DiceTokenFetcher* token_fetcher,
-                              const std::string& refresh_token,
-                              bool is_under_advanced_protection,
-                              const std::vector<uint8_t>& wrapped_binding_key);
-  void OnTokenExchangeFailure(DiceTokenFetcher* token_fetcher,
-                              const GoogleServiceAuthError& error);
   // Called to unlock the reconcilor after a SLO outage.
   void OnTimeoutUnlockReconcilor();
 
@@ -261,7 +295,7 @@
   // Must be cleaned up as soon as `token_fetchers_` becomes empty.
   std::unique_ptr<BindingKeyRegistrationTokenHelper>
       registration_token_helper_;
-  std::vector<std::unique_ptr<DiceTokenFetcher>> token_fetchers_;
+  std::vector<std::unique_ptr<DiceSigninSession>> sessions_;
   // Lock the account reconcilor for kLockAccountReconcilorTimeoutHours
   // when there was OAuth outage in Dice.
   std::unique_ptr<AccountReconcilor::Lock> lock_;
diff --git a/chrome/browser/signin/dice_response_handler_unittest.cc b/chrome/browser/signin/dice_response_handler_unittest.cc
index 2f57053..4e2cad7 100644
--- a/chrome/browser/signin/dice_response_handler_unittest.cc
+++ b/chrome/browser/signin/dice_response_handler_unittest.cc
@@ -245,7 +245,7 @@
   }
 
   void RunSignoutTest(
-      const DiceResponseParams& dice_params,
+      DiceResponseParams dice_params,
       const std::vector<CoreAccountId>& secondary_with_valid_refresh_tokens,
       const CoreAccountId& primary_account,
       bool invalid_primary_account);
@@ -358,12 +358,13 @@
 };
 
 void DiceResponseHandlerTest::RunSignoutTest(
-    const DiceResponseParams& dice_params,
+    DiceResponseParams dice_params,
     const std::vector<CoreAccountId>& secondary_with_valid_refresh_tokens,
     const CoreAccountId& primary_account,
     bool invalid_primary_account) {
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
 
   // Only the token corresponding the the Dice parameter has been removed, and
   // the user is still signed in.
@@ -413,12 +414,12 @@
   DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN);
   const auto* account = dice_params.signin_info()->GetInitiator();
   ASSERT_TRUE(account);
-  const auto& account_info = account->account_info;
   CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
-      account_info.gaia_id, account_info.email);
+      account->account_info.gaia_id, account->account_info.email);
   EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that a GaiaAuthFetcher has been created.
   GaiaAuthConsumer* consumer = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer, testing::NotNull());
@@ -474,7 +475,8 @@
 
   // Check that a GaiaAuthFetcher has been created.
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
 
   GaiaAuthConsumer* consumer = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer, testing::NotNull());
@@ -500,15 +502,15 @@
   DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN);
   const auto* account = dice_params.signin_info()->GetInitiator();
   ASSERT_TRUE(account);
-  const auto& account_info = account->account_info;
   CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
-      account_info.gaia_id, account_info.email);
+      account->account_info.gaia_id, account->account_info.email);
   EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id));
   const std::string authorization_code = account->authorization_code;
   ExpectRegistrationTokenHelperCreated({authorization_code},
                                        base::ToVector(kAcceptableAlgorithms));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
 
   // Token fetch should be blocked on the binding registration token generation.
   ASSERT_THAT(signin_client_.GetAndClearConsumer(), testing::IsNull());
@@ -546,13 +548,14 @@
       &dice_params.data.emplace<DiceResponseParams::SigninInfo>();
   signin_info->AddAccount({GetDiceResponseParamsAccountInfo(kEmail),
                            kAuthorizationCode, false, std::string(), false});
-  const auto& account_info = signin_info->GetInitiator()->account_info;
   CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
-      account_info.gaia_id, account_info.email);
+      signin_info->GetInitiator()->account_info.gaia_id,
+      signin_info->GetInitiator()->account_info.email);
   EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id));
 
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that a GaiaAuthFetcher has been created immediately.
   GaiaAuthConsumer* consumer = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer, testing::NotNull());
@@ -577,14 +580,14 @@
   EnableRegistrationTokenHelperFactory();
   identity_test_env_.ResetToAccountsNotYetLoadedFromDiskState();
   DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN);
-  const auto& account_info =
-      dice_params.signin_info()->GetInitiator()->account_info;
   CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
-      account_info.gaia_id, account_info.email);
+      dice_params.signin_info()->GetInitiator()->account_info.gaia_id,
+      dice_params.signin_info()->GetInitiator()->account_info.email);
   EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id));
 
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that a GaiaAuthFetcher has been created immediately.
   GaiaAuthConsumer* consumer = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer, testing::NotNull());
@@ -611,15 +614,15 @@
   DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN);
   const auto* account = dice_params.signin_info()->GetInitiator();
   ASSERT_TRUE(account);
-  const auto& account_info = account->account_info;
   CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
-      account_info.gaia_id, account_info.email);
+      account->account_info.gaia_id, account->account_info.email);
   EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id));
   const std::string authorization_code = account->authorization_code;
   ExpectRegistrationTokenHelperCreated({authorization_code},
                                        base::ToVector(kAcceptableAlgorithms));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
 
   // Token fetch should be blocked on the binding registration token generation.
   ASSERT_THAT(signin_client_.GetAndClearConsumer(), testing::IsNull());
@@ -660,13 +663,13 @@
   DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN);
   const auto* account = dice_params.signin_info()->GetInitiator();
   ASSERT_TRUE(account);
-  const auto& account_info = account->account_info;
   CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
-      account_info.gaia_id, account_info.email);
+      account->account_info.gaia_id, account->account_info.email);
   const std::string authorization_code = account->authorization_code;
   ExpectRegistrationTokenHelperCreated({authorization_code}, kWrappedKey);
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
 
   // Verify that the next step can complete with the reused token.
   // Token fetch should be blocked on the binding registration token generation.
@@ -703,7 +706,8 @@
       {dice_params.signin_info()->GetInitiator()->authorization_code},
       kWrappedKey);
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
 }
 
 TEST_F(DiceResponseHandlerTest, NewBindingKeyOtherTokenIsNotBound) {
@@ -715,16 +719,16 @@
       {dice_params.signin_info()->GetInitiator()->authorization_code},
       base::ToVector(kAcceptableAlgorithms));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
 }
 
 TEST_F(DiceResponseHandlerTest, TwoFetchersReuseRegistrationTokenHelper) {
   EnableRegistrationTokenHelperFactory();
   auto account_id = [&](const DiceResponseParams& dice_params) {
-    const auto& account_info =
-        dice_params.signin_info()->GetInitiator()->account_info;
-    return identity_manager()->PickAccountIdForAccount(account_info.gaia_id,
-                                                       account_info.email);
+    const auto* initiator = dice_params.signin_info()->GetInitiator();
+    return identity_manager()->PickAccountIdForAccount(
+        initiator->account_info.gaia_id, initiator->account_info.email);
   };
   auto authorization_code = [&](const DiceResponseParams& dice_params) {
     return dice_params.signin_info()->GetInitiator()->authorization_code;
@@ -737,13 +741,19 @@
   signin_info_2->AddAccount(
       {GetDiceResponseParamsAccountInfo("other@email.com"),
        "other_authorization_code", false, kEligibleForTokenBinding, false});
-  ExpectRegistrationTokenHelperCreated(
-      {authorization_code(dice_params_1), authorization_code(dice_params_2)},
-      base::ToVector(kAcceptableAlgorithms));
+  std::string code_1 = authorization_code(dice_params_1);
+  std::string code_2 = authorization_code(dice_params_2);
+  CoreAccountId id_1 = account_id(dice_params_1);
+  CoreAccountId id_2 = account_id(dice_params_2);
+
+  ExpectRegistrationTokenHelperCreated({code_1, code_2},
+                                       base::ToVector(kAcceptableAlgorithms));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params_1, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params_1),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params_2, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params_2),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
 
   // Token fetch should be blocked on the binding registration token generation.
   ASSERT_THAT(signin_client_.GetAndClearConsumer(), testing::IsNull());
@@ -751,17 +761,15 @@
   // been created.
   const std::vector<uint8_t> kWrappedKey = {1, 2, 3};
   SimulateRegistrationTokenHelperResult(
-      authorization_code(dice_params_2),
-      BindingKeyRegistrationTokenHelper::Result(
-          unexportable_keys::UnexportableKeyId(), kWrappedKey,
-          "test_registration_token"));
+      code_2, BindingKeyRegistrationTokenHelper::Result(
+                  unexportable_keys::UnexportableKeyId(), kWrappedKey,
+                  "test_registration_token"));
   GaiaAuthConsumer* consumer_2 = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer_2, testing::NotNull());
   SimulateRegistrationTokenHelperResult(
-      authorization_code(dice_params_1),
-      BindingKeyRegistrationTokenHelper::Result(
-          unexportable_keys::UnexportableKeyId(), kWrappedKey,
-          "other_registration_token"));
+      code_1, BindingKeyRegistrationTokenHelper::Result(
+                  unexportable_keys::UnexportableKeyId(), kWrappedKey,
+                  "other_registration_token"));
   GaiaAuthConsumer* consumer_1 = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer_1, testing::NotNull());
 
@@ -770,13 +778,11 @@
   consumer_1->OnClientOAuthSuccess(GaiaAuthConsumer::ClientOAuthResult(
       "refresh_token", "access_token", 10, /*is_child_account=*/false,
       /*is_under_advanced_protection=*/false, /*is_bound_to_key=*/true));
-  EXPECT_TRUE(identity_manager()->HasAccountWithBoundRefreshToken(
-      account_id(dice_params_1)));
+  EXPECT_TRUE(identity_manager()->HasAccountWithBoundRefreshToken(id_1));
   consumer_2->OnClientOAuthSuccess(GaiaAuthConsumer::ClientOAuthResult(
       "refresh_token", "access_token", 10, /*is_child_account=*/false,
       /*is_under_advanced_protection=*/false, /*is_bound_to_key=*/true));
-  EXPECT_TRUE(identity_manager()->HasAccountWithBoundRefreshToken(
-      account_id(dice_params_2)));
+  EXPECT_TRUE(identity_manager()->HasAccountWithBoundRefreshToken(id_2));
   EXPECT_EQ(identity_manager()->GetWrappedBindingKey(), kWrappedKey);
   histogram_tester_.ExpectUniqueSample(
       kTokenBindingOutcomeHistogram,
@@ -797,18 +803,19 @@
   signin_info->AddAccount({GetDiceResponseParamsAccountInfo("other@email.com"),
                            "other_authorization_code", false, std::string(),
                            false});
-  ExpectRegistrationTokenHelperCreated(
-      {authorization_code(eligible_dice_params_)},
-      base::ToVector(kAcceptableAlgorithms));
+  std::string eligible_code = authorization_code(eligible_dice_params_);
+
+  ExpectRegistrationTokenHelperCreated({eligible_code},
+                                       base::ToVector(kAcceptableAlgorithms));
 
   dice_response_handler_->ProcessDiceHeader(
-      eligible_dice_params_,
+      std::move(eligible_dice_params_),
       std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Token fetch should be blocked on the binding registration token generation.
   ASSERT_THAT(signin_client_.GetAndClearConsumer(), testing::IsNull());
 
   dice_response_handler_->ProcessDiceHeader(
-      ineligible_dice_params,
+      std::move(ineligible_dice_params),
       std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Token fetch should start immediately for ineligible account.
   ASSERT_THAT(signin_client_.GetAndClearConsumer(), testing::NotNull());
@@ -817,10 +824,9 @@
   // been created.
   const std::vector<uint8_t> kWrappedKey = {1, 2, 3};
   SimulateRegistrationTokenHelperResult(
-      authorization_code(eligible_dice_params_),
-      BindingKeyRegistrationTokenHelper::Result(
-          unexportable_keys::UnexportableKeyId(), kWrappedKey,
-          "test_registration_token"));
+      eligible_code, BindingKeyRegistrationTokenHelper::Result(
+                         unexportable_keys::UnexportableKeyId(), kWrappedKey,
+                         "test_registration_token"));
   ASSERT_THAT(signin_client_.GetAndClearConsumer(), testing::NotNull());
 }
 
@@ -828,29 +834,31 @@
        NewRegistrationTokenHelperCreatedForConsecutiveFetchers) {
   EnableRegistrationTokenHelperFactory();
   auto account_id = [&](const DiceResponseParams& dice_params) {
-    const auto& account_info =
-        dice_params.signin_info()->GetInitiator()->account_info;
-    return identity_manager()->PickAccountIdForAccount(account_info.gaia_id,
-                                                       account_info.email);
+    const auto* initiator = dice_params.signin_info()->GetInitiator();
+    return identity_manager()->PickAccountIdForAccount(
+        initiator->account_info.gaia_id, initiator->account_info.email);
   };
   auto authorization_code = [&](const DiceResponseParams& dice_params) {
     return dice_params.signin_info()->GetInitiator()->authorization_code;
   };
 
   DiceResponseParams dice_params_1 = MakeDiceParams(DiceAction::SIGNIN);
-  ExpectRegistrationTokenHelperCreated({authorization_code(dice_params_1)},
+  std::string code_1 = authorization_code(dice_params_1);
+  CoreAccountId id_1 = account_id(dice_params_1);
+
+  ExpectRegistrationTokenHelperCreated({code_1},
                                        base::ToVector(kAcceptableAlgorithms));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params_1, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params_1),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
 
   // Token fetch should be blocked on the binding registration token generation.
   ASSERT_THAT(signin_client_.GetAndClearConsumer(), testing::IsNull());
   const std::vector<uint8_t> kWrappedKey = {1, 2, 3};
   SimulateRegistrationTokenHelperResult(
-      authorization_code(dice_params_1),
-      BindingKeyRegistrationTokenHelper::Result(
-          unexportable_keys::UnexportableKeyId(), kWrappedKey,
-          "test_registration_token"));
+      code_1, BindingKeyRegistrationTokenHelper::Result(
+                  unexportable_keys::UnexportableKeyId(), kWrappedKey,
+                  "test_registration_token"));
   GaiaAuthConsumer* consumer_1 = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer_1, testing::NotNull());
 
@@ -858,10 +866,8 @@
   consumer_1->OnClientOAuthSuccess(GaiaAuthConsumer::ClientOAuthResult(
       "refresh_token", "access_token", 10, /*is_child_account=*/false,
       /*is_under_advanced_protection=*/false, /*is_bound_to_key=*/false));
-  EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken(
-      account_id(dice_params_1)));
-  EXPECT_FALSE(identity_manager()->HasAccountWithBoundRefreshToken(
-      account_id(dice_params_1)));
+  EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken(id_1));
+  EXPECT_FALSE(identity_manager()->HasAccountWithBoundRefreshToken(id_1));
 
   // Next request should create a new RegistrationTokenHelper with a new binding
   // key as none of the existing tokens are bound.
@@ -874,7 +880,8 @@
   ExpectRegistrationTokenHelperCreated({authorization_code(dice_params_2)},
                                        base::ToVector(kAcceptableAlgorithms));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params_2, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params_2),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
 }
 
 TEST_F(DiceResponseHandlerTest, SigninWithFailedBoundTokenAttempt) {
@@ -883,15 +890,15 @@
   const auto* signin_info = dice_params.signin_info();
   const auto* account = signin_info->GetInitiator();
   ASSERT_TRUE(account);
-  const auto& account_info = account->account_info;
   CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
-      account_info.gaia_id, account_info.email);
+      account->account_info.gaia_id, account->account_info.email);
   EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id));
   const std::string authorization_code = account->authorization_code;
   ExpectRegistrationTokenHelperCreated({authorization_code},
                                        base::ToVector(kAcceptableAlgorithms));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
 
   // Token fetch should be blocked on the binding registration token generation.
   ASSERT_THAT(signin_client_.GetAndClearConsumer(), testing::IsNull());
@@ -926,7 +933,8 @@
   signin_info->AddAccount({GetDiceResponseParamsAccountInfo(kEmail),
                            std::string(), true, std::string(), false});
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that the reconcilor was blocked and not unblocked before timeout.
   EXPECT_EQ(1, reconcilor_blocked_count_);
   EXPECT_EQ(0, reconcilor_unblocked_count_);
@@ -948,7 +956,8 @@
   signin_info_1->AddAccount({GetDiceResponseParamsAccountInfo(kEmail),
                              std::string(), true, std::string(), false});
   dice_response_handler_->ProcessDiceHeader(
-      dice_params_1, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params_1),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that the reconcilor was blocked and not unblocked before timeout.
   EXPECT_EQ(1, reconcilor_blocked_count_);
   EXPECT_EQ(0, reconcilor_unblocked_count_);
@@ -962,7 +971,8 @@
   signin_info_2->AddAccount({GetDiceResponseParamsAccountInfo(kEmail),
                              std::string(), true, std::string(), false});
   dice_response_handler_->ProcessDiceHeader(
-      dice_params_2, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params_2),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   task_environment_.FastForwardBy(
       base::Hours((kLockAccountReconcilorTimeoutHours + 1) / 2 + 1));
   // Check that the reconcilor was not unblocked after the first timeout
@@ -986,16 +996,18 @@
   signin_info_1->AddAccount({GetDiceResponseParamsAccountInfo(kEmail),
                              std::string(), true, std::string(), false});
   dice_response_handler_->ProcessDiceHeader(
-      dice_params_1, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params_1),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Create params for the valid header with an authorization code.
   DiceResponseParams dice_params_2 = MakeDiceParams(DiceAction::SIGNIN);
-  const auto& account_info_2 =
+  const auto account_info_2 =
       dice_params_2.signin_info()->GetInitiator()->account_info;
   CoreAccountId account_id_2 = identity_manager()->PickAccountIdForAccount(
       account_info_2.gaia_id, account_info_2.email);
   EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id_2));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params_2, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params_2),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that the reconcilor was blocked and not unblocked before timeout.
   EXPECT_EQ(1, reconcilor_blocked_count_);
   EXPECT_EQ(0, reconcilor_unblocked_count_);
@@ -1052,7 +1064,8 @@
       identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState(
           account_id));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that a GaiaAuthFetcher has been created.
   GaiaAuthConsumer* consumer = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer, testing::NotNull());
@@ -1075,13 +1088,14 @@
 // Checks that a GaiaAuthFetcher failure is handled correctly.
 TEST_F(DiceResponseHandlerTest, SigninFailure) {
   DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN);
-  const auto& account_info =
-      dice_params.signin_info()->GetInitiator()->account_info;
+  std::string email =
+      dice_params.signin_info()->GetInitiator()->account_info.email;
   CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
-      account_info.gaia_id, account_info.email);
+      dice_params.signin_info()->GetInitiator()->account_info.gaia_id, email);
   EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that a GaiaAuthFetcher has been created.
   GaiaAuthConsumer* consumer = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer, testing::NotNull());
@@ -1094,7 +1108,7 @@
       0u, dice_response_handler_->GetPendingDiceTokenFetchersCountForTesting());
   // Check that the token has not been inserted in the token service.
   EXPECT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id));
-  EXPECT_EQ(account_info.email, auth_error_email_);
+  EXPECT_EQ(email, auth_error_email_);
   EXPECT_EQ(GoogleServiceAuthError::SERVICE_UNAVAILABLE, auth_error_.state());
 }
 
@@ -1102,19 +1116,21 @@
 // request is already in flight.
 TEST_F(DiceResponseHandlerTest, SigninRepeatedWithSameAccount) {
   DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN);
-  const auto& account_info =
-      dice_params.signin_info()->GetInitiator()->account_info;
   CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
-      account_info.gaia_id, account_info.email);
+      dice_params.signin_info()->GetInitiator()->account_info.gaia_id,
+      dice_params.signin_info()->GetInitiator()->account_info.email);
   ASSERT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that a GaiaAuthFetcher has been created.
   GaiaAuthConsumer* consumer_1 = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer_1, testing::NotNull());
   // Start a second request for the same account.
+  dice_params = MakeDiceParams(DiceAction::SIGNIN);
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that there is no new request.
   GaiaAuthConsumer* consumer_2 = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer_2, testing::IsNull());
@@ -1132,7 +1148,7 @@
 // Checks that two SIGNIN requests can happen concurrently.
 TEST_F(DiceResponseHandlerTest, SigninWithTwoAccounts) {
   DiceResponseParams dice_params_1 = MakeDiceParams(DiceAction::SIGNIN);
-  const auto& account_info_1 =
+  const auto account_info_1 =
       dice_params_1.signin_info()->GetInitiator()->account_info;
   DiceResponseParams dice_params_2;
   DiceResponseParams::SigninInfo* signin_info_2 =
@@ -1142,7 +1158,7 @@
                              false,
                              kEligibleForTokenBinding,
                              false});
-  const auto& account_info_2 = signin_info_2->GetInitiator()->account_info;
+  const auto account_info_2 = signin_info_2->GetInitiator()->account_info;
   CoreAccountId account_id_1 = identity_manager()->PickAccountIdForAccount(
       account_info_1.gaia_id, account_info_1.email);
   CoreAccountId account_id_2 = identity_manager()->PickAccountIdForAccount(
@@ -1151,7 +1167,8 @@
   ASSERT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id_2));
   // Start first request.
   dice_response_handler_->ProcessDiceHeader(
-      dice_params_1, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params_1),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that a GaiaAuthFetcher has been created.
   GaiaAuthConsumer* consumer_1 = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer_1, testing::NotNull());
@@ -1159,7 +1176,8 @@
   EXPECT_EQ(0, reconcilor_unblocked_count_);
   // Start second request.
   dice_response_handler_->ProcessDiceHeader(
-      dice_params_2, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params_2),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   GaiaAuthConsumer* consumer_2 = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer_2, testing::NotNull());
   // Simulate GaiaAuthFetcher success for the first request.
@@ -1190,13 +1208,15 @@
 TEST_F(DiceResponseHandlerTest,
        SigninEnableSyncDiceHeaderAfterRefreshTokenFetched) {
   DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN);
-  const auto& account_info =
-      dice_params.signin_info()->GetInitiator()->account_info;
-  CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
-      account_info.gaia_id, account_info.email);
+  auto gaia_id =
+      dice_params.signin_info()->GetInitiator()->account_info.gaia_id;
+  auto email = dice_params.signin_info()->GetInitiator()->account_info.email;
+  CoreAccountId account_id =
+      identity_manager()->PickAccountIdForAccount(gaia_id, email);
   ASSERT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that a GaiaAuthFetcher has been created.
   GaiaAuthConsumer* consumer = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer, testing::NotNull());
@@ -1217,8 +1237,8 @@
       MakeDiceParams(DiceAction::ENABLE_SYNC),
       std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that delegate was called to enable sync.
-  EXPECT_EQ(account_info.gaia_id, complete_profile_signin_account_info_.gaia);
-  EXPECT_EQ(account_info.email, complete_profile_signin_account_info_.email);
+  EXPECT_EQ(gaia_id, complete_profile_signin_account_info_.gaia);
+  EXPECT_EQ(email, complete_profile_signin_account_info_.email);
 }
 
 // Checks that a ENABLE_SYNC action received before the refresh token is added
@@ -1227,13 +1247,15 @@
 TEST_F(DiceResponseHandlerTest,
        SigninEnableSyncDiceHeaderBeforeRefreshTokenFetched) {
   DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN);
-  const auto& account_info =
-      dice_params.signin_info()->GetInitiator()->account_info;
-  CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
-      account_info.gaia_id, account_info.email);
+  auto gaia_id =
+      dice_params.signin_info()->GetInitiator()->account_info.gaia_id;
+  auto email = dice_params.signin_info()->GetInitiator()->account_info.email;
+  CoreAccountId account_id =
+      identity_manager()->PickAccountIdForAccount(gaia_id, email);
   ASSERT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that a GaiaAuthFetcher has been created.
   GaiaAuthConsumer* consumer = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer, testing::NotNull());
@@ -1255,8 +1277,8 @@
   EXPECT_EQ(token_exchange_account_id_, account_id);
   EXPECT_TRUE(token_exchange_is_new_account_);
   // Check that delegate was called to enable sync.
-  EXPECT_EQ(account_info.gaia_id, complete_profile_signin_account_info_.gaia);
-  EXPECT_EQ(account_info.email, complete_profile_signin_account_info_.email);
+  EXPECT_EQ(gaia_id, complete_profile_signin_account_info_.gaia);
+  EXPECT_EQ(email, complete_profile_signin_account_info_.email);
 }
 
 // Checks that a ENABLE_SYNC action is ignored when the account info is missing.
@@ -1287,13 +1309,13 @@
 
 TEST_F(DiceResponseHandlerTest, Timeout) {
   DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN);
-  const auto& account_info =
-      dice_params.signin_info()->GetInitiator()->account_info;
   CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
-      account_info.gaia_id, account_info.email);
+      dice_params.signin_info()->GetInitiator()->account_info.gaia_id,
+      dice_params.signin_info()->GetInitiator()->account_info.email);
   ASSERT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that a GaiaAuthFetcher has been created.
   GaiaAuthConsumer* consumer = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer, testing::NotNull());
@@ -1315,13 +1337,13 @@
 // the timeout expires. Tests the scenario from https://crbug.com/1290214
 TEST_F(DiceResponseHandlerTest, DeleteBeforeTimeout) {
   DiceResponseParams dice_params = MakeDiceParams(DiceAction::SIGNIN);
-  const auto& account_info =
-      dice_params.signin_info()->GetInitiator()->account_info;
   CoreAccountId account_id = identity_manager()->PickAccountIdForAccount(
-      account_info.gaia_id, account_info.email);
+      dice_params.signin_info()->GetInitiator()->account_info.gaia_id,
+      dice_params.signin_info()->GetInitiator()->account_info.email);
   ASSERT_FALSE(identity_manager()->HasAccountWithRefreshToken(account_id));
   dice_response_handler_->ProcessDiceHeader(
-      dice_params, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that a GaiaAuthFetcher has been created.
   GaiaAuthConsumer* consumer = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer, testing::NotNull());
@@ -1363,7 +1385,7 @@
       identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSignin));
 
   // Receive signout response including primary and secondary account.
-  RunSignoutTest(dice_params, {secondary_not_signed_out.account_id},
+  RunSignoutTest(std::move(dice_params), {secondary_not_signed_out.account_id},
                  primary_account.account_id,
                  /*invalid_primary_account=*/true);
 }
@@ -1387,7 +1409,7 @@
   EXPECT_TRUE(
       identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSignin));
   // Receive signout response for the secondary account.
-  RunSignoutTest(dice_params, {}, primary_account_info.account_id,
+  RunSignoutTest(std::move(dice_params), {}, primary_account_info.account_id,
                  /*invalid_primary_account=*/false);
 }
 
@@ -1407,7 +1429,7 @@
   EXPECT_FALSE(
       identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSignin));
   // Receive signout response.
-  RunSignoutTest(dice_params, {secondary_account_info.account_id},
+  RunSignoutTest(std::move(dice_params), {secondary_account_info.account_id},
                  /*primary_account=*/CoreAccountId(),
                  /*invalid_primary_account=*/false);
 }
@@ -1428,13 +1450,14 @@
   // Start Dice signin (reauth).
   DiceResponseParams dice_params_2 = MakeDiceParams(DiceAction::SIGNIN);
   dice_response_handler_->ProcessDiceHeader(
-      dice_params_2, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(dice_params_2),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that a GaiaAuthFetcher has been created and is pending.
   ASSERT_THAT(signin_client_.GetAndClearConsumer(), testing::NotNull());
   EXPECT_EQ(
       1u, dice_response_handler_->GetPendingDiceTokenFetchersCountForTesting());
   // Signout while signin is in flight.
-  RunSignoutTest(dice_params, {}, account_info.account_id,
+  RunSignoutTest(std::move(dice_params), {}, account_info.account_id,
                  /*invalid_primary_account=*/true);
   // Check that the token fetcher has been canceled and the token is invalid.
   EXPECT_EQ(
@@ -1464,12 +1487,14 @@
   CoreAccountId account_id_2 = identity_manager()->PickAccountIdForAccount(
       signin_account_info_2.gaia_id, signin_account_info_2.email);
   dice_response_handler_->ProcessDiceHeader(
-      signin_params_1, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(signin_params_1),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
 
   GaiaAuthConsumer* consumer_1 = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer_1, testing::NotNull());
   dice_response_handler_->ProcessDiceHeader(
-      signin_params_2, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(signin_params_2),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   GaiaAuthConsumer* consumer_2 = signin_client_.GetAndClearConsumer();
   ASSERT_THAT(consumer_2, testing::NotNull());
   EXPECT_EQ(
@@ -1484,7 +1509,8 @@
           account_id_2));
   // Signout from one of the accounts while signin is in flight.
   dice_response_handler_->ProcessDiceHeader(
-      signout_params_1, std::make_unique<TestProcessDiceHeaderDelegate>(this));
+      std::move(signout_params_1),
+      std::make_unique<TestProcessDiceHeaderDelegate>(this));
   // Check that one of the fetchers is cancelled.
   EXPECT_EQ(
       1u, dice_response_handler_->GetPendingDiceTokenFetchersCountForTesting());
@@ -1521,7 +1547,7 @@
   EXPECT_TRUE(
       identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSignin));
   // Receive signout response.
-  RunSignoutTest(dice_params, {}, primary_account.account_id,
+  RunSignoutTest(std::move(dice_params), {}, primary_account.account_id,
                  /*invalid_primary_account=*/true);
 
   // Check that the reconcilor was not blocked.
diff --git a/chrome/browser/signin/dice_tab_helper.cc b/chrome/browser/signin/dice_tab_helper.cc
index e498c7af..2a8970d4 100644
--- a/chrome/browser/signin/dice_tab_helper.cc
+++ b/chrome/browser/signin/dice_tab_helper.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/ui/browser_finder.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/public/profile_browser_collection.h"
 #include "chrome/browser/ui/signin/signin_view_controller.h"
 #include "chrome/browser/ui/webui/signin/history_sync_optin_helper.h"
 #include "chrome/browser/ui/webui/signin/history_sync_optin_service.h"
@@ -67,7 +68,8 @@
     DCHECK(profile);
     BrowserWindowInterface* browser =
         web_contents ? chrome::FindBrowserWithTab(web_contents)
-                     : chrome::FindBrowserWithProfile(profile);
+                     : ProfileBrowserCollection::GetForProfile(profile)
+                           ->GetLastActiveBrowser();
     if (!browser) {
       return;
     }
@@ -101,7 +103,8 @@
 
     BrowserWindowInterface* browser =
         web_contents ? chrome::FindBrowserWithTab(web_contents)
-                     : chrome::FindBrowserWithProfile(profile);
+                     : ProfileBrowserCollection::GetForProfile(profile)
+                           ->GetLastActiveBrowser();
     if (!browser) {
       return;
     }
@@ -139,7 +142,8 @@
     }
     BrowserWindowInterface* browser =
         web_contents ? chrome::FindBrowserWithTab(web_contents)
-                     : chrome::FindBrowserWithProfile(profile);
+                     : ProfileBrowserCollection::GetForProfile(profile)
+                           ->GetLastActiveBrowser();
     if (!browser) {
       return;
     }
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.cc b/chrome/browser/signin/dice_web_signin_interceptor.cc
index b918d2e..4953dc9 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor.cc
@@ -53,8 +53,8 @@
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/hats/survey_config.h"
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
 #include "chrome/browser/ui/profiles/profile_colors_util.h"
@@ -1478,7 +1478,8 @@
     return;
   }
 
-  BrowserWindowInterface* browser = chrome::FindBrowserWithProfile(profile_);
+  BrowserWindowInterface* browser =
+      ProfileBrowserCollection::GetForProfile(profile_)->GetLastActiveBrowser();
   DCHECK(browser);
   delegate_->ShowFirstRunExperienceInNewProfile(
       browser->GetBrowserForMigrationOnly(), state_->account_id_,
diff --git a/chrome/browser/signin/e2e_tests/live_sign_in_test.cc b/chrome/browser/signin/e2e_tests/live_sign_in_test.cc
index 1cc8a7b..8be7126 100644
--- a/chrome/browser/signin/e2e_tests/live_sign_in_test.cc
+++ b/chrome/browser/signin/e2e_tests/live_sign_in_test.cc
@@ -7,6 +7,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/task/current_thread.h"
 #include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/test_future.h"
 #include "base/test/with_feature_override.h"
 #include "build/build_config.h"
@@ -44,6 +45,7 @@
 #include "components/signin/public/identity_manager/identity_test_utils.h"
 #include "components/signin/public/identity_manager/test_accounts.h"
 #include "components/signin/public/identity_manager/tribool.h"
+#include "components/sync/base/features.h"
 #include "components/sync/service/sync_service.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
@@ -65,11 +67,12 @@
 
 // Live tests for SignIn.
 // These tests can be run with:
-// browser_tests --gtest_filter=LiveSignInTest.* --run-live-tests --run-manual
-class LiveSignInTest : public signin::test::LiveTest {
+// browser_tests --gtest_filter=LiveSignInTest*.* --run-live-tests --run-manual
+
+class LiveSignInTestBase : public signin::test::LiveTest {
  public:
-  LiveSignInTest() = default;
-  ~LiveSignInTest() override = default;
+  LiveSignInTestBase() = default;
+  ~LiveSignInTestBase() override = default;
 
   void SetUp() override {
     LiveTest::SetUp();
@@ -98,12 +101,34 @@
       }));
 };
 
+class LiveSignInTest : public base::test::WithFeatureOverride,
+                       public LiveSignInTestBase {
+ public:
+  LiveSignInTest()
+      : base::test::WithFeatureOverride(
+            syncer::kReplaceSyncPromosWithSignInPromos) {}
+};
+
+// TODO(crbug.com/40066949): Simplify once kSync becomes unreachable or is
+// deleted from the codebase. See ConsentLevel::kSync documentation for
+// details.
+class LiveSignInTestFullSync : public LiveSignInTestBase {
+ public:
+  LiveSignInTestFullSync() {
+    feature_list_.InitAndDisableFeature(
+        syncer::kReplaceSyncPromosWithSignInPromos);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 // This test can pass. Marked as manual because it TIMED_OUT on Win7.
 // See crbug.com/1025335.
 // Sings in an account through the settings page and checks that the account is
 // added to Chrome. Sync should be disabled because the test doesn't pass
 // through the Sync confirmation dialog.
-IN_PROC_BROWSER_TEST_F(LiveSignInTest, MANUAL_SimpleSignInFlow) {
+IN_PROC_BROWSER_TEST_P(LiveSignInTest, MANUAL_SimpleSignInFlow) {
   std::optional<TestAccountSigninCredentials> test_account =
       GetTestAccounts()->GetAccount("TEST_ACCOUNT_1");
   CHECK(test_account.has_value());
@@ -129,7 +154,7 @@
 // Sync is enabled.
 // Then, signs out on the web and checks that the account is removed from
 // cookies and Sync paused error is displayed.
-IN_PROC_BROWSER_TEST_F(LiveSignInTest, MANUAL_WebSignOut) {
+IN_PROC_BROWSER_TEST_F(LiveSignInTestFullSync, MANUAL_WebSignOut) {
   std::optional<TestAccountSigninCredentials> test_account =
       GetTestAccounts()->GetAccount("TEST_ACCOUNT_1");
   CHECK(test_account.has_value());
@@ -166,7 +191,7 @@
 // Sings in two accounts on the web and checks that cookies and refresh tokens
 // are added to Chrome. Sync should be disabled.
 // Then, signs out on the web and checks that accounts are removed from Chrome.
-IN_PROC_BROWSER_TEST_F(LiveSignInTest, MANUAL_WebSignInAndSignOut) {
+IN_PROC_BROWSER_TEST_P(LiveSignInTest, MANUAL_WebSignInAndSignOut) {
   std::optional<TestAccountSigninCredentials> test_account =
       GetTestAccounts()->GetAccount("TEST_ACCOUNT_1");
   CHECK(test_account.has_value());
@@ -226,7 +251,7 @@
 // Sync is enabled. Signs in a second account on the web.
 // Then, turns Sync off from the settings page and checks that both accounts are
 // removed from Chrome and from cookies.
-IN_PROC_BROWSER_TEST_F(LiveSignInTest, MANUAL_TurnOffSync) {
+IN_PROC_BROWSER_TEST_F(LiveSignInTestFullSync, MANUAL_TurnOffSync) {
   std::optional<TestAccountSigninCredentials> test_account_1 =
       GetTestAccounts()->GetAccount("TEST_ACCOUNT_1");
   CHECK(test_account_1.has_value());
@@ -258,7 +283,7 @@
 // In "Sync paused" state, when the primary account is invalid, turns off sync
 // from settings. Checks that the account is removed from Chrome.
 // Regression test for https://crbug.com/1114646
-IN_PROC_BROWSER_TEST_F(LiveSignInTest, MANUAL_TurnOffSyncWhenPaused) {
+IN_PROC_BROWSER_TEST_F(LiveSignInTestFullSync, MANUAL_TurnOffSyncWhenPaused) {
   std::optional<TestAccountSigninCredentials> test_account =
       GetTestAccounts()->GetAccount("TEST_ACCOUNT_1");
   CHECK(test_account.has_value());
@@ -291,7 +316,8 @@
 // Signs in an account on the web. Goes to the Chrome settings to enable Sync
 // but cancels the sync confirmation dialog. Checks that the account is still
 // signed in on the web but Sync is disabled.
-IN_PROC_BROWSER_TEST_F(LiveSignInTest, MANUAL_CancelSyncWithWebAccount) {
+IN_PROC_BROWSER_TEST_F(LiveSignInTestFullSync,
+                       MANUAL_CancelSyncWithWebAccount) {
   std::optional<TestAccountSigninCredentials> test_account =
       GetTestAccounts()->GetAccount("TEST_ACCOUNT_1");
   CHECK(test_account.has_value());
@@ -332,14 +358,19 @@
 // Starts the sign in flow from the settings page, enters credentials on the
 // login page but cancels the Sync confirmation dialog. Checks that Sync is
 // disabled but the account is still signed in to Chrome.
-IN_PROC_BROWSER_TEST_F(LiveSignInTest, MANUAL_CancelSync) {
+IN_PROC_BROWSER_TEST_P(LiveSignInTest, MANUAL_CancelSync) {
   std::optional<TestAccountSigninCredentials> test_account =
       GetTestAccounts()->GetAccount("TEST_ACCOUNT_1");
   CHECK(test_account.has_value());
   sign_in_functions.SignInFromSettings(*test_account, 0);
 
-  EXPECT_TRUE(login_ui_test_utils::CancelSyncConfirmationDialog(
-      browser(), kDialogTimeout));
+  if (IsParamFeatureEnabled()) {
+    EXPECT_TRUE(login_ui_test_utils::RejectHistorySyncOptinDialog(
+        browser(), kDialogTimeout));
+  } else {
+    EXPECT_TRUE(login_ui_test_utils::CancelSyncConfirmationDialog(
+        browser(), kDialogTimeout));
+  }
   // The account is still signed in, but not syncing.
   EXPECT_FALSE(
       identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSync));
@@ -366,7 +397,7 @@
 // on "This wasn't me" in the email confirmation dialog. Checks that the new
 // profile is created. Checks that Sync to account 2 is enabled in the new
 // profile. Checks that account 2 was removed from the original profile.
-IN_PROC_BROWSER_TEST_F(LiveSignInTest,
+IN_PROC_BROWSER_TEST_F(LiveSignInTestFullSync,
                        MANUAL_SyncSecondAccount_CreateNewProfile) {
   // Enable and disable sync for the first account.
   std::optional<TestAccountSigninCredentials> test_account_1 =
@@ -449,7 +480,7 @@
 // Enables and disables sync to account 1. Enables sync to account 2 and clicks
 // on "This was me" in the email confirmation dialog. Checks that Sync to
 // account 2 is enabled in the current profile.
-IN_PROC_BROWSER_TEST_F(LiveSignInTest,
+IN_PROC_BROWSER_TEST_F(LiveSignInTestFullSync,
                        MANUAL_SyncSecondAccount_InExistingProfile) {
   // Enable and disable sync for the first account.
   std::optional<TestAccountSigninCredentials> test_account_1 =
@@ -508,7 +539,7 @@
 // Enables and disables sync to account 1. Enables sync to account 2 and clicks
 // on "Cancel" in the email confirmation dialog. Checks that the account is left
 // signed in without syncing.
-IN_PROC_BROWSER_TEST_F(LiveSignInTest,
+IN_PROC_BROWSER_TEST_F(LiveSignInTestFullSync,
                        MANUAL_SyncSecondAccount_CancelOnEmailConfirmation) {
   // Enable and disable sync for the first account.
   std::optional<TestAccountSigninCredentials> test_account_1 =
@@ -559,7 +590,7 @@
   EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
 }
 
-IN_PROC_BROWSER_TEST_F(LiveSignInTest,
+IN_PROC_BROWSER_TEST_P(LiveSignInTest,
                        MANUAL_AccountCapabilities_FetchedOnSignIn) {
   // Test primary adult account.
   {
@@ -613,7 +644,7 @@
 }
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
-IN_PROC_BROWSER_TEST_F(LiveSignInTest, MANUAL_CreateSignedInProfile) {
+IN_PROC_BROWSER_TEST_F(LiveSignInTestFullSync, MANUAL_CreateSignedInProfile) {
   base::HistogramTester histogram_tester;
   std::optional<TestAccountSigninCredentials> test_account =
       GetTestAccounts()->GetAccount("TEST_ACCOUNT_1");
@@ -681,7 +712,7 @@
 // Tests that a doing a web signin from a tab that was previously opened for
 // a browser signin, does not sign in the user in the browser.
 // TODO(crbug.com/467170772): Remove the logging once flakiness reason is identified.
-IN_PROC_BROWSER_TEST_F(LiveSignInGaiaIntegrationTest,
+IN_PROC_BROWSER_TEST_P(LiveSignInGaiaIntegrationTest,
                        MANUAL_WebSignInFromExistingChromeSignInTab) {
   base::HistogramTester histogram_tester;
   std::optional<TestAccountSigninCredentials> test_account =
@@ -739,5 +770,11 @@
 
 #endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
+INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(LiveSignInTest);
+
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(LiveSignInGaiaIntegrationTest);
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
+
 }  // namespace
 }  // namespace signin::test
diff --git a/chrome/browser/signin/signin_ui_util_browsertest.cc b/chrome/browser/signin/signin_ui_util_browsertest.cc
index 992444e..9387493 100644
--- a/chrome/browser/signin/signin_ui_util_browsertest.cc
+++ b/chrome/browser/signin/signin_ui_util_browsertest.cc
@@ -34,8 +34,8 @@
 #include "chrome/browser/signin/signin_util.h"
 #include "chrome/browser/sync/sync_service_factory.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/signin/promos/signin_promo_tab_helper.h"
 #include "chrome/browser/ui/tabs/tab_enums.h"
 #include "chrome/browser/ui/ui_features.h"
@@ -997,13 +997,15 @@
   Profile* new_profile = CreateProfile();
 
   // New profile should not have any browser windows.
-  EXPECT_FALSE(chrome::FindBrowserWithProfile(new_profile));
+  EXPECT_FALSE(ProfileBrowserCollection::GetForProfile(new_profile)
+                   ->GetLastActiveBrowser());
 
   ShowExtensionSigninPrompt(new_profile, /*enable_sync=*/false,
                             /*email_hint=*/std::string());
   // `ShowExtensionSigninPrompt()` creates a new browser.
   BrowserWindowInterface* browser =
-      chrome::FindBrowserWithProfile(new_profile);
+      ProfileBrowserCollection::GetForProfile(new_profile)
+          ->GetLastActiveBrowser();
   ASSERT_TRUE(browser);
   EXPECT_EQ(1, browser->GetTabStripModel()->count());
 
@@ -1018,12 +1020,14 @@
           new_profile->GetPath(), base::DoNothing(),
           ProfileMetrics::DELETE_PROFILE_USER_MANAGER);
   observer.Wait();
-  EXPECT_FALSE(chrome::FindBrowserWithProfile(new_profile));
+  EXPECT_FALSE(ProfileBrowserCollection::GetForProfile(new_profile)
+                   ->GetLastActiveBrowser());
 
   // `ShowExtensionSigninPrompt()` does nothing for deleted profile.
   ShowExtensionSigninPrompt(new_profile, /*enable_sync=*/false,
                             /*email_hint=*/std::string());
-  EXPECT_FALSE(chrome::FindBrowserWithProfile(new_profile));
+  EXPECT_FALSE(ProfileBrowserCollection::GetForProfile(new_profile)
+                   ->GetLastActiveBrowser());
 }
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
diff --git a/chrome/browser/signin/signin_util_unittest.cc b/chrome/browser/signin/signin_util_unittest.cc
index 2725c54..6b29e07 100644
--- a/chrome/browser/signin/signin_util_unittest.cc
+++ b/chrome/browser/signin/signin_util_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "base/test/scoped_feature_list.h"
+#include "base/test/with_feature_override.h"
 #include "build/buildflag.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/enterprise/util/managed_browser_utils.h"
@@ -23,6 +24,7 @@
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/base/signin_switches.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
+#include "components/sync/base/features.h"
 #include "components/sync/base/user_selectable_type.h"
 #include "components/sync/service/sync_prefs.h"
 #include "components/sync/test/test_sync_service.h"
@@ -711,7 +713,15 @@
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
-TEST(SignedInStatesTest, SignedInStates) {
+class SignedInStatesTest : public base::test::WithFeatureOverride,
+                           public ::testing::Test {
+ public:
+  SignedInStatesTest()
+      : base::test::WithFeatureOverride(
+            syncer::kReplaceSyncPromosWithSignInPromos) {}
+};
+
+TEST_P(SignedInStatesTest, SignedInStates) {
   base::test::SingleThreadTaskEnvironment task_environment;
   signin::IdentityTestEnvironment identity_test_env;
   signin::IdentityManager* identity_manager =
@@ -728,14 +738,19 @@
             signin_util::GetSignedInState(identity_manager));
 
   // Syncing.
+  const signin::ConsentLevel consent_level = IsParamFeatureEnabled()
+                                                 ? signin::ConsentLevel::kSignin
+                                                 : signin::ConsentLevel::kSync;
   AccountInfo info = identity_test_env.MakePrimaryAccountAvailable(
-      "test@email.com", signin::ConsentLevel::kSync);
-  EXPECT_EQ(SignedInState::kSyncing,
+      "test@email.com", consent_level);
+  EXPECT_EQ(IsParamFeatureEnabled() ? SignedInState::kSignedIn
+                                    : SignedInState::kSyncing,
             signin_util::GetSignedInState(identity_manager));
 
   // Sync paused state.
   identity_test_env.SetInvalidRefreshTokenForPrimaryAccount();
-  EXPECT_EQ(SignedInState::kSyncPaused,
+  EXPECT_EQ(IsParamFeatureEnabled() ? SignedInState::kSignInPending
+                                    : SignedInState::kSyncPaused,
             signin_util::GetSignedInState(identity_manager));
 
   // Remove account.
@@ -758,4 +773,6 @@
   EXPECT_EQ(SignedInState::kSignInPending,
             signin_util::GetSignedInState(identity_manager));
 }
+
+INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(SignedInStatesTest);
 #endif  // !BUILDFLAG(ENABLE_DICE_SUPPORT)
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 68143ab..0ba6b50 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -68,6 +68,7 @@
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/tab_ui_helper.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
@@ -2638,7 +2639,8 @@
 
   // Last activated browser should be the popup.
   BrowserWindowInterface* popup_browser =
-      chrome::FindBrowserWithProfile(browser()->profile());
+      ProfileBrowserCollection::GetForProfile(browser()->profile())
+          ->GetLastActiveBrowser();
   WebContents* popup =
       popup_browser->GetTabStripModel()->GetActiveWebContents();
   EXPECT_NE(popup, tab1);
diff --git a/chrome/browser/sync/test/integration/live_sync_signin_delegate_desktop.cc b/chrome/browser/sync/test/integration/live_sync_signin_delegate_desktop.cc
index 4c526b9d..f81654d 100644
--- a/chrome/browser/sync/test/integration/live_sync_signin_delegate_desktop.cc
+++ b/chrome/browser/sync/test/integration/live_sync_signin_delegate_desktop.cc
@@ -10,8 +10,8 @@
 #include "chrome/browser/profiles/profile_test_util.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
 #include "chrome/browser/ui/webui/signin/login_ui_test_utils.h"
@@ -50,7 +50,8 @@
                                          /*always_create=*/true);
 
   BrowserWindowInterface* browser =
-      chrome::FindBrowserWithProfile(profile_.get());
+      ProfileBrowserCollection::GetForProfile(profile_.get())
+          ->GetLastActiveBrowser();
   if (!browser) {
     LOG(ERROR) << "Failed to open browser to sign in.";
     return false;
@@ -66,8 +67,12 @@
 }
 
 bool LiveSyncSigninDelegateDesktop::ConfirmSync() {
+  BrowserWindowInterface* confirm_browser =
+      ProfileBrowserCollection::GetForProfile(profile_.get())
+          ->GetLastActiveBrowser();
   if (!login_ui_test_utils::ConfirmSyncConfirmationDialog(
-          chrome::FindBrowserWithProfile(profile_.get()))) {
+          confirm_browser ? confirm_browser->GetBrowserForMigrationOnly()
+                          : nullptr)) {
     LOG(ERROR) << "Failed to dismiss sync confirmation dialog.";
     return false;
   }
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninSurveyController.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninSurveyController.java
index 91d7db6..44680e4 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninSurveyController.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninSurveyController.java
@@ -117,6 +117,10 @@
      */
     public static void registerTrigger(
             @Nullable Profile profile, @SigninSurveyType int surveyType) {
+        // Do not register trigger for testing unless explicitly enabled.
+        if (BuildConfig.IS_FOR_TEST && !sEnableForTesting) {
+            return;
+        }
         if (profile == null || profile.isOffTheRecord()) {
             return;
         }
@@ -129,6 +133,14 @@
                 surveyType,
                 SigninSurveyType.MAX_VALUE);
         controller.mRegisteredTrigger = surveyType;
+
+        if (surveyType == SigninSurveyType.WEB
+                || surveyType == SigninSurveyType.NTP_SIGNIN_BUTTON
+                || surveyType == SigninSurveyType.NTP_PROMO) {
+            // These access points don't use a separate activity to sign-in and use
+            // ChromeTabbedActivity. So try to show the survey here.
+            controller.maybeShowSurvey();
+        }
     }
 
     // Enables triggering the survey in tests and a delay after which the survey will be shown.
@@ -174,7 +186,7 @@
     }
 
     private void maybeShowSurvey() {
-        if (mRegisteredTrigger == null || mAlreadyTriedShowing) {
+        if (mRegisteredTrigger == null || mActivityHolder == null || mAlreadyTriedShowing) {
             return;
         }
 
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/WebSigninAccountPickerDelegate.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/WebSigninAccountPickerDelegate.java
index 16825f81..75f53d6 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/WebSigninAccountPickerDelegate.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/WebSigninAccountPickerDelegate.java
@@ -18,6 +18,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.ui.signin.BottomSheetSigninAndHistorySyncCoordinator;
 import org.chromium.chrome.browser.ui.signin.DelegateContext;
+import org.chromium.chrome.browser.ui.signin.SigninSurveyController;
 import org.chromium.components.signin.base.CoreAccountInfo;
 import org.chromium.components.signin.browser.WebSigninTrackerResult;
 import org.chromium.components.signin.metrics.AccountConsistencyPromoAction;
@@ -174,6 +175,8 @@
                         // that the tab is still alive.
                         tab.loadUrl(new LoadUrlParams(continueUrl));
                     }
+                    SigninSurveyController.registerTrigger(
+                            mProfile, SigninSurveyController.SigninSurveyType.WEB);
                     break;
                 case WebSigninTrackerResult.AUTH_ERROR:
                     SigninMetricsUtils.logAccountConsistencyPromoAction(
diff --git a/chrome/browser/ui/browser_finder_unittest.cc b/chrome/browser/ui/browser_finder_unittest.cc
index 7ee1071e..ba5ba4e 100644
--- a/chrome/browser/ui/browser_finder_unittest.cc
+++ b/chrome/browser/ui/browser_finder_unittest.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/ui/browser_finder.h"
 
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 
@@ -12,7 +14,9 @@
 
 TEST_F(BrowserFinderTest, ScheduledForDeletion) {
   EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
-  EXPECT_EQ(browser(), chrome::FindBrowserWithProfile(profile()));
+  EXPECT_EQ(browser(), ProfileBrowserCollection::GetForProfile(profile())
+                           ->GetLastActiveBrowser()
+                           ->GetBrowserForMigrationOnly());
   // Add a tab as the tabstrip starts empty and CloseAllTabs() effectively
   // does nothing if there are no tabs (meaning Browser deletion isn't
   // scheduled).
@@ -25,5 +29,6 @@
   browser->OnWindowClosing();
   EXPECT_TRUE(browser->is_delete_scheduled());
   EXPECT_EQ(0u, chrome::GetTotalBrowserCount());
-  EXPECT_EQ(nullptr, chrome::FindBrowserWithProfile(profile()));
+  EXPECT_EQ(nullptr, ProfileBrowserCollection::GetForProfile(profile())
+                         ->GetLastActiveBrowser());
 }
diff --git a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
index 52528643..c53cb1c 100644
--- a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/webui_url_constants.h"
@@ -248,7 +249,8 @@
 
   ASSERT_EQ(1u, chrome::GetTotalBrowserCount());
   BrowserWindowInterface* browser =
-      chrome::FindBrowserWithProfile(primary_user_profile);
+      ProfileBrowserCollection::GetForProfile(primary_user_profile)
+          ->GetLastActiveBrowser();
   ASSERT_TRUE(browser);
 
   // Start multi-user sign-in.
diff --git a/chrome/browser/ui/browser_window/internal/browser_window_features.cc b/chrome/browser/ui/browser_window/internal/browser_window_features.cc
index 55f8ab6f..727eab0 100644
--- a/chrome/browser/ui/browser_window/internal/browser_window_features.cc
+++ b/chrome/browser/ui/browser_window/internal/browser_window_features.cc
@@ -26,7 +26,6 @@
 #include "chrome/browser/devtools/devtools_ui_controller.h"
 #include "chrome/browser/enterprise/data_protection/data_protection_ui_controller.h"
 #include "chrome/browser/extensions/browser_extension_window_controller.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/glic/browser_ui/glic_button_controller.h"
 #include "chrome/browser/glic/browser_ui/glic_iph_controller.h"
 #include "chrome/browser/glic/browser_ui/glic_nudge_controller.h"
@@ -176,6 +175,7 @@
 #include "components/search/ntp_features.h"
 #include "components/search/search.h"
 #include "content/public/common/content_constants.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/browser/mv2_experiment_stage.h"
 #include "extensions/common/extension_features.h"
 #include "ui/views/interaction/element_highlighter_views.h"
diff --git a/chrome/browser/ui/extensions/mv2_disabled_dialog_controller.cc b/chrome/browser/ui/extensions/mv2_disabled_dialog_controller.cc
index 82ef604..b26dc8c 100644
--- a/chrome/browser/ui/extensions/mv2_disabled_dialog_controller.cc
+++ b/chrome/browser/ui/extensions/mv2_disabled_dialog_controller.cc
@@ -10,7 +10,6 @@
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/chrome_pages.h"
@@ -21,6 +20,7 @@
 #include "extensions/browser/extension_registrar.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/image_loader.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/browser/mv2_experiment_stage.h"
 #include "extensions/browser/pref_types.h"
 #include "extensions/common/manifest_handlers/icons_handler.h"
diff --git a/chrome/browser/ui/passwords/password_change_ui_controller.cc b/chrome/browser/ui/passwords/password_change_ui_controller.cc
index 3b03e808..d44824c 100644
--- a/chrome/browser/ui/passwords/password_change_ui_controller.cc
+++ b/chrome/browser/ui/passwords/password_change_ui_controller.cc
@@ -410,7 +410,7 @@
   toast_view_ = toast_view.get();
 
   auto toast_delegate = std::make_unique<views::WidgetDelegate>();
-  toast_delegate->SetModalType(ui::mojom::ModalType::kChild);
+  toast_delegate->SetModalType(ui::mojom::ModalType::kNone);
   toast_delegate->SetContentsView(std::move(toast_view));
   toast_delegate->SetAccessibleWindowRole(ax::mojom::Role::kAlert);
   toast_delegate->SetAccessibleTitle(title);
diff --git a/chrome/browser/ui/passwords/password_change_ui_controller_browsertest.cc b/chrome/browser/ui/passwords/password_change_ui_controller_browsertest.cc
index fb67432..77aca2f 100644
--- a/chrome/browser/ui/passwords/password_change_ui_controller_browsertest.cc
+++ b/chrome/browser/ui/passwords/password_change_ui_controller_browsertest.cc
@@ -8,10 +8,15 @@
 #include "chrome/browser/password_manager/password_change_delegate_mock.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
 #include "components/tabs/public/tab_interface.h"
 #include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/keycodes/dom/dom_code.h"
+#include "ui/events/keycodes/dom/dom_key.h"
+#include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/events/test/test_event.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/test/button_test_api.h"
@@ -347,4 +352,38 @@
   EXPECT_TRUE(ui_controller()->dialog_widget());
 }
 
+IN_PROC_BROWSER_TEST_F(PasswordChangeUIControllerBrowserTest,
+                       PageIsInteractableWhileToastIsShowing) {
+  // Navigate to a simple page with an input field.
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), GURL("data:text/html,<input id='test_input'>")));
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // Focus the input field.
+  EXPECT_TRUE(content::ExecJs(
+      web_contents, "document.getElementById('test_input').focus();"));
+
+  // Show the toast.
+  UpdateState(PasswordChangeDelegate::State::kChangingPassword);
+
+  // Verify that the input field is still the active element.
+  EXPECT_EQ(
+      true,
+      content::EvalJs(
+          web_contents,
+          "document.activeElement === document.getElementById('test_input');"));
+
+  // Simulate typing 'a'.
+  content::SimulateKeyPress(web_contents, ui::DomKey::FromCharacter('a'),
+                            ui::DomCode::US_A, ui::VKEY_A, false, false, false,
+                            false);
+
+  // Verify the value.
+  EXPECT_EQ("a",
+            content::EvalJs(web_contents,
+                            "document.getElementById('test_input').value;"));
+}
+
 }  // namespace
diff --git a/chrome/browser/ui/sharing_hub/BUILD.gn b/chrome/browser/ui/sharing_hub/BUILD.gn
index 4d11153..49cf433 100644
--- a/chrome/browser/ui/sharing_hub/BUILD.gn
+++ b/chrome/browser/ui/sharing_hub/BUILD.gn
@@ -19,6 +19,7 @@
     "//chrome/browser/share",
     "//chrome/browser/sharing_hub",
     "//content/public/browser",
+    "//ui/base/interaction",
   ]
 
   if (is_chromeos) {
diff --git a/chrome/browser/ui/signin/dice_migration_service.cc b/chrome/browser/ui/signin/dice_migration_service.cc
index 1890da2..46f5b5803 100644
--- a/chrome/browser/ui/signin/dice_migration_service.cc
+++ b/chrome/browser/ui/signin/dice_migration_service.cc
@@ -11,9 +11,9 @@
 #include "chrome/browser/profiles/profile_window.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/toasts/api/toast_id.h"
 #include "chrome/browser/ui/toasts/toast_controller.h"
 #include "components/pref_registry/pref_registry_syncable.h"
@@ -130,7 +130,9 @@
 
   // Trigger the timer to show the toast.
   auto show_toast = [](Profile* profile) {
-    BrowserWindowInterface* browser = chrome::FindBrowserWithProfile(profile);
+    BrowserWindowInterface* browser =
+        ProfileBrowserCollection::GetForProfile(profile)
+            ->GetLastActiveBrowser();
     base::UmaHistogramBoolean(
         kForcedSigninBrowserInstanceAvailableAfterTimerHistogram, browser);
     if (browser) {
diff --git a/chrome/browser/ui/startup/profile_launch_observer.cc b/chrome/browser/ui/startup/profile_launch_observer.cc
index b8114aa..d40f95d6 100644
--- a/chrome/browser/ui/startup/profile_launch_observer.cc
+++ b/chrome/browser/ui/startup/profile_launch_observer.cc
@@ -9,12 +9,12 @@
 #include "chrome/browser/global_features.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/waap/initial_web_ui_manager.h"
 #include "content/public/browser/browser_thread.h"
+#include "ui/base/base_window.h"
 
 ProfileLaunchObserver::ProfileLaunchObserver() {
   browser_collection_observation_.Observe(
@@ -82,7 +82,8 @@
     observed_profiles_.AddObservation(profile);
   }
   launched_profiles_.insert(profile);
-  if (chrome::FindBrowserWithProfile(profile)) {
+  if (ProfileBrowserCollection::GetForProfile(profile)
+          ->GetLastActiveBrowser()) {
     // A browser may get opened before we get initialized (e.g., in tests),
     // so we never see the OnBrowserAdded() for it.
     opened_profiles_.insert(profile);
@@ -118,7 +119,8 @@
     if (opened_profiles_.find(*i) == opened_profiles_.end()) {
       return;
     }
-    BrowserWindowInterface* browser = chrome::FindBrowserWithProfile(*i);
+    BrowserWindowInterface* browser =
+        ProfileBrowserCollection::GetForProfile(*i)->GetLastActiveBrowser();
     // Defer the profile activation if the initial WebUI is pending.
     if (browser && InitialWebUIManager::From(browser) &&
         InitialWebUIManager::From(browser)->RequestDeferShow(
@@ -141,7 +143,8 @@
   // We need to test again, in case the profile got deleted in the mean time.
   if (profile_to_activate_) {
     BrowserWindowInterface* browser =
-        chrome::FindBrowserWithProfile(profile_to_activate_);
+        ProfileBrowserCollection::GetForProfile(profile_to_activate_)
+            ->GetLastActiveBrowser();
     // |profile| may never get launched, e.g., if it only had
     // incognito Windows and one of them was used to exit Chrome.
     // So it won't have a browser in that case.
diff --git a/chrome/browser/ui/startup/profile_launch_observer.h b/chrome/browser/ui/startup/profile_launch_observer.h
index 6316f89..ee1086ba 100644
--- a/chrome/browser/ui/startup/profile_launch_observer.h
+++ b/chrome/browser/ui/startup/profile_launch_observer.h
@@ -62,12 +62,12 @@
 
   // These are the profiles that get launched by
   // StartupBrowserCreator::LaunchBrowser.
-  std::set<raw_ptr<const Profile, SetExperimental>> launched_profiles_;
+  std::set<raw_ptr<Profile, SetExperimental>> launched_profiles_;
   // These are the profiles for which at least one browser window has been
   // opened. This is needed to know when it is safe to activate
   // |profile_to_activate_|, otherwise, new browser windows being opened will
   // be activated on top of it.
-  std::set<raw_ptr<const Profile, SetExperimental>> opened_profiles_;
+  std::set<raw_ptr<Profile, SetExperimental>> opened_profiles_;
   // This is null until the profile to activate has been chosen. This value
   // should only be set once all profiles have been launched, otherwise,
   // activation may not happen after the launch of newer profiles.
diff --git a/chrome/browser/ui/startup/startup_browser_creator_interactive_uitest.cc b/chrome/browser/ui/startup/startup_browser_creator_interactive_uitest.cc
index 9f4e421c..2e8e615 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_interactive_uitest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_interactive_uitest.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/chrome_test_utils.h"
@@ -87,7 +88,8 @@
 
   // The last used profile (the profile_2 in this case) must be active.
   ASSERT_EQ(1u, chrome::GetBrowserCount(&profile_2));
-  new_browser = chrome::FindBrowserWithProfile(&profile_2);
+  new_browser = ProfileBrowserCollection::GetForProfile(&profile_2)
+                    ->GetLastActiveBrowser();
   ASSERT_TRUE(new_browser);
   EXPECT_TRUE(new_browser->GetWindow()->IsVisible());
 
@@ -100,17 +102,20 @@
 
   // All other profiles browser should not be active.
   ASSERT_EQ(1u, chrome::GetBrowserCount(&profile_1));
-  new_browser = chrome::FindBrowserWithProfile(&profile_1);
+  new_browser = ProfileBrowserCollection::GetForProfile(&profile_1)
+                    ->GetLastActiveBrowser();
   ASSERT_TRUE(new_browser);
   EXPECT_FALSE(new_browser->GetWindow()->IsActive());
 
   ASSERT_EQ(1u, chrome::GetBrowserCount(&profile_3));
-  new_browser = chrome::FindBrowserWithProfile(&profile_3);
+  new_browser = ProfileBrowserCollection::GetForProfile(&profile_3)
+                    ->GetLastActiveBrowser();
   ASSERT_TRUE(new_browser);
   EXPECT_FALSE(new_browser->GetWindow()->IsActive());
 
   ASSERT_EQ(1u, chrome::GetBrowserCount(&profile_4));
-  new_browser = chrome::FindBrowserWithProfile(&profile_4);
+  new_browser = ProfileBrowserCollection::GetForProfile(&profile_4)
+                    ->GetLastActiveBrowser();
   ASSERT_TRUE(new_browser);
   EXPECT_FALSE(new_browser->GetWindow()->IsActive());
 }
diff --git a/chrome/browser/ui/startup/startup_browser_creator_triggered_reset_browsertest_win.cc b/chrome/browser/ui/startup/startup_browser_creator_triggered_reset_browsertest_win.cc
index 4adce50..c05df0b 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_triggered_reset_browsertest_win.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_triggered_reset_browsertest_win.cc
@@ -18,9 +18,9 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/startup/startup_browser_creator.h"
 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
@@ -261,7 +261,8 @@
   }
 
   BrowserWindowInterface* other_profile_browser =
-      chrome::FindBrowserWithProfile(other_profile_ptr);
+      ProfileBrowserCollection::GetForProfile(other_profile_ptr)
+          ->GetLastActiveBrowser();
   ASSERT_NE(nullptr, other_profile_browser);
 
   // Check for the expected reset dialog in the second browser too.
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_ui_controller.cc b/chrome/browser/ui/views/download/bubble/download_toolbar_ui_controller.cc
index c806b621..7ef0dbbf 100644
--- a/chrome/browser/ui/views/download/bubble/download_toolbar_ui_controller.cc
+++ b/chrome/browser/ui/views/download/bubble/download_toolbar_ui_controller.cc
@@ -27,7 +27,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_actions.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
-#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
@@ -1122,9 +1122,12 @@
   // Check if the current browser is the last active browser in this profile.
   // TODO(crbug.com/323962334): This should also check whether the bubble is
   // open once the bubble is added.
+  BrowserWindowInterface* last_active =
+      ProfileBrowserCollection::GetForProfile(browser_view_->GetProfile())
+          ->GetLastActiveBrowser();
   bool should_update_button_progress =
-      browser_view_->browser() ==
-      chrome::FindBrowserWithProfile(browser_view_->GetProfile());
+      last_active &&
+      browser_view_->browser() == last_active->GetBrowserForMigrationOnly();
   if (is_dormant_ == !should_update_button_progress) {
     return;
   }
diff --git a/chrome/browser/ui/views/extensions/dialogs/mv2_deprecation_disabled_dialog.cc b/chrome/browser/ui/views/extensions/dialogs/mv2_deprecation_disabled_dialog.cc
index 92bafd4f..a91f6627 100644
--- a/chrome/browser/ui/views/extensions/dialogs/mv2_deprecation_disabled_dialog.cc
+++ b/chrome/browser/ui/views/extensions/dialogs/mv2_deprecation_disabled_dialog.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "base/functional/callback_helpers.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
diff --git a/chrome/browser/ui/views/extensions/dialogs/mv2_deprecation_keep_dialog.cc b/chrome/browser/ui/views/extensions/dialogs/mv2_deprecation_keep_dialog.cc
index e0695f6..56488f5 100644
--- a/chrome/browser/ui/views/extensions/dialogs/mv2_deprecation_keep_dialog.cc
+++ b/chrome/browser/ui/views/extensions/dialogs/mv2_deprecation_keep_dialog.cc
@@ -3,13 +3,13 @@
 // found in the LICENSE file.
 
 #include "base/functional/callback_helpers.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/extensions/extension_dialog_utils.h"
 #include "chrome/browser/ui/extensions/extensions_dialogs.h"
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/browser/ui_util.h"
 #include "extensions/common/extension.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/ui/views/extensions/dialogs/mv2_disabled_dialog_controller_interactive_uitest.cc b/chrome/browser/ui/views/extensions/dialogs/mv2_disabled_dialog_controller_interactive_uitest.cc
index 2fc2f78..275c85c 100644
--- a/chrome/browser/ui/views/extensions/dialogs/mv2_disabled_dialog_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/extensions/dialogs/mv2_disabled_dialog_controller_interactive_uitest.cc
@@ -6,7 +6,6 @@
 #include "base/path_service.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/extensions/scoped_test_mv2_enabler.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -21,6 +20,7 @@
 #include "content/public/test/browser_test.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/install_verifier.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/browser/mv2_experiment_stage.h"
 #include "extensions/common/extension_features.h"
 #include "extensions/common/mojom/manifest.mojom-shared.h"
diff --git a/chrome/browser/ui/views/frame/browser_frame_view_chromeos_browsertest.cc b/chrome/browser/ui/views/frame/browser_frame_view_chromeos_browsertest.cc
index a6d10b8..848af22 100644
--- a/chrome/browser/ui/views/frame/browser_frame_view_chromeos_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_view_chromeos_browsertest.cc
@@ -52,6 +52,7 @@
 #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/public/browser_window_interface_iterator.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/page_action/page_action_icon_type.h"
 #include "chrome/browser/ui/passwords/passwords_client_ui_delegate.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
@@ -1628,7 +1629,8 @@
   ash::NewWindowDelegate::GetInstance()->NewWindow(
       /*incognito=*/false, /*should_trigger_session_restore=*/false);
   BrowserWindowInterface* browser =
-      chrome::FindBrowserWithProfile(primary_user_profile);
+      ProfileBrowserCollection::GetForProfile(primary_user_profile)
+          ->GetLastActiveBrowser();
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
   BrowserFrameViewChromeOS* frame_view = GetFrameViewChromeOS(browser_view);
   BrowserFrameViewChromeOSTestApi test_api(frame_view);
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
index 5a4be2cc3..9102819 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
@@ -3738,6 +3738,7 @@
       .Times(testing::AtLeast(1));
   passkey_unlock_manager()->NotifyObserversForTesting();
 
+  // Once the error disappeared, the button should return to the normal state.
   EXPECT_EQ(avatar->GetText(), std::u16string());
   histogram_tester.ExpectBucketCount(
       kPasskeyUnlockErrorUiEventHistogram,
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_state_manager.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button_state_manager.cc
index e705c81..c7c161e 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_state_manager.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_state_manager.cc
@@ -933,6 +933,7 @@
       case ButtonState::kSigninPending:
       case ButtonState::kSyncPaused:
       case ButtonState::kExplicitTextShowing:
+      case ButtonState::kPasskeysLockedError:
         Collapse();
         return;
       case ButtonState::kOnSignin:
@@ -940,7 +941,6 @@
       case ButtonState::kIncognitoProfile:
       case ButtonState::kGuestSession:
         break;
-      case ButtonState::kPasskeysLockedError:
       case ButtonState::kNormal:
       case ButtonState::kManagement:
         CHECK(!collapse_timer_.IsRunning());
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
index 29cfe2bb..2b21e1e 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
@@ -60,9 +60,8 @@
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/browser_finder.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/public/profile_browser_collection.h"
 #include "chrome/browser/ui/hats/hats_service_factory.h"
 #include "chrome/browser/ui/hats/mock_hats_service.h"
 #include "chrome/browser/ui/hats/survey_config.h"
@@ -2869,7 +2868,8 @@
   Profile* second_profile = CreateAdditionalProfile();
   web_app::test::WaitUntilWebAppProviderAndSubsystemsReady(
       web_app::WebAppProvider::GetForTest(second_profile));
-  ASSERT_FALSE(chrome::FindBrowserWithProfile(second_profile));
+  ASSERT_FALSE(ProfileBrowserCollection::GetForProfile(second_profile)
+                   ->GetLastActiveBrowser());
 
   // Install and launch an application for the first profile.
   webapps::AppId app_id = toolbar_helper().InstallAndLaunchCustomWebApp(
@@ -2894,7 +2894,8 @@
   content::WebContents* new_web_contents = waiter.Wait();
   ASSERT_TRUE(new_web_contents);
   BrowserWindowInterface* new_browser =
-      chrome::FindBrowserWithProfile(second_profile);
+      ProfileBrowserCollection::GetForProfile(second_profile)
+          ->GetLastActiveBrowser();
   ASSERT_TRUE(new_browser);
   EXPECT_TRUE(new_browser->GetType() == BrowserWindowInterface::TYPE_APP);
   EXPECT_EQ(new_browser->GetTabStripModel()->GetActiveWebContents(),
@@ -2917,7 +2918,8 @@
   EXPECT_EQ(app_id,
             toolbar_helper().InstallWebApp(profile3, GURL("https://test.org")));
   SetTargetBrowser(toolbar_helper().app_browser());
-  EXPECT_FALSE(chrome::FindBrowserWithProfile(profile3));
+  EXPECT_FALSE(ProfileBrowserCollection::GetForProfile(profile3)
+                   ->GetLastActiveBrowser());
 
   // Open profile menu in first profile.
   auto* toolbar =
@@ -2935,9 +2937,10 @@
   Click(focused_item);
   content::WebContents* new_web_contents = waiter.Wait();
   ASSERT_TRUE(new_web_contents);
-  EXPECT_FALSE(chrome::FindBrowserWithProfile(profile2));
+  EXPECT_FALSE(ProfileBrowserCollection::GetForProfile(profile2)
+                   ->GetLastActiveBrowser());
   BrowserWindowInterface* new_browser =
-      chrome::FindBrowserWithProfile(profile3);
+      ProfileBrowserCollection::GetForProfile(profile3)->GetLastActiveBrowser();
   ASSERT_TRUE(new_browser);
   EXPECT_TRUE(new_browser->GetType() == BrowserWindowInterface::TYPE_APP);
   EXPECT_EQ(new_browser->GetTabStripModel()->GetActiveWebContents(),
diff --git a/chrome/browser/ui/views/search_ai_mode/signin_promo_controller.cc b/chrome/browser/ui/views/search_ai_mode/signin_promo_controller.cc
index b2ca485..33a5dad 100644
--- a/chrome/browser/ui/views/search_ai_mode/signin_promo_controller.cc
+++ b/chrome/browser/ui/views/search_ai_mode/signin_promo_controller.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
+#include "chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h"
 #include "chrome/browser/ui/views/profiles/avatar_toolbar_button.h"
 #include "chrome/browser/ui/views/search_ai_mode/signin_promo_view.h"
 #include "chrome/browser/ui/views/toolbar/avatar_toolbar_button_interface.h"
@@ -22,26 +23,36 @@
 SearchAIModeSignInPromoController::SearchAIModeSignInPromoController(
     content::WebContents* web_contents)
     : web_contents_(web_contents->GetWeakPtr()) {}
-
 SearchAIModeSignInPromoController::~SearchAIModeSignInPromoController() =
     default;
 
-void SearchAIModeSignInPromoController::MaybeShowPromo(
+void SearchAIModeSignInPromoController::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void SearchAIModeSignInPromoController::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+bool SearchAIModeSignInPromoController::MaybeShowPromo(
     BrowserView* browser_view) {
   CHECK(base::FeatureList::IsEnabled(switches::kEnableSearchAIModeSigninPromo));
   CHECK(!promo_view_);
   CHECK(browser_view);
   CHECK(web_contents_);
-  AvatarToolbarButton* avatar_button =
-      browser_view->toolbar_button_provider()->GetAvatarToolbarButton();
-  CHECK(avatar_button);
 
   Profile* profile =
       Profile::FromBrowserContext(web_contents_->GetBrowserContext());
   if (!signin::ShouldShowSearchAIModeSignInPromo(*profile)) {
-    return;
+    // This may result in destroying the caller and this object.
+    observers_.Notify(&Observer::OnFlowAborted);
+    return false;
   }
 
+  AvatarToolbarButton* avatar_button =
+      browser_view->toolbar_button_provider()->GetAvatarToolbarButton();
+  CHECK(avatar_button);
+
   avatar_pill_closure_runner_ = avatar_button->SetExplicitButtonState(
       l10n_util::GetStringUTF16(IDS_AI_SIGNIN_PROMO_AVATAR_PILL_TEXT),
       // TODO(crbug.com/486858498): Check if an A11y label is needed.
@@ -54,9 +65,18 @@
 
   views::BubbleDialogDelegateView::CreateBubble(std::move(promo_view));
   promo_view_->ShowForReason(LocationBarBubbleDelegateView::USER_GESTURE);
+  return true;
 }
 
-void SearchAIModeSignInPromoController::OnBubbleClosed() {
+void SearchAIModeSignInPromoController::OnViewIsDeleting() {
   promo_view_ = nullptr;
   avatar_pill_closure_runner_.RunAndReset();
 }
+
+void SearchAIModeSignInPromoController::HandlePromoClosing(
+    views::Widget::ClosedReason closed_reason) {
+  if (closed_reason != views::Widget::ClosedReason::kAcceptButtonClicked) {
+    // This may result in destroying the caller and this object.
+    observers_.Notify(&Observer::OnFlowAborted);
+  }
+}
diff --git a/chrome/browser/ui/views/search_ai_mode/signin_promo_controller.h b/chrome/browser/ui/views/search_ai_mode/signin_promo_controller.h
index d181d80..469aab1 100644
--- a/chrome/browser/ui/views/search_ai_mode/signin_promo_controller.h
+++ b/chrome/browser/ui/views/search_ai_mode/signin_promo_controller.h
@@ -7,13 +7,20 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
+#include "chrome/browser/contextual_tasks/search_ai_mode_signin_promo_controller_observer.h"
 #include "content/public/browser/web_contents.h"
+#include "ui/views/widget/widget.h"
 
 class SearchAIModeSignInPromoView;
 class BrowserView;
 
 class SearchAIModeSignInPromoController {
  public:
+  using Observer =
+      ::contextual_tasks::SearchAIModeSignInPromoControllerObserver;
+
   explicit SearchAIModeSignInPromoController(
       content::WebContents* web_contents);
   virtual ~SearchAIModeSignInPromoController();
@@ -22,11 +29,21 @@
   SearchAIModeSignInPromoController& operator=(
       const SearchAIModeSignInPromoController&) = delete;
 
-  // Triggers the promo, subject to eligibility conditions (rate limits).
-  virtual void MaybeShowPromo(BrowserView* browser_view);
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
 
-  // Called when the bubble is closed.
-  void OnBubbleClosed();
+  // Triggers the promo, subject to eligibility conditions (rate limits).
+  // Returns false is the flow is aborted and the promo cannot be shown,
+  // otherwise it returns true and triggers the promo.
+  virtual bool MaybeShowPromo(BrowserView* browser_view);
+
+  // Called once when the view is being destroyed.
+  // It resets temporary UI state.
+  void OnViewIsDeleting();
+
+  // Called when the promo bubble starts to close. Determines if the sign-in
+  // flow should be aborted based on the `closed_reason`.
+  void HandlePromoClosing(views::Widget::ClosedReason closed_reason);
 
   base::WeakPtr<SearchAIModeSignInPromoController> GetWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
@@ -35,6 +52,7 @@
  private:
   base::WeakPtr<content::WebContents> web_contents_;
   raw_ptr<SearchAIModeSignInPromoView> promo_view_ = nullptr;
+  base::ObserverList<Observer> observers_;
   base::ScopedClosureRunner avatar_pill_closure_runner_;
   base::WeakPtrFactory<SearchAIModeSignInPromoController> weak_ptr_factory_{
       this};
diff --git a/chrome/browser/ui/views/search_ai_mode/signin_promo_view.cc b/chrome/browser/ui/views/search_ai_mode/signin_promo_view.cc
index f4d99c5c..ded8ea8e 100644
--- a/chrome/browser/ui/views/search_ai_mode/signin_promo_view.cc
+++ b/chrome/browser/ui/views/search_ai_mode/signin_promo_view.cc
@@ -61,7 +61,7 @@
 
 SearchAIModeSignInPromoView::~SearchAIModeSignInPromoView() {
   if (controller_) {
-    controller_->OnBubbleClosed();
+    controller_->OnViewIsDeleting();
   }
 }
 
@@ -70,6 +70,12 @@
   self_dismissal_timer_.FireNow();
 }
 
+void SearchAIModeSignInPromoView::WindowClosing() {
+  if (controller_) {
+    controller_->HandlePromoClosing(GetWidget()->closed_reason());
+  }
+}
+
 void SearchAIModeSignInPromoView::AddedToWidget() {
   GetBubbleFrameView()->SetProperty(views::kElementIdentifierKey,
                                     kSearchAIModeSignInPromoFrameViewId);
diff --git a/chrome/browser/ui/views/search_ai_mode/signin_promo_view.h b/chrome/browser/ui/views/search_ai_mode/signin_promo_view.h
index cbc4441..a7b236f1 100644
--- a/chrome/browser/ui/views/search_ai_mode/signin_promo_view.h
+++ b/chrome/browser/ui/views/search_ai_mode/signin_promo_view.h
@@ -30,6 +30,9 @@
   void FireTimerForTesting();
 
  private:
+  // views::WidgetDelegate:
+  void WindowClosing() override;
+
   // LocationBarBubbleDelegateView:
   void AddedToWidget() override;
 
diff --git a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc
index b05cab65..4db76f62 100644
--- a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc
@@ -2275,6 +2275,13 @@
 // Drags from browser to separate window and releases mouse.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        DetachToOwnWindow) {
+#if BUILDFLAG(IS_LINUX)
+  if (ui::OzonePlatform::RunningOnWaylandForTest() &&
+      base::FeatureList::IsEnabled(features::kInitialWebUI)) {
+    GTEST_SKIP() << "Test crashes on Wayland with InitialWebUI enabled. "
+                    "See b/471235790.";
+  }
+#endif
   const gfx::Rect initial_bounds(browser()->window()->GetBounds());
   AddTabsAndResetBrowser(browser(), 1);
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
@@ -2498,6 +2505,13 @@
 // Validates behavior when the drag delegate moves the dragged tab back to the
 // source tab strip.
 IN_PROC_BROWSER_TEST_P(TabDragTargetTest, DelegateMovesTabToSourceTabStrip) {
+#if BUILDFLAG(IS_MAC)
+  if (base::FeatureList::IsEnabled(features::kInitialWebUI)) {
+    GTEST_SKIP() << "Skipping test because it fails on Mac with InitialWebUI "
+                    "enabled. See b/464087732.";
+  }
+#endif
+
   AddTabsAndResetBrowser(browser(), 2);
   ASSERT_EQ("0 1 2", IDString(browser()->tab_strip_model()));
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
@@ -2562,6 +2576,13 @@
 // tabs back to the source tab strip.
 IN_PROC_BROWSER_TEST_P(TabDragTargetTest,
                        DelegateMovesSubsetOfTabsToSourceTabStrip) {
+#if BUILDFLAG(IS_MAC)
+  if (base::FeatureList::IsEnabled(features::kInitialWebUI)) {
+    GTEST_SKIP() << "Skipping test because it fails on Mac with InitialWebUI "
+                    "enabled. See b/464087732.";
+  }
+#endif
+
   AddTabsAndResetBrowser(browser(), 4);
   ASSERT_EQ("0 1 2 3 4", IDString(browser()->tab_strip_model()));
 
@@ -2603,6 +2624,13 @@
 
 // Validates behavior when the drag delegate rearranges multiple dragged tabs.
 IN_PROC_BROWSER_TEST_P(TabDragTargetTest, DelegateRearrangesDraggedTabs) {
+#if BUILDFLAG(IS_MAC)
+  if (base::FeatureList::IsEnabled(features::kInitialWebUI)) {
+    GTEST_SKIP() << "Skipping test because it fails on Mac with InitialWebUI "
+                    "enabled. See b/464087732.";
+  }
+#endif
+
   AddTabsAndResetBrowser(browser(), 4);
   ASSERT_EQ("0 1 2 3 4", IDString(browser()->tab_strip_model()));
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
@@ -2640,6 +2668,13 @@
 // Validates behavior when the drag delegate moves a dragged tab that belongs
 // to a group.
 IN_PROC_BROWSER_TEST_P(TabDragTargetTest, DelegateRemovesDraggedTabFromGroup) {
+#if BUILDFLAG(IS_MAC)
+  if (base::FeatureList::IsEnabled(features::kInitialWebUI)) {
+    GTEST_SKIP() << "Skipping test because it fails on Mac with InitialWebUI "
+                    "enabled. See b/464087732.";
+  }
+#endif
+
   AddTabsAndResetBrowser(browser(), 3);
   ASSERT_EQ("0 1 2 3", IDString(browser()->tab_strip_model()));
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
@@ -2724,6 +2759,13 @@
 // screen.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        DetachFromFullsizeWindow) {
+#if BUILDFLAG(IS_LINUX)
+  if (ui::OzonePlatform::RunningOnWaylandForTest() &&
+      base::FeatureList::IsEnabled(features::kInitialWebUI)) {
+    GTEST_SKIP() << "Test crashes on Wayland with InitialWebUI enabled. "
+                    "See b/471235790.";
+  }
+#endif
   // Resize the browser window so that it is as big as the work area.
   gfx::Rect work_area =
       display::Screen::Get()
@@ -2777,6 +2819,13 @@
 // Drags from browser to a separate window and releases mouse.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        DetachToOwnWindowFromMaximizedWindow) {
+#if BUILDFLAG(IS_LINUX)
+  if (ui::OzonePlatform::RunningOnWaylandForTest() &&
+      base::FeatureList::IsEnabled(features::kInitialWebUI)) {
+    GTEST_SKIP() << "Test crashes on Wayland with InitialWebUI enabled. "
+                    "See b/471235790.";
+  }
+#endif
   // InitialWebUI defers window visibility. If we Maximize() before the window
   // is visible, the OS may silently cache or drop the request, causing tests to
   // hang on Wayland. Wait for visibility.
@@ -3956,6 +4005,13 @@
 // the drag.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        RevertHeaderDragWhileDetached) {
+#if BUILDFLAG(IS_MAC)
+  if (base::FeatureList::IsEnabled(features::kInitialWebUI)) {
+    GTEST_SKIP() << "Skipping test because it fails on Mac with InitialWebUI "
+                    "enabled. See b/464087732.";
+  }
+#endif
+
   ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups());
 
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
@@ -4068,6 +4124,13 @@
 // the drag.
 IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
                        RevertCollapsedHeaderDragWhileDetached) {
+#if BUILDFLAG(IS_MAC)
+  if (base::FeatureList::IsEnabled(features::kInitialWebUI)) {
+    GTEST_SKIP() << "Skipping test because it fails on Mac with InitialWebUI "
+                    "enabled. See b/464087732.";
+  }
+#endif
+
   ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups());
 
   TabStrip* tab_strip = GetTabStripForBrowser(browser());
@@ -4200,6 +4263,13 @@
 // The kTearOffWebAppTabOpensWebAppWindow experiment determines whether the new
 // browser window will be a normal browser window or an app window.
 IN_PROC_BROWSER_TEST_P(DetachTabWithUrlControlledByWebApp, TearOffWebApp) {
+#if BUILDFLAG(IS_LINUX)
+  if (ui::OzonePlatform::RunningOnWaylandForTest() &&
+      base::FeatureList::IsEnabled(features::kInitialWebUI)) {
+    GTEST_SKIP() << "Test crashes on Wayland with InitialWebUI enabled. "
+                    "See b/471235790.";
+  }
+#endif
   // OS integration is needed to be able to launch web applications. This
   // override ensures OS integration doesn't leave any traces.
   std::unique_ptr<web_app::OsIntegrationTestOverrideImpl::BlockingRegistration>
diff --git a/chrome/browser/ui/webui/browser_command/browser_command_handler.cc b/chrome/browser/ui/webui/browser_command/browser_command_handler.cc
index f64b635..7c89948 100644
--- a/chrome/browser/ui/webui/browser_command/browser_command_handler.cc
+++ b/chrome/browser/ui/webui/browser_command/browser_command_handler.cc
@@ -22,10 +22,9 @@
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_navigator.h"
-#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/customize_chrome/side_panel_controller.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
@@ -282,7 +281,8 @@
 }
 
 void BrowserCommandHandler::StartTutorial(StartTutorialInPage::Params params) {
-  BrowserWindowInterface* browser = chrome::FindBrowserWithProfile(profile_);
+  BrowserWindowInterface* browser =
+      ProfileBrowserCollection::GetForProfile(profile_)->GetLastActiveBrowser();
   StartTutorialInPage::Start(browser->GetBrowserForMigrationOnly(),
                              std::move(params));
 }
@@ -294,7 +294,8 @@
 }
 
 bool BrowserCommandHandler::BrowserSupportsTabGroups() {
-  BrowserWindowInterface* browser = chrome::FindBrowserWithProfile(profile_);
+  BrowserWindowInterface* browser =
+      ProfileBrowserCollection::GetForProfile(profile_)->GetLastActiveBrowser();
   return browser->GetTabStripModel()->SupportsTabGroups();
 }
 
@@ -312,17 +313,19 @@
 
 void BrowserCommandHandler::NavigateToEnhancedProtectionSetting() {
   chrome::ShowSafeBrowsingEnhancedProtectionWithIph(
-      chrome::FindBrowserWithProfile(profile_),
+      ProfileBrowserCollection::GetForProfile(profile_)->GetLastActiveBrowser(),
       safe_browsing::SafeBrowsingSettingReferralMethod::kPromoSlingerReferral);
 }
 
 void BrowserCommandHandler::OpenPasswordManager() {
-  chrome::ShowPasswordManager(chrome::FindBrowserWithProfile(profile_));
+  chrome::ShowPasswordManager(ProfileBrowserCollection::GetForProfile(profile_)
+                                  ->GetLastActiveBrowser());
 }
 
 void BrowserCommandHandler::OpenAISettings() {
-  chrome::ShowSettingsSubPage(chrome::FindBrowserWithProfile(profile_),
-                              chrome::kExperimentalAISettingsSubPage);
+  chrome::ShowSettingsSubPage(
+      ProfileBrowserCollection::GetForProfile(profile_)->GetLastActiveBrowser(),
+      chrome::kExperimentalAISettingsSubPage);
 }
 
 bool BrowserCommandHandler::DefaultSearchProviderIsGoogle() {
@@ -330,7 +333,8 @@
 }
 
 bool BrowserCommandHandler::BrowserSupportsSavedTabGroups() {
-  BrowserWindowInterface* browser = chrome::FindBrowserWithProfile(profile_);
+  BrowserWindowInterface* browser =
+      ProfileBrowserCollection::GetForProfile(profile_)->GetLastActiveBrowser();
 
   // Duplicated from chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
   // Which cannot be included here
diff --git a/chrome/browser/ui/webui/extensions/extensions_ui.cc b/chrome/browser/ui/webui/extensions/extensions_ui.cc
index 72bc6ea..be54b7c6 100644
--- a/chrome/browser/ui/webui/extensions/extensions_ui.cc
+++ b/chrome/browser/ui/webui/extensions/extensions_ui.cc
@@ -16,7 +16,6 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
 #include "chrome/browser/extensions/permissions_url_constants.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/favicon_source.h"
@@ -42,6 +41,7 @@
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 #include "extensions/browser/mv2_experiment_stage.h"
 #include "extensions/common/extension_features.h"
 #include "extensions/common/extension_urls.h"
diff --git a/chrome/browser/ui/webui/password_manager/sync_handler.cc b/chrome/browser/ui/webui/password_manager/sync_handler.cc
index f9abc3d..a090e497 100644
--- a/chrome/browser/ui/webui/password_manager/sync_handler.cc
+++ b/chrome/browser/ui/webui/password_manager/sync_handler.cc
@@ -20,7 +20,8 @@
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 #include "chrome/browser/profiles/batch_upload/batch_upload_service.h"
 #include "chrome/browser/profiles/batch_upload/batch_upload_service_factory.h"
-#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #endif
 
 namespace password_manager {
@@ -191,8 +192,10 @@
   BatchUploadService* batch_upload =
       BatchUploadServiceFactory::GetForProfile(profile_);
   CHECK(batch_upload);
-  batch_upload->OpenBatchUpload(chrome::FindBrowserWithProfile(profile_),
-                                entry_point);
+  BrowserWindowInterface* browser =
+      ProfileBrowserCollection::GetForProfile(profile_)->GetLastActiveBrowser();
+  batch_upload->OpenBatchUpload(
+      browser ? browser->GetBrowserForMigrationOnly() : nullptr, entry_point);
 }
 #endif
 
diff --git a/chrome/browser/ui/webui/policy/policy_test_ui_browsertest.cc b/chrome/browser/ui/webui/policy/policy_test_ui_browsertest.cc
index 651b791..36a9768 100644
--- a/chrome/browser/ui/webui/policy/policy_test_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/policy/policy_test_ui_browsertest.cc
@@ -316,6 +316,36 @@
 };
 
 IN_PROC_BROWSER_TEST_P(PolicyTestHandlerTest,
+                       HandleRestartBrowserNotSupported) {
+  // Ensure chrome://policy/test not supported.
+  policy::ScopedManagementServiceOverrideForTesting profile_management(
+      policy::ManagementServiceFactory::GetForProfile(GetProfile()),
+      policy::EnterpriseManagementAuthority::CLOUD);
+  std::unique_ptr<PolicyUIHandler> handler = SetUpHandler();
+  const std::string jsonString =
+      R"([
+      {"level": 0,"scope": 0,"source": 0, "namespace": "chrome",
+       "name": "AutofillAddressEnabled","value": false},
+      {"level": 1,"scope": 1,"source": 2, "namespace": "chrome",
+       "name": "CloudReportingEnabled","value": true}
+      ])";
+
+  // Open chrome://policy
+  ASSERT_TRUE(
+      content::NavigateToURL(web_contents(), GURL(chrome::kChromeUIPolicyURL)));
+  RestartBrowser(jsonString);
+
+  base::RunLoop().RunUntilIdle();
+
+  // Check policies not applied to preference
+  PrefService* prefs = g_browser_process->local_state();
+  std::string pref_value =
+      prefs->GetString(policy::policy_prefs::kLocalTestPoliciesForNextStartup);
+  EXPECT_TRUE(pref_value.empty());
+}
+
+
+IN_PROC_BROWSER_TEST_P(PolicyTestHandlerTest,
                        HandleSetLocalTestPoliciesNotSupported) {
   // Ensure chrome://policy/test not supported.
   policy::ScopedManagementServiceOverrideForTesting profile_management(
@@ -655,6 +685,9 @@
   RestartBrowser(jsonString);
 
   handler.reset();
+
+  // Restart the browser.
+  chrome::AttemptRestart();
 }
 
 IN_PROC_BROWSER_TEST_P(PolicyTestHandlerTestDisabledByPolicy,
diff --git a/chrome/browser/ui/webui/policy/policy_ui_handler.cc b/chrome/browser/ui/webui/policy/policy_ui_handler.cc
index 8426032..5a3318e1 100644
--- a/chrome/browser/ui/webui/policy/policy_ui_handler.cc
+++ b/chrome/browser/ui/webui/policy/policy_ui_handler.cc
@@ -429,6 +429,10 @@
 }
 
 void PolicyUIHandler::RestartBrowser(const std::string& policies) {
+  if (!PolicyUI::ShouldLoadTestPage(&*profile_)) {
+    return;
+  }
+
   // Set policies to preference
   PrefService* prefs = g_browser_process->local_state();
   prefs->SetString(policy::policy_prefs::kLocalTestPoliciesForNextStartup,
diff --git a/chrome/browser/ui/webui/settings/search_engines_handler.cc b/chrome/browser/ui/webui/settings/search_engines_handler.cc
index b247524..86bad8ac 100644
--- a/chrome/browser/ui/webui/settings/search_engines_handler.cc
+++ b/chrome/browser/ui/webui/settings/search_engines_handler.cc
@@ -313,11 +313,14 @@
   // The icons that are used for search engines in the EEA region are bundled
   // with Chrome. We use the favicon service for countries outside the EEA
   // region to guarantee having icons for all search engines.
+  // There may not be a resource ID associated with this template URL, e.g. for
+  // starter pack engines or non-branded builds. In this case, do not set the
+  // icon path but let the WebUI handle the fallback logic instead.
   regional_capabilities::RegionalCapabilitiesService* regional_capabilities =
       regional_capabilities::RegionalCapabilitiesServiceFactory::GetForProfile(
           profile);
   const bool is_eea_region = regional_capabilities->IsInEeaCountry();
-  if (is_eea_region) {
+  if (is_eea_region && template_url->GetBaseBuiltinResourceId().has_value()) {
     // The search engine icon path are 24px, but displayed at 16px, or 32px on
     // HiDPI screens. Use the 2x version (48px) for a large enough icon.
     // Note that this icon path is used in `site-favicon` which does not
diff --git a/chrome/browser/ui/webui/user_education_internals/user_education_internals_page_handler_impl.cc b/chrome/browser/ui/webui/user_education_internals/user_education_internals_page_handler_impl.cc
index 3fc2c8b..bb2853c5 100644
--- a/chrome/browser/ui/webui/user_education_internals/user_education_internals_page_handler_impl.cc
+++ b/chrome/browser/ui/webui/user_education_internals/user_education_internals_page_handler_impl.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/browser/ui/interaction/browser_elements.h"
 #include "chrome/browser/ui/user_education/browser_user_education_interface.h"
 #include "chrome/browser/ui/webui/user_education_internals/user_education_internals.mojom-forward.h"
@@ -516,7 +517,8 @@
   std::string result;
   if (tutorial_service) {
     const ui::ElementContext context =
-        BrowserElements::From(chrome::FindBrowserWithProfile(profile_))
+        BrowserElements::From(ProfileBrowserCollection::GetForProfile(profile_)
+                                  ->GetLastActiveBrowser())
             ->GetContext();
     tutorial_service->StartTutorial(tutorial_id, context);
     if (!tutorial_service->IsRunningTutorial()) {
diff --git a/chrome/browser/ui/webui/util/webui_util_desktop.cc b/chrome/browser/ui/webui/util/webui_util_desktop.cc
index 2cd6ab1..befca7f0c 100644
--- a/chrome/browser/ui/webui/util/webui_util_desktop.cc
+++ b/chrome/browser/ui/webui/util/webui_util_desktop.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
+#include "chrome/browser/ui/browser_window/public/profile_browser_collection.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/common/webui_url_utils.h"
 #include "content/public/common/url_constants.h"
@@ -124,7 +125,8 @@
   // WebContents during navigation.
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
-  BrowserWindowInterface* browser = chrome::FindBrowserWithProfile(profile);
+  BrowserWindowInterface* browser =
+      ProfileBrowserCollection::GetForProfile(profile)->GetLastActiveBrowser();
   if (browser) {
     return browser->GetBrowserForMigrationOnly()->window()->GetThemeProvider();
   }
diff --git a/chrome/build/android-arm64.orderfile.txt b/chrome/build/android-arm64.orderfile.txt
index acf8342..a4cade4 100644
--- a/chrome/build/android-arm64.orderfile.txt
+++ b/chrome/build/android-arm64.orderfile.txt
@@ -1 +1 @@
-OCPeEpdCl0kxFX9BT1jyKhJwDHOW-lh_XJ4BSn6jPyAC
+49a8QfNuE8XTISLWbL6Q0El65_6_e_D9PPS_Q7lF7xQC
diff --git a/chrome/build/android-desktop-x64.pgo.txt b/chrome/build/android-desktop-x64.pgo.txt
index 03da1b9..46f8671 100644
--- a/chrome/build/android-desktop-x64.pgo.txt
+++ b/chrome/build/android-desktop-x64.pgo.txt
@@ -1 +1 @@
-chrome-android-desktop-x64-main-1776275870-4db3f41de6a5e7bf76fb9b58a8dc237d183cd9ad-2c0127627a6192500ecfda237a6e39c1a07bc3ce.profdata
+chrome-android-desktop-x64-main-1776340680-bda24e9637623958cb8fa8a78fc672e31a435fcb-fdda86211746a969bcd1294f2a5876cf0948ecd5.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index b1a1ee3f..070f748b 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1776319179-afa3fe3ae3734e7dbb399f9547260e8e5bea932a-845843206aeb25b24b62b4c2ebdc4d851b3f74ad.profdata
+chrome-mac-arm-main-1776340680-5723b1f4ead72a5f13601a784ae332627309820a-fdda86211746a969bcd1294f2a5876cf0948ecd5.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index f3c42a1..74dfc8f 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1776297489-9bc541136d4dab4eaa59d383a20f7263dc77f513-170c47067be83a2534102af3998446b0994925d6.profdata
+chrome-mac-main-1776319179-cafe38f158b6e9f5d5de5264d6e33c3db455c62f-845843206aeb25b24b62b4c2ebdc4d851b3f74ad.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index c29d34a..db00fcc 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1776297489-2cccd3f947af06e4f8dcae37b81f8796dd9a8c1b-170c47067be83a2534102af3998446b0994925d6.profdata
+chrome-win-arm64-main-1776340680-436feba0c9b81c58d5b89b6903a1c230fb22f317-fdda86211746a969bcd1294f2a5876cf0948ecd5.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 8ee78a5..e929b95 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1776297489-db3f271be9b19b281387b22692912144a095e4b6-170c47067be83a2534102af3998446b0994925d6.profdata
+chrome-win32-main-1776329916-4f5bba16f4b911260c362e7eb62be91a3e4c1e3f-7dc9ca237bc9b33cbb17e69d1f11981c74f0d41e.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 4fde057a..2ed8c4a 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1776297489-dd45842656b0cd05ece5033dff8d346e9985c84e-170c47067be83a2534102af3998446b0994925d6.profdata
+chrome-win64-main-1776329916-af1823a1c792c24a921c604c1d3b974e9cc269d5-7dc9ca237bc9b33cbb17e69d1f11981c74f0d41e.profdata
diff --git a/chrome/common/extensions/api/autofill_private.idl b/chrome/common/extensions/api/autofill_private.idl
index d572494..05d2f82 100644
--- a/chrome/common/extensions/api/autofill_private.idl
+++ b/chrome/common/extensions/api/autofill_private.idl
@@ -435,6 +435,8 @@
     boolean? shouldAuthenticateToView;
     // Whether the entity is (or should be) stored in Google Wallet servers.
     boolean? storedInWallet;
+    // Whether the entity is read only.
+    boolean? isReadOnly;
   };
 
   // Contains the minimum amount of information needed to display an entity
@@ -455,6 +457,8 @@
     // If the entity is `storedInWallet`, this string contains the URL to the
     // management page of the pass on the Wallet website.
     DOMString? walletEntityUrl;
+    // Whether the entity is read only.
+    boolean? isReadOnly;
   };
 
   // A Pay Over Time Issuer entry which can be displayed in the autofill
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index a1533326..d5b1cb9 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1399,7 +1399,6 @@
     "../browser/policy/test/browsing_history_policy_browsertest.cc",
     "../browser/safe_browsing/client_side_detection_service_browsertest.cc",
     "../browser/search_engines/prepopulated_engines_migration_browsertest.cc",
-    "../browser/share/qr_code_generator_pixeltest.cc",
     "../browser/ssl/crlset_browsertest.cc",
     "../browser/task_manager/mock_web_contents_task_manager.cc",
     "../browser/task_manager/mock_web_contents_task_manager.h",
@@ -1436,6 +1435,7 @@
     "//chrome/browser/segmentation_platform",
     "//chrome/browser/segmentation_platform:browser_tests",
     "//chrome/browser/segmentation_platform:test_utils",
+    "//chrome/browser/share:browser_tests",
     "//chrome/browser/signin",
     "//chrome/browser/storage:platform_browser_tests",
     "//chrome/browser/tab_group_sync",
@@ -6726,8 +6726,6 @@
     "../browser/services_unittest.cc",
     "../browser/sessions/chrome_serialized_navigation_driver_unittest.cc",
     "../browser/sessions/session_common_utils_unittest.cc",
-    "../browser/share/share_history_unittest.cc",
-    "../browser/share/share_ranking_unittest.cc",
     "../browser/sharing/sharing_device_registration_impl_unittest.cc",
     "../browser/signin/account_consistency_mode_manager_unittest.cc",
     "../browser/signin/account_id_from_account_info_unittest.cc",
@@ -7126,7 +7124,7 @@
     "//chrome/browser/segmentation_platform:test_utils",
     "//chrome/browser/segmentation_platform:unit_tests",
     "//chrome/browser/serial:unit_tests",
-    "//chrome/browser/share",
+    "//chrome/browser/share:unit_tests",
     "//chrome/browser/sharing",
     "//chrome/browser/sharing/click_to_call:unit_tests",
     "//chrome/browser/sharing/sms:unit_tests",
@@ -8059,6 +8057,7 @@
       "//chrome/browser/safe_browsing/android:test_support_java",
       "//chrome/browser/safety_hub/android:unit_tests",
       "//chrome/browser/share",
+      "//chrome/browser/share:test_support",
       "//chrome/browser/sharing/optimization_guide:unit_tests",
       "//chrome/browser/tab:unit_tests",
       "//chrome/browser/thumbnail:unit_tests",
@@ -9858,6 +9857,7 @@
     if (is_chromeos) {
       sources += [
         "../browser/chromeos/extensions/wm/wm_desks_private_events_unittest.cc",
+        "../browser/extensions/api/braille_display_private/braille_display_private_api_unittest.cc",
         "../browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api_ash_unittest.cc",
         "../browser/extensions/api/enterprise_kiosk_input/enterprise_kiosk_input_unittest.cc",
         "../browser/extensions/api/enterprise_login/enterprise_login_api_unittest.cc",
@@ -9877,6 +9877,7 @@
 
       deps += [
         "//chrome/browser/chromeos/extensions/wm",
+        "//chrome/browser/extensions/api/braille_display_private",
         "//chrome/browser/prefs:util",
         "//chrome/browser/ui/webui/ash/parent_access:mojo_bindings",
         "//chrome/common/chromeos/extensions",
diff --git a/chrome/test/data/autofill/captured_sites/artifacts b/chrome/test/data/autofill/captured_sites/artifacts
index cf45122..3a4f9cb 160000
--- a/chrome/test/data/autofill/captured_sites/artifacts
+++ b/chrome/test/data/autofill/captured_sites/artifacts
@@ -1 +1 @@
-Subproject commit cf4512236aee7a35b15bf7b782f4fade8ae9f06b
+Subproject commit 3a4f9cb1d9eab377eadb9f55b5f833d6fbbe5369
diff --git a/chrome/test/data/extensions/api_test/autofill_private/test.js b/chrome/test/data/extensions/api_test/autofill_private/test.js
index 97dcb3a..cdbb877 100644
--- a/chrome/test/data/extensions/api_test/autofill_private/test.js
+++ b/chrome/test/data/extensions/api_test/autofill_private/test.js
@@ -72,6 +72,7 @@
   nickname: 'Personal car',
   shouldAuthenticateToView: false,
   storedInWallet: false,
+  isReadOnly: false,
 };
 
 var UPDATED_ENTITY_INSTANCE = structuredClone(ENTITY_INSTANCE);
@@ -221,6 +222,7 @@
     entityInstanceLabel: entityInstance.type.typeNameAsString,
     entityInstanceSubLabel: sublabel,
     storedInWallet: false,
+    isReadOnly: entityInstance.isReadOnly,
   });
 };
 
@@ -977,7 +979,8 @@
             },
           ],
           guid: 'e4bbe384-ee63-45a4-8df3-713a58fdc181',
-          nickname: 'Personal car'
+          nickname: 'Personal car',
+          isReadOnly: false,
         },
         expectedLabel: 'John Dolan'
       },
@@ -1033,6 +1036,7 @@
           ],
           guid: 'e4bbe384-ee63-45a4-8df3-713a58fdc182',
           nickname: 'Personal passport 1',
+          isReadOnly: false,
         },
         expectedLabel: 'John Dolan · Germany'
       },
@@ -1075,6 +1079,7 @@
           ],
           guid: 'e4bbe384-ee63-45a4-8df3-713a58fdc183',
           nickname: 'Personal passport 1',
+          isReadOnly: false,
         },
         expectedLabel: 'Sansa · Italy'
       },
@@ -1117,6 +1122,7 @@
           ],
           guid: 'e4bbe384-ee63-45a4-8df3-713a58fdc185',
           nickname: 'Personal passport 1',
+          isReadOnly: false,
         },
         expectedLabel: 'John Dolan · Brazil'
       },
@@ -1161,6 +1167,7 @@
           ],
           guid: 'e4bbe384-ee63-45a4-8df3-713a58fdc187',
           nickname: 'Vehicle 1',
+          isReadOnly: false,
         },
         expectedLabel: 'Uno'
       },
@@ -1195,6 +1202,7 @@
           ],
           guid: 'e4bbe384-ee63-45a4-8df3-713a58fdc186',
           nickname: 'Vehicle 2',
+          isReadOnly: false,
         },
         expectedLabel: 'Linea'
       },
@@ -1253,6 +1261,7 @@
           ],
           guid: 'e4bbe384-ee63-45a4-8df3-713a58fdc192',
           nickname: 'Passport 1',
+          isReadOnly: false,
         },
         // Obfuscated Number
         // "...1234"
@@ -1282,6 +1291,7 @@
           ],
           guid: 'e4bbe384-ee63-45a4-8df3-713a58fdc193',
           nickname: 'Passport 2',
+          isReadOnly: false,
         },
         // "...5678"
         expectedLabel: OBFUSCATION_DOT + OBFUSCATION_DOT + OBFUSCATION_DOT +
@@ -1568,6 +1578,33 @@
     chrome.test.succeed();
   },
 
+  async function verifyIsReadOnlyTrueRetained() {
+    const readOnlyGuid = 'e4bbe384-ee63-45a4-8df3-713a58fdc199';
+    const readOnlyEntity = structuredClone(ENTITY_INSTANCE);
+    readOnlyEntity.guid = readOnlyGuid;
+    readOnlyEntity.isReadOnly = true;
+
+    await new Promise(resolve => {
+      chrome.test.listenOnce(
+          chrome.autofillPrivate.onEntityInstancesChanged, resolve);
+      chrome.autofillPrivate.addOrUpdateEntityInstance(readOnlyEntity);
+    });
+
+    const loadedReadOnly =
+        await chrome.autofillPrivate.getEntityInstanceByGuid(readOnlyGuid);
+    chrome.test.assertTrue(!!loadedReadOnly);
+    chrome.test.assertTrue(loadedReadOnly.isReadOnly);
+
+    const entityInstancesWithLabelsList =
+        await chrome.autofillPrivate.loadEntityInstances();
+    const loadedReadOnlyWithLabels =
+        entityInstancesWithLabelsList.find(e => e.guid === readOnlyGuid);
+    chrome.test.assertTrue(!!loadedReadOnlyWithLabels);
+    chrome.test.assertTrue(loadedReadOnlyWithLabels.isReadOnly);
+
+    chrome.test.succeed();
+  },
+
   async function shouldAuthenticateToView() {
     const obfuscatedEntityGuid = 'e4bbe384-ee63-45a4-8df3-713a58fdc188';
     const nonObfuscatedEntityGuid = 'e4bbe384-ee63-45a4-8df3-713a58fdc189';
@@ -1593,6 +1630,7 @@
       }],
       guid: obfuscatedEntityGuid,
       nickname: 'Obfuscated Passport',
+      isReadOnly: false,
     };
 
     // Entity without obfuscated field
@@ -1616,6 +1654,7 @@
       }],
       guid: nonObfuscatedEntityGuid,
       nickname: 'Clear Passport',
+      isReadOnly: false,
     };
 
     const addEntity = async (entity) => {
@@ -1695,6 +1734,7 @@
   'loadUpdatedEntityInstance': ['loadUpdatedEntityInstance'],
   'getEntityInstanceByGuid': ['getEntityInstanceByGuid'],
   'shouldAuthenticateToView': ['shouldAuthenticateToView'],
+  'verifyIsReadOnlyTrueRetained': ['verifyIsReadOnlyTrueRetained'],
   'getWritableEntityTypes': ['getWritableEntityTypes'],
   'verifyWritableEntityTypesDoesNotIncludeReadOnlyTypes':
       ['verifyWritableEntityTypesDoesNotIncludeReadOnlyTypes'],
diff --git a/chrome/test/data/webui/side_panel/read_anything/app_content_test.ts b/chrome/test/data/webui/side_panel/read_anything/app_content_test.ts
index f91dc56..3ebe094 100644
--- a/chrome/test/data/webui/side_panel/read_anything/app_content_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/app_content_test.ts
@@ -218,6 +218,22 @@
     assertLT(0, getLineFocusPadding());
   });
 
+  test('line focus only shows on content', async () => {
+    chrome.readingMode.isLineFocusEnabled = true;
+
+    contentController.setState(ContentType.NO_CONTENT);
+    await microtasksFinished();
+    assertTrue(app.$.lineFocus.hasAttribute('hidden'));
+
+    contentController.setState(ContentType.LOADING);
+    await microtasksFinished();
+    assertTrue(app.$.lineFocus.hasAttribute('hidden'));
+
+    contentController.setState(ContentType.HAS_CONTENT);
+    await microtasksFinished();
+    assertFalse(app.$.lineFocus.hasAttribute('hidden'));
+  });
+
   test('showLoading shows spinner', async () => {
     const spinner = 'throbber';
 
diff --git a/chrome/test/data/webui/side_panel/read_anything/app_receives_toolbar_changes_test.ts b/chrome/test/data/webui/side_panel/read_anything/app_receives_toolbar_changes_test.ts
index d7e531e7..8e47ec9 100644
--- a/chrome/test/data/webui/side_panel/read_anything/app_receives_toolbar_changes_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/app_receives_toolbar_changes_test.ts
@@ -202,8 +202,9 @@
 
   test('line focus style change updates line focus', async () => {
     chrome.readingMode.isLineFocusEnabled = true;
-    const lineFocus =
-        app.$.containerParent.querySelector<HTMLElement>('#lineFocus');
+    app.updateContent();
+    await microtasksFinished();
+    const lineFocus = app.$.lineFocus;
     assertTrue(!!lineFocus);
 
     let expectedData = LineFocusStyle.UNDERLINE;
@@ -310,8 +311,7 @@
 
   test('line focus change does nothing with flag disabled', async () => {
     chrome.readingMode.isLineFocusEnabled = false;
-    const lineFocus =
-        app.$.containerParent.querySelector<HTMLElement>('#lineFocus');
+    const lineFocus = app.$.lineFocus;
     assertTrue(!!lineFocus);
 
     emitEvent(
diff --git a/chrome/test/data/webui/side_panel/read_anything/app_style_updater_test.ts b/chrome/test/data/webui/side_panel/read_anything/app_style_updater_test.ts
index fce0153..1bb3ec7 100644
--- a/chrome/test/data/webui/side_panel/read_anything/app_style_updater_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/app_style_updater_test.ts
@@ -4,7 +4,6 @@
 import {AppStyleUpdater, BrowserProxy, LineFocusType} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
 import type {AppElement} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
 import {assertEquals, assertGT, assertNotEquals, assertStringContains} from 'chrome-untrusted://webui-test/chai_assert.js';
-import {microtasksFinished} from 'chrome-untrusted://webui-test/test_util.js';
 
 import {createApp} from './common.js';
 import {FakeReadingMode} from './fake_reading_mode.js';
@@ -18,6 +17,10 @@
     return window.getComputedStyle(app.$.container).getPropertyValue(style);
   }
 
+  function computeLineFocusStyle(style: string) {
+    return window.getComputedStyle(app.$.lineFocus).getPropertyValue(style);
+  }
+
   function setAppFontSize(size: number) {
     app.style.fontSize = size + 'px';
   }
@@ -139,62 +142,34 @@
         assertNotEquals(windowBg, lineBg);
       });
 
+  test('setLineFocusStyle sets different padding for different types', () => {
+    chrome.readingMode.isLineFocusEnabled = true;
+
+    updater.setLineFocusStyle(LineFocusType.WINDOW);
+    assertEquals('0px', computeLineFocusStyle('left'));
+    assertEquals('0px', computeLineFocusStyle('right'));
+
+    updater.setLineFocusStyle(LineFocusType.LINE);
+    assertNotEquals('0px', computeLineFocusStyle('left'));
+    assertNotEquals('0px', computeLineFocusStyle('right'));
+  });
+
   test('setLineFocusPos sets y position', () => {
     const pos = 123;
 
-    updater.setLineFocusPos(pos, null, app.$.containerParent);
+    updater.setLineFocusPos(pos, null);
 
     assertEquals(`${pos}px`, app.style.getPropertyValue('--line-focus-y'));
-    assertEquals(
-        `-${pos}px`, app.style.getPropertyValue('--line-focus-clip-top'));
     assertEquals('', app.style.getPropertyValue('--line-focus-height'));
-    assertEquals('', app.style.getPropertyValue('--line-focus-clip-bottom'));
-  });
-
-  test('setLineFocusPos offsets top', async () => {
-    const pos = 123;
-    // Ensure there's content so there is an offset.
-    app.updateContent();
-    await microtasksFinished();
-
-    updater.setLineFocusPos(pos, null, app.$.containerParent);
-
-    assertEquals(`${pos}px`, app.style.getPropertyValue('--line-focus-y'));
-    assertEquals(
-        `-${pos - app.$.containerParent.offsetTop}px`,
-        app.style.getPropertyValue('--line-focus-clip-top'));
-    assertEquals('', app.style.getPropertyValue('--line-focus-height'));
-    assertEquals('', app.style.getPropertyValue('--line-focus-clip-bottom'));
   });
 
   test('setLineFocusPos sets height', () => {
     const height = 456;
 
-    updater.setLineFocusPos(0, height, app.$.containerParent);
+    updater.setLineFocusPos(0, height);
 
     assertEquals(
         `${height}px`, app.style.getPropertyValue('--line-focus-height'));
-    assertEquals(
-        `${height}px`, app.style.getPropertyValue('--line-focus-clip-bottom'));
-  });
-
-  test('setLineFocusPos offsets bottom when height given', async () => {
-    const pos = 123;
-    // Ensure there's content so there is an offset.
-    app.updateContent();
-    await microtasksFinished();
-    const containerHeight = app.$.containerParent.offsetHeight;
-    const containerTop = app.$.containerParent.offsetTop;
-    const windowHeight = containerHeight / 10;
-    console.error('height', app.$.containerParent.offsetHeight);
-
-    updater.setLineFocusPos(pos, windowHeight, app.$.containerParent);
-
-    assertEquals(
-        `${windowHeight}px`, app.style.getPropertyValue('--line-focus-height'));
-    assertEquals(
-        `-${containerHeight - pos - windowHeight + containerTop}px`,
-        app.style.getPropertyValue('--line-focus-clip-bottom'));
   });
 
   test('line spacing depends on font size', () => {
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index 93af27a6..cb7e4aad 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-149-7778.6-1776047075-benchmark-149.0.7790.0_pre1613492-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-149-7778.6-1776047075-benchmark-149.0.7793.0_pre1614753-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index a2fd382..dc0e33e 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-149-7778.6-1776045031-benchmark-149.0.7790.0_pre1613492-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-149-7778.6-1776045031-benchmark-149.0.7793.0_pre1614753-r1-redacted.afdo.xz
diff --git a/clank b/clank
index dd7e41d5..97bf935 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit dd7e41d58f73e6718581674e8f0841fcd40ac49f
+Subproject commit 97bf935c264d604563f58ace7cb554bc4f34aa19
diff --git a/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl.cc b/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl.cc
index 365d365..70812c2 100644
--- a/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl.cc
+++ b/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl.cc
@@ -139,6 +139,9 @@
   }
 
   OneTimeToken& token = token_or_error.value();
+  if (!token.value().empty()) {
+    owner_->GetOtpFormEventLogger().OnOtpAvailable();
+  }
 
   // We run PhishGuard check to make sure OTPs are not shown to users on
   // potential phishing sites.
@@ -195,7 +198,6 @@
     suggestions.clear();
   }
 
-  owner_->GetOtpFormEventLogger().OnOtpAvailable();
   std::move(last_pending_get_suggestions_callback_).Run(std::move(suggestions));
 }
 
diff --git a/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl_unittest.cc b/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl_unittest.cc
index a0b88f7..a1b73fb 100644
--- a/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl_unittest.cc
+++ b/components/autofill/core/browser/integrators/one_time_tokens/otp_manager_impl_unittest.cc
@@ -468,6 +468,39 @@
       /*OneTimeTokensPhishGuardVerdict::kUnknown*/ 0, 1);
 }
 
+// Tests that `OnOtpAvailable` is logged even if the PhishGuard check blocks delivery.
+TEST_F(OtpManagerImplTest, OnOtpAvailable_LoggedEvenIfPhishGuardBlocks) {
+  OtpManagerImpl otp_manager(autofill_manager(), &one_time_token_service_);
+
+  one_time_tokens::OneTimeToken otp(one_time_tokens::OneTimeTokenType::kSmsOtp,
+                                    kDefaultOtpValue, base::Time::Now());
+  EXPECT_CALL(sms_otp_backend_, RetrieveSmsOtp)
+      .WillOnce(RunOnceCallback<0>(otp));
+
+  base::OnceCallback<void(bool)> phish_guard_callback;
+  EXPECT_CALL(otp_phish_guard_delegate(), StartOtpPhishGuardCheck)
+      .WillOnce([&](const GURL&, base::OnceCallback<void(bool)> callback) {
+        phish_guard_callback = std::move(callback);
+      });
+
+  // Observing an OTP field triggers retrieval.
+  AddFormWithOtpField();
+
+  base::test::TestFuture<const std::vector<std::string>> future;
+  otp_manager.GetOtpSuggestions(future.GetCallback());
+
+  // Simulate phishing detection.
+  std::move(phish_guard_callback).Run(true);
+
+  // Suggestions should be empty because delivery is blocked.
+  EXPECT_TRUE(future.Get().empty());
+
+  // However, the metric should still be logged as the OTP was successfully retrieved.
+  EXPECT_TRUE(autofill_manager()
+                  .GetOtpFormEventLogger()
+                  .HasLoggedDataToFillAvailableForTesting());
+}
+
 // Tests that `OnBeforeFocusOnFormField` clears the pending callback for
 // `GetOtpSuggestions`.
 TEST_F(OtpManagerImplTest, OnBeforeFocusOnFormField_ClearsPendingCallback) {
diff --git a/components/autofill/core/browser/suggestions/autofill_ai/autofill_ai_suggestion_generator.cc b/components/autofill/core/browser/suggestions/autofill_ai/autofill_ai_suggestion_generator.cc
index 815e26f..b6bdcef 100644
--- a/components/autofill/core/browser/suggestions/autofill_ai/autofill_ai_suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/autofill_ai/autofill_ai_suggestion_generator.cc
@@ -362,6 +362,12 @@
 }
 
 Suggestion::Icon GetSuggestionIcon(EntityType trigger_entity_type) {
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillAiNoFillingIconsExperiment)) {
+    return Suggestion::Icon::kNoIcon;
+  }
+#endif
   switch (trigger_entity_type.name()) {
     case EntityTypeName::kDriversLicense:
       return Suggestion::Icon::kIdCard;
diff --git a/components/autofill/core/browser/suggestions/autofill_ai/autofill_ai_suggestion_generator_unittest.cc b/components/autofill/core/browser/suggestions/autofill_ai/autofill_ai_suggestion_generator_unittest.cc
index 1e382b3..c958675 100644
--- a/components/autofill/core/browser/suggestions/autofill_ai/autofill_ai_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/suggestions/autofill_ai/autofill_ai_suggestion_generator_unittest.cc
@@ -465,6 +465,23 @@
   EXPECT_THAT(suggestions[0], HasIcon(Suggestion::Icon::kFlight));
 }
 
+#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
+// Tests that no icon is set when `kAutofillAiNoFillingIconsExperiment` is
+// enabled.
+TEST_F(AutofillAiSuggestionGeneratorTest,
+       GetFillingSuggestion_FlightReservationEntity_NoIconIfFeatureIsEnabled) {
+  base::test::ScopedFeatureList scoped_feature_list(
+      features::kAutofillAiNoFillingIconsExperiment);
+  SetEntities({GetFlightReservationEntityInstanceWithRandomGuid()});
+  SetForm({FLIGHT_RESERVATION_FLIGHT_NUMBER, FLIGHT_RESERVATION_TICKET_NUMBER,
+           FLIGHT_RESERVATION_CONFIRMATION_CODE});
+
+  std::vector<Suggestion> suggestions =
+      CreateAutofillAiFillingSuggestions(field(0));
+  EXPECT_THAT(suggestions[0], HasIcon(Suggestion::Icon::kNoIcon));
+}
+#endif
+
 TEST_F(AutofillAiSuggestionGeneratorTest, GetFillingSuggestion_PrefixMatching) {
   EntityInstance passport_prefix_matches =
       GetPassportEntityInstanceWithRandomGuid({.name = u"Jon Doe"});
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 444a5a0e..e9b7e1c1 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -212,6 +212,10 @@
 // both the previous and the new value of an updated entity attribute.
 BASE_FEATURE(kAutofillAiNewUpdatePrompt, base::FEATURE_DISABLED_BY_DEFAULT);
 
+// If enabled, Autofill AI filling suggestion do not have an icon.
+BASE_FEATURE(kAutofillAiNoFillingIconsExperiment,
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // If enabled, AutofillAi supports order entities.
 BASE_FEATURE(kAutofillAiOrder, base::FEATURE_DISABLED_BY_DEFAULT);
 
diff --git a/components/autofill/core/common/autofill_features.h b/components/autofill/core/common/autofill_features.h
index 382c35d..db0f915c 100644
--- a/components/autofill/core/common/autofill_features.h
+++ b/components/autofill/core/common/autofill_features.h
@@ -69,6 +69,8 @@
 COMPONENT_EXPORT(AUTOFILL)
 BASE_DECLARE_FEATURE_PARAM(std::string, kAutofillAiIgnoreGeoIpBlocklist);
 COMPONENT_EXPORT(AUTOFILL) BASE_DECLARE_FEATURE(kAutofillAiNewUpdatePrompt);
+COMPONENT_EXPORT(AUTOFILL)
+BASE_DECLARE_FEATURE(kAutofillAiNoFillingIconsExperiment);
 COMPONENT_EXPORT(AUTOFILL) BASE_DECLARE_FEATURE(kAutofillAiOrder);
 COMPONENT_EXPORT(AUTOFILL) BASE_DECLARE_FEATURE(kAutofillAiReauthRequired);
 COMPONENT_EXPORT(AUTOFILL) BASE_DECLARE_FEATURE(kAutofillAiSavePromptSurvey);
diff --git a/components/gwp_asan/client/lightweight_detector/poison_metadata_recorder.h b/components/gwp_asan/client/lightweight_detector/poison_metadata_recorder.h
index 185a83f5..2260b929 100644
--- a/components/gwp_asan/client/lightweight_detector/poison_metadata_recorder.h
+++ b/components/gwp_asan/client/lightweight_detector/poison_metadata_recorder.h
@@ -17,6 +17,7 @@
 
 namespace gwp_asan::internal {
 FORWARD_DECLARE_TEST(LightweightDetectorAnalyzerTest, InternalError);
+FORWARD_DECLARE_TEST(LightweightDetectorAnalyzerTest, InvalidTraceLength);
 }  // namespace gwp_asan::internal
 
 namespace gwp_asan::internal::lud {
@@ -64,6 +65,9 @@
   FRIEND_TEST_ALL_PREFIXES(
       ::gwp_asan::internal::LightweightDetectorAnalyzerTest,
       InternalError);
+  FRIEND_TEST_ALL_PREFIXES(
+      ::gwp_asan::internal::LightweightDetectorAnalyzerTest,
+      InvalidTraceLength);
 };
 
 extern template class EXPORT_TEMPLATE_DECLARE(GWP_ASAN_EXPORT)
diff --git a/components/gwp_asan/crash_handler/crash_analyzer.cc b/components/gwp_asan/crash_handler/crash_analyzer.cc
index 56af968b..0953a77 100644
--- a/components/gwp_asan/crash_handler/crash_analyzer.cc
+++ b/components/gwp_asan/crash_handler/crash_analyzer.cc
@@ -387,7 +387,8 @@
       metadata.dealloc.trace_len) {
     ReadAllocationInfo(metadata.deallocation_stack_trace,
                        /* stack_trace_offset = */ 0, metadata.dealloc,
-                       proto->mutable_deallocation());
+                       proto->mutable_deallocation(),
+                       LightweightDetectorState::kMaxPackedTraceLength);
   }
 
   ReportHistogram(Crash_Allocator_PARTITIONALLOC,
@@ -533,12 +534,14 @@
     if (metadata.alloc.tid != base::kInvalidThreadId ||
         metadata.alloc.trace_len) {
       ReadAllocationInfo(metadata.stack_trace_pool, 0, metadata.alloc,
-                         proto->mutable_allocation());
+                         proto->mutable_allocation(),
+                         AllocatorState::kMaxPackedTraceLength);
     }
     if (metadata.dealloc.tid != base::kInvalidThreadId ||
         metadata.dealloc.trace_len) {
       ReadAllocationInfo(metadata.stack_trace_pool, metadata.alloc.trace_len,
-                         metadata.dealloc, proto->mutable_deallocation());
+                         metadata.dealloc, proto->mutable_deallocation(),
+                         AllocatorState::kMaxPackedTraceLength);
     }
   }
 
@@ -550,7 +553,8 @@
     const uint8_t* stack_trace,
     size_t stack_trace_offset,
     const AllocationInfo& slot_info,
-    gwp_asan::Crash_AllocationInfo* proto_info) {
+    gwp_asan::Crash_AllocationInfo* proto_info,
+    size_t max_trace_length) {
   if (slot_info.tid != base::kInvalidThreadId) {
     // The PlatformThreadId will match the Crashpad tid in terms of the bit
     // values, however it can differ in bitwidth and sign. To make this uniform,
@@ -564,9 +568,8 @@
   if (!slot_info.trace_len || !slot_info.trace_collected)
     return;
 
-  if (slot_info.trace_len > AllocatorState::kMaxPackedTraceLength ||
-      stack_trace_offset + slot_info.trace_len >
-          AllocatorState::kMaxPackedTraceLength) {
+  if (slot_info.trace_len > max_trace_length ||
+      stack_trace_offset + slot_info.trace_len > max_trace_length) {
     DLOG(ERROR) << "Stack trace length is corrupted: " << slot_info.trace_len;
     return;
   }
diff --git a/components/gwp_asan/crash_handler/crash_analyzer.h b/components/gwp_asan/crash_handler/crash_analyzer.h
index 1410909a..540de0f8 100644
--- a/components/gwp_asan/crash_handler/crash_analyzer.h
+++ b/components/gwp_asan/crash_handler/crash_analyzer.h
@@ -116,7 +116,8 @@
   static void ReadAllocationInfo(const uint8_t* stack_trace,
                                  size_t stack_trace_offset,
                                  const AllocationInfo& slot_info,
-                                 gwp_asan::Crash_AllocationInfo* proto_info);
+                                 gwp_asan::Crash_AllocationInfo* proto_info,
+                                 size_t max_trace_length);
 
   // This method analyzes the AllocatorState of the crashing process. If the
   // exception is related to the Lightweight UAF Detector it fills out the
diff --git a/components/gwp_asan/crash_handler/crash_analyzer_unittest.cc b/components/gwp_asan/crash_handler/crash_analyzer_unittest.cc
index d681247d..fb3c5a5 100644
--- a/components/gwp_asan/crash_handler/crash_analyzer_unittest.cc
+++ b/components/gwp_asan/crash_handler/crash_analyzer_unittest.cc
@@ -339,6 +339,22 @@
             CrashAnalyzer::LightweightDetectorModeToGwpAsanMode(GetParam()));
 }
 
+TEST_P(LightweightDetectorAnalyzerTest, InvalidTraceLength) {
+  uint64_t alloc;
+  ASSERT_TRUE(lud::PoisonMetadataRecorder::Get());
+  lud::PoisonMetadataRecorder::Get()->RecordAndZap(&alloc, sizeof(alloc));
+  InitializeSnapshot(alloc);
+
+  // Corrupt the trace_len to be larger than 90 but less than 400.
+  lud::PoisonMetadataRecorder::Get()->metadata_[0].dealloc.trace_len = 400;
+
+  base::HistogramTester histogram_tester;
+  gwp_asan::Crash proto;
+  bool proto_present =
+      CrashAnalyzer::GetExceptionInfo(process_snapshot_, &proto);
+  ASSERT_TRUE(proto_present);
+}
+
 INSTANTIATE_TEST_SUITE_P(
     VaryLightweightDetectorMode,
     LightweightDetectorAnalyzerTest,
diff --git a/components/multistep_filter/core/annotation_index/BUILD.gn b/components/multistep_filter/core/annotation_index/BUILD.gn
index 677e3b0b..098eea1 100644
--- a/components/multistep_filter/core/annotation_index/BUILD.gn
+++ b/components/multistep_filter/core/annotation_index/BUILD.gn
@@ -72,6 +72,7 @@
     "//base/test:test_support",
     "//components/multistep_filter/core",
     "//components/multistep_filter/core:features",
+    "//components/multistep_filter/core:switches",
     "//components/multistep_filter/core/annotation_index/proto",
     "//components/multistep_filter/core/data_models",
     "//google_apis/common:test_support",
diff --git a/components/multistep_filter/core/annotation_index/annotation_index_client_impl_test_api.h b/components/multistep_filter/core/annotation_index/annotation_index_client_impl_test_api.h
index 52d17b2d..5edf1e6f 100644
--- a/components/multistep_filter/core/annotation_index/annotation_index_client_impl_test_api.h
+++ b/components/multistep_filter/core/annotation_index/annotation_index_client_impl_test_api.h
@@ -26,15 +26,6 @@
   explicit AnnotationIndexClientImplTestApi(AnnotationIndexClientImpl& client)
       : client_(client) {}
 
-  void ExecuteRequest(
-      std::unique_ptr<network::ResourceRequest> request,
-      std::string request_body,
-      net::NetworkTrafficAnnotationTag traffic_annotation,
-      base::OnceCallback<void(std::optional<std::string>)> callback) {
-    client_->ExecuteRequest(std::move(request), std::move(request_body),
-                            traffic_annotation, std::move(callback));
-  }
-
   static std::unique_ptr<AnnotationIndexClientImpl> CreateManagerForApiKey(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& api_key) {
diff --git a/components/multistep_filter/core/annotation_index/annotation_index_client_impl_unittest.cc b/components/multistep_filter/core/annotation_index/annotation_index_client_impl_unittest.cc
index c00e8cdbc..1f791cc 100644
--- a/components/multistep_filter/core/annotation_index/annotation_index_client_impl_unittest.cc
+++ b/components/multistep_filter/core/annotation_index/annotation_index_client_impl_unittest.cc
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/command_line.h"
 #include "base/functional/bind.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/test/scoped_feature_list.h"
@@ -17,12 +18,14 @@
 #include "base/time/time.h"
 #include "base/uuid.h"
 #include "components/multistep_filter/core/annotation_index/annotation_index_client_impl_test_api.h"
+#include "components/multistep_filter/core/annotation_index/annotation_index_test_utils.h"
 #include "components/multistep_filter/core/annotation_index/proto/annotation_index.pb.h"
 #include "components/multistep_filter/core/data_models/filter_annotation.h"
 #include "components/multistep_filter/core/data_models/filter_suggestion_candidate.h"
 #include "components/multistep_filter/core/features.h"
+#include "components/multistep_filter/core/switches.h"
 #include "google_apis/common/api_key_request_test_util.h"
-#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/public/cpp/data_element.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
@@ -35,18 +38,48 @@
 
 namespace {
 
-constexpr char kTestApiUrl[] = "https://api.googleapis.com/test";
-constexpr char kTestApiUrl2[] = "https://api.googleapis.com/test2";
-constexpr char kTestApiBody[] = "{\"test_body\": true}";
-constexpr char kTestFakeSuccessResponse[] = "{\"status\": \"ok\"}";
-constexpr char kTestCandidateId[] = "12345678-1234-4678-a234-567812345678";
+constexpr char kTestApiUrl[] = "https://api.googleapis.com/test/";
+constexpr char kTestNonGoogleApiUrl[] = "https://api.example.com/test/";
+constexpr char kTestSwitchApiUrl[] = "https://switch.example.com/api/";
+constexpr char kTestInvalidUrl[] = "invalid_url";
+constexpr char kDummyApiKey[] = "dummykey";
+constexpr char kTestUrl[] = "https://example.com/test";
+constexpr char kTestExtractUrl[] = "https://example.com/path?q=1";
+constexpr char kTestSuggestionUrl[] = "https://travel.com/flights?min=100";
+constexpr char kTestCandidateId[] = "12345678-1234-5678-1234-567812345678";
+constexpr char kTestDomain[] = "example.com";
+constexpr char kTestDomain1[] = "example1.com";
+constexpr char kTestDomain2[] = "example2.com";
+constexpr char kTask1[] = "TASK1";
+constexpr char kTask2[] = "TASK2";
+constexpr char kTestTaskType[] = "SEARCH_FLIGHTS";
+constexpr char kTestAttributeKey[] = "PRICE_MIN";
+constexpr char kTestAttributeValue[] = "100";
+constexpr char kTestAttributeLabel[] = "Min Price";
 
-std::unique_ptr<network::ResourceRequest> CreateRequest(
-    const std::string& url) {
-  auto request = std::make_unique<network::ResourceRequest>();
-  request->url = GURL(url);
-  request->method = "POST";
-  return request;
+// Extracts the string payload from a URLLoader request body.
+std::string GetStringFromDataElements(
+    const std::vector<network::DataElement>* data_elements) {
+  std::string result;
+  for (const network::DataElement& e : *data_elements) {
+    if (e.type() == network::DataElement::Tag::kBytes) {
+      result.append(e.As<network::DataElementBytes>().AsStringPiece());
+    }
+  }
+  return result;
+}
+
+// Helper to deserialize the uploaded proto from a pending request.
+template <typename RequestProto>
+bool GetRequestProtoFromPendingRequest(
+    const network::TestURLLoaderFactory::PendingRequest* request,
+    RequestProto* out_proto) {
+  if (!request || !request->request.request_body) {
+    return false;
+  }
+  std::string body_content =
+      GetStringFromDataElements(request->request.request_body->elements());
+  return out_proto->ParseFromString(body_content);
 }
 
 class AnnotationIndexClientImplTest : public testing::Test {
@@ -65,189 +98,425 @@
 
   ~AnnotationIndexClientImplTest() override = default;
 
+  void TearDown() override {
+    base::CommandLine::ForCurrentProcess()->RemoveSwitch(
+        switches::kMultistepFilterIndexServerApiBaseUrl);
+  }
+
  protected:
-  base::test::TaskEnvironment task_environment_;
+  void OverrideBaseUrlWithSwitch(const std::string& base_url) {
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        switches::kMultistepFilterIndexServerApiBaseUrl, base_url);
+  }
+
+  void SimulateHttpError(network::TestURLLoaderFactory::PendingRequest* request,
+                         net::HttpStatusCode status) {
+    test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+        request, network::CreateURLResponseHead(status), "Server Error",
+        network::URLLoaderCompletionStatus(net::OK));
+  }
+
+  void SimulateNetworkError(
+      network::TestURLLoaderFactory::PendingRequest* request,
+      int error_code = net::ERR_FAILED) {
+    test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+        request, network::mojom::URLResponseHead::New(), std::string(),
+        network::URLLoaderCompletionStatus(error_code));
+  }
+
+  void SimulateInvalidResponse(
+      network::TestURLLoaderFactory::PendingRequest* request) {
+    test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+        request, "not a valid proto");
+  }
+
+  void SimulateTimeout(network::TestURLLoaderFactory::PendingRequest* request) {
+    SimulateNetworkError(request, net::ERR_TIMED_OUT);
+  }
+
+  void SimulateEmptyResponse(
+      network::TestURLLoaderFactory::PendingRequest* request) {
+    test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+        request, "");
+  }
+
   base::test::ScopedFeatureList scoped_feature_list_;
+  base::test::TaskEnvironment task_environment_;
   network::TestURLLoaderFactory test_url_loader_factory_;
   scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
 
   std::unique_ptr<AnnotationIndexClientImpl> client_;
 };
 
-TEST_F(AnnotationIndexClientImplTest, ExecuteRequest_Success) {
-  base::test::TestFuture<std::optional<std::string>> future;
-
-  test_api(*client_).ExecuteRequest(
-      CreateRequest(kTestApiUrl), std::string(kTestApiBody),
-      TRAFFIC_ANNOTATION_FOR_TESTS, future.GetCallback());
-  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
-
-  network::TestURLLoaderFactory::PendingRequest* request =
-      test_url_loader_factory_.GetPendingRequest(0);
-  EXPECT_EQ(google_apis::test_util::GetAPIKeyFromRequest(request->request),
-            "dummykey");
-
-  test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
-      request, kTestFakeSuccessResponse);
-
-  std::optional<std::string> result = future.Take();
-
-  ASSERT_TRUE(result);
-  EXPECT_EQ(*result, kTestFakeSuccessResponse);
-}
-
-TEST_F(AnnotationIndexClientImplTest, ExecuteRequest_MultipleRequests) {
-  base::test::TestFuture<std::optional<std::string>> future1;
-  base::test::TestFuture<std::optional<std::string>> future2;
-
-  test_api(*client_).ExecuteRequest(
-      CreateRequest(kTestApiUrl), std::string(kTestApiBody),
-      TRAFFIC_ANNOTATION_FOR_TESTS, future1.GetCallback());
-  test_api(*client_).ExecuteRequest(
-      CreateRequest(kTestApiUrl2), std::string(kTestApiBody),
-      TRAFFIC_ANNOTATION_FOR_TESTS, future2.GetCallback());
-
-  EXPECT_EQ(test_url_loader_factory_.NumPending(), 2);
-
-  network::TestURLLoaderFactory::PendingRequest* request1 =
-      test_url_loader_factory_.GetPendingRequest(0);
-  test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
-      request1, kTestFakeSuccessResponse);
-
-  network::TestURLLoaderFactory::PendingRequest* request2 =
-      test_url_loader_factory_.GetPendingRequest(1);
-  test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
-      request2, kTestFakeSuccessResponse);
-
-  std::optional<std::string> result1 = future1.Take();
-  ASSERT_TRUE(result1);
-  EXPECT_EQ(*result1, kTestFakeSuccessResponse);
-
-  std::optional<std::string> result2 = future2.Take();
-  ASSERT_TRUE(result2);
-  EXPECT_EQ(*result2, kTestFakeSuccessResponse);
-}
-
-TEST_F(AnnotationIndexClientImplTest, ExecuteRequest_NetworkError) {
-  base::test::TestFuture<std::optional<std::string>> future;
-
-  test_api(*client_).ExecuteRequest(
-      CreateRequest(kTestApiUrl), std::string(kTestApiBody),
-      TRAFFIC_ANNOTATION_FOR_TESTS, future.GetCallback());
-
-  EXPECT_EQ(test_url_loader_factory_.NumPending(), 1);
-
-  network::TestURLLoaderFactory::PendingRequest* request =
-      test_url_loader_factory_.GetPendingRequest(0);
-  test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
-      request, network::mojom::URLResponseHead::New(), std::string(),
-      network::URLLoaderCompletionStatus(net::ERR_FAILED));
-
-  std::optional<std::string> result = future.Take();
-
-  EXPECT_FALSE(result.has_value());
-}
-
-TEST_F(AnnotationIndexClientImplTest, ExecuteRequest_HttpError) {
-  base::test::TestFuture<std::optional<std::string>> future;
-
-  test_api(*client_).ExecuteRequest(
-      CreateRequest(kTestApiUrl), std::string(kTestApiBody),
-      TRAFFIC_ANNOTATION_FOR_TESTS, future.GetCallback());
-
-  EXPECT_EQ(test_url_loader_factory_.NumPending(), 1);
-
-  network::TestURLLoaderFactory::PendingRequest* request =
-      test_url_loader_factory_.GetPendingRequest(0);
-  test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
-      request, network::CreateURLResponseHead(net::HTTP_NOT_FOUND), "Not Found",
-      network::URLLoaderCompletionStatus(net::OK));
-
-  std::optional<std::string> result = future.Take();
-
-  EXPECT_FALSE(result.has_value());
-}
-
-TEST_F(AnnotationIndexClientImplTest, GetSupportedTaskTypesForDomain_Success) {
-  GetSupportedTasksResponse proto_response;
-  proto_response.add_supported_tasks()->set_task_type("TASK1");
-  proto_response.add_supported_tasks()->set_task_type("TASK2");
-
-  base::test::TestFuture<std::optional<std::vector<std::string>>> future;
-
-  client_->GetSupportedTaskTypesForDomain("example.com", future.GetCallback());
-
-  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
-  network::TestURLLoaderFactory::PendingRequest* request =
-      test_url_loader_factory_.GetPendingRequest(0);
-  test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
-      request, proto_response.SerializeAsString());
-
-  std::optional<std::vector<std::string>> result = future.Take();
-  ASSERT_TRUE(result);
-  EXPECT_EQ(result->size(), 2u);
-  EXPECT_EQ((*result)[0], "TASK1");
-  EXPECT_EQ((*result)[1], "TASK2");
-}
-
-TEST_F(AnnotationIndexClientImplTest, GetFilterSuggestionCandidates_Success) {
-  GetTaskExecutionStrategiesResponse proto_response;
-  TaskExecutionStrategy* strategy = proto_response.add_execution_strategies();
-  strategy->set_candidate_id(kTestCandidateId);
-  AppliedFilterUIString* filter1 = strategy->add_applied_filters();
-  filter1->set_key("PRICE_MIN");
-  filter1->set_label("Min Price");
-  strategy->mutable_execution()->mutable_url_navigation()->set_navigation_url(
-      "https://travel.com/flights?min=100");
-
+TEST_F(AnnotationIndexClientImplTest,
+       GetFilterSuggestionCandidates_Success_ReturnsCandidates) {
+  GetTaskExecutionStrategiesResponse proto_response =
+      CreateTaskExecutionStrategiesResponse(
+          GURL(kTestSuggestionUrl), {{kTestAttributeKey, kTestAttributeLabel}});
+  proto_response.mutable_execution_strategies(0)->set_candidate_id(
+      kTestCandidateId);
   base::test::TestFuture<std::optional<std::vector<FilterSuggestionCandidate>>>
       future;
-
   std::vector<FilterAnnotation> annotations;
-  client_->GetFilterSuggestionCandidates(GURL("https://example.com/test"),
-                                         annotations, future.GetCallback());
+
+  client_->GetFilterSuggestionCandidates(GURL(kTestUrl), annotations,
+                                         future.GetCallback());
 
   ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
-  network::TestURLLoaderFactory::PendingRequest* request =
+  network::TestURLLoaderFactory::PendingRequest* pending_request =
       test_url_loader_factory_.GetPendingRequest(0);
+  EXPECT_EQ(pending_request->request.url.spec(),
+            std::string(kTestApiUrl) + "GetTaskExecutionStrategies");
+  EXPECT_EQ(
+      google_apis::test_util::GetAPIKeyFromRequest(pending_request->request),
+      kDummyApiKey);
+  GetTaskExecutionStrategiesRequest request_proto;
+  ASSERT_TRUE(
+      GetRequestProtoFromPendingRequest(pending_request, &request_proto));
+  EXPECT_EQ(request_proto.current_url(), kTestUrl);
   test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
-      request, proto_response.SerializeAsString());
-
+      pending_request, proto_response.SerializeAsString());
   std::optional<std::vector<FilterSuggestionCandidate>> result = future.Take();
   ASSERT_TRUE(result);
-  EXPECT_EQ(result->size(), 1u);
+  ASSERT_EQ(result->size(), 1u);
   EXPECT_EQ((*result)[0].filter_annotation_id.AsLowercaseString(),
             kTestCandidateId);
-  EXPECT_EQ((*result)[0].navigation_url.spec(),
-            "https://travel.com/flights?min=100");
+  EXPECT_EQ((*result)[0].navigation_url.spec(), kTestSuggestionUrl);
 }
 
-TEST_F(AnnotationIndexClientImplTest, ExtractFilterAnnotation_Success) {
-  ExtractTaskAttributesResponse proto_response;
-  proto_response.set_domain("example.com");
-  proto_response.set_task_type("SEARCH_FLIGHTS");
-  TaskAttribute* attr = proto_response.add_task_attributes();
-  attr->set_key("PRICE_MIN");
-  attr->set_value("100");
+TEST_F(AnnotationIndexClientImplTest,
+       GetFilterSuggestionCandidates_HttpError_ReturnsNullopt) {
+  base::test::TestFuture<std::optional<std::vector<FilterSuggestionCandidate>>>
+      future;
+  std::vector<FilterAnnotation> annotations;
 
-  base::test::TestFuture<std::optional<FilterAnnotation>> future;
-
-  client_->ExtractFilterAnnotation(GURL("https://example.com/path?q=1"),
-                                   future.GetCallback());
+  client_->GetFilterSuggestionCandidates(GURL(kTestUrl), annotations,
+                                         future.GetCallback());
 
   ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
-  network::TestURLLoaderFactory::PendingRequest* request =
-      test_url_loader_factory_.GetPendingRequest(0);
-  test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
-      request, proto_response.SerializeAsString());
+  SimulateHttpError(test_url_loader_factory_.GetPendingRequest(0),
+                    net::HTTP_INTERNAL_SERVER_ERROR);
+  EXPECT_FALSE(future.Take().has_value());
+}
 
+TEST_F(AnnotationIndexClientImplTest,
+       GetFilterSuggestionCandidates_NetworkError_ReturnsNullopt) {
+  base::test::TestFuture<std::optional<std::vector<FilterSuggestionCandidate>>>
+      future;
+  std::vector<FilterAnnotation> annotations;
+
+  client_->GetFilterSuggestionCandidates(GURL(kTestUrl), annotations,
+                                         future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  SimulateNetworkError(test_url_loader_factory_.GetPendingRequest(0));
+  EXPECT_FALSE(future.Take().has_value());
+}
+
+TEST_F(AnnotationIndexClientImplTest,
+       GetFilterSuggestionCandidates_InvalidResponse_ReturnsNullopt) {
+  base::test::TestFuture<std::optional<std::vector<FilterSuggestionCandidate>>>
+      future;
+  std::vector<FilterAnnotation> annotations;
+
+  client_->GetFilterSuggestionCandidates(GURL(kTestUrl), annotations,
+                                         future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  SimulateInvalidResponse(test_url_loader_factory_.GetPendingRequest(0));
+  EXPECT_FALSE(future.Take().has_value());
+}
+
+TEST_F(AnnotationIndexClientImplTest,
+       GetFilterSuggestionCandidates_EmptyResponse_ReturnsEmptyVector) {
+  base::test::TestFuture<std::optional<std::vector<FilterSuggestionCandidate>>>
+      future;
+  std::vector<FilterAnnotation> annotations;
+
+  client_->GetFilterSuggestionCandidates(GURL(kTestUrl), annotations,
+                                         future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  SimulateEmptyResponse(test_url_loader_factory_.GetPendingRequest(0));
+  auto result = future.Take();
+  ASSERT_TRUE(result.has_value());
+  EXPECT_TRUE(result->empty());
+}
+
+TEST_F(AnnotationIndexClientImplTest,
+       GetFilterSuggestionCandidates_Timeout_ReturnsNullopt) {
+  base::test::TestFuture<std::optional<std::vector<FilterSuggestionCandidate>>>
+      future;
+  std::vector<FilterAnnotation> annotations;
+
+  client_->GetFilterSuggestionCandidates(GURL(kTestUrl), annotations,
+                                         future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  SimulateTimeout(test_url_loader_factory_.GetPendingRequest(0));
+  EXPECT_FALSE(future.Take().has_value());
+}
+
+TEST_F(AnnotationIndexClientImplTest,
+       GetSupportedTaskTypesForDomain_Success_ReturnsTaskTypes) {
+  GetSupportedTasksResponse proto_response =
+      CreateSupportedTasksResponse({kTask1, kTask2});
+  base::test::TestFuture<std::optional<std::vector<std::string>>> future;
+
+  client_->GetSupportedTaskTypesForDomain(kTestDomain, future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  network::TestURLLoaderFactory::PendingRequest* pending_request =
+      test_url_loader_factory_.GetPendingRequest(0);
+  EXPECT_EQ(pending_request->request.url.spec(),
+            std::string(kTestApiUrl) + "GetSupportedTasks");
+  EXPECT_EQ(
+      google_apis::test_util::GetAPIKeyFromRequest(pending_request->request),
+      kDummyApiKey);
+  GetSupportedTasksRequest request_proto;
+  ASSERT_TRUE(
+      GetRequestProtoFromPendingRequest(pending_request, &request_proto));
+  EXPECT_EQ(request_proto.domain(), kTestDomain);
+  test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+      pending_request, proto_response.SerializeAsString());
+  std::optional<std::vector<std::string>> result = future.Take();
+  ASSERT_TRUE(result);
+  ASSERT_EQ(result->size(), 2u);
+  EXPECT_EQ((*result)[0], kTask1);
+  EXPECT_EQ((*result)[1], kTask2);
+}
+
+TEST_F(AnnotationIndexClientImplTest,
+       GetSupportedTaskTypesForDomain_HttpError_ReturnsNullopt) {
+  base::test::TestFuture<std::optional<std::vector<std::string>>> future;
+
+  client_->GetSupportedTaskTypesForDomain(kTestDomain, future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  SimulateHttpError(test_url_loader_factory_.GetPendingRequest(0),
+                    net::HTTP_NOT_FOUND);
+  EXPECT_FALSE(future.Take().has_value());
+}
+
+TEST_F(AnnotationIndexClientImplTest,
+       GetSupportedTaskTypesForDomain_NetworkError_ReturnsNullopt) {
+  base::test::TestFuture<std::optional<std::vector<std::string>>> future;
+
+  client_->GetSupportedTaskTypesForDomain(kTestDomain, future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  SimulateNetworkError(test_url_loader_factory_.GetPendingRequest(0));
+  EXPECT_FALSE(future.Take().has_value());
+}
+
+TEST_F(AnnotationIndexClientImplTest,
+       GetSupportedTaskTypesForDomain_InvalidResponse_ReturnsNullopt) {
+  base::test::TestFuture<std::optional<std::vector<std::string>>> future;
+
+  client_->GetSupportedTaskTypesForDomain(kTestDomain, future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  SimulateInvalidResponse(test_url_loader_factory_.GetPendingRequest(0));
+  EXPECT_FALSE(future.Take().has_value());
+}
+
+TEST_F(AnnotationIndexClientImplTest,
+       GetSupportedTaskTypesForDomain_EmptyResponse_ReturnsEmptyVector) {
+  base::test::TestFuture<std::optional<std::vector<std::string>>> future;
+
+  client_->GetSupportedTaskTypesForDomain(kTestDomain, future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  SimulateEmptyResponse(test_url_loader_factory_.GetPendingRequest(0));
+  auto result = future.Take();
+  ASSERT_TRUE(result.has_value());
+  EXPECT_TRUE(result->empty());
+}
+
+TEST_F(AnnotationIndexClientImplTest,
+       GetSupportedTaskTypesForDomain_Timeout_ReturnsNullopt) {
+  base::test::TestFuture<std::optional<std::vector<std::string>>> future;
+
+  client_->GetSupportedTaskTypesForDomain(kTestDomain, future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  SimulateTimeout(test_url_loader_factory_.GetPendingRequest(0));
+  EXPECT_FALSE(future.Take().has_value());
+}
+
+TEST_F(AnnotationIndexClientImplTest,
+       ExtractFilterAnnotation_Success_ReturnsAnnotation) {
+  ExtractTaskAttributesResponse proto_response =
+      CreateExtractTaskAttributesResponse(
+          kTestDomain, kTestTaskType,
+          {{kTestAttributeKey, kTestAttributeValue}});
+  base::test::TestFuture<std::optional<FilterAnnotation>> future;
+
+  client_->ExtractFilterAnnotation(GURL(kTestExtractUrl), future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  network::TestURLLoaderFactory::PendingRequest* pending_request =
+      test_url_loader_factory_.GetPendingRequest(0);
+  EXPECT_EQ(pending_request->request.url.spec(),
+            std::string(kTestApiUrl) + "ExtractTaskAttributes");
+  EXPECT_EQ(
+      google_apis::test_util::GetAPIKeyFromRequest(pending_request->request),
+      kDummyApiKey);
+  ExtractTaskAttributesRequest request_proto;
+  ASSERT_TRUE(
+      GetRequestProtoFromPendingRequest(pending_request, &request_proto));
+  EXPECT_EQ(request_proto.source().raw_url(), kTestExtractUrl);
+  test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+      pending_request, proto_response.SerializeAsString());
   std::optional<FilterAnnotation> result = future.Take();
   ASSERT_TRUE(result);
-  EXPECT_EQ(result->task_type, "SEARCH_FLIGHTS");
-  EXPECT_EQ(result->source_domain, "example.com");
+  EXPECT_EQ(result->task_type, kTestTaskType);
+  EXPECT_EQ(result->source_domain, kTestDomain);
   ASSERT_EQ(result->attributes.size(), 1u);
-  EXPECT_EQ(result->attributes[0].key, "PRICE_MIN");
-  EXPECT_EQ(result->attributes[0].value, "100");
+  EXPECT_EQ(result->attributes[0].key, kTestAttributeKey);
+  EXPECT_EQ(result->attributes[0].value, kTestAttributeValue);
+}
+
+TEST_F(AnnotationIndexClientImplTest,
+       ExtractFilterAnnotation_HttpError_ReturnsNullopt) {
+  base::test::TestFuture<std::optional<FilterAnnotation>> future;
+
+  client_->ExtractFilterAnnotation(GURL(kTestExtractUrl), future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  SimulateHttpError(test_url_loader_factory_.GetPendingRequest(0),
+                    net::HTTP_INTERNAL_SERVER_ERROR);
+  EXPECT_FALSE(future.Take().has_value());
+}
+
+TEST_F(AnnotationIndexClientImplTest,
+       ExtractFilterAnnotation_NetworkError_ReturnsNullopt) {
+  base::test::TestFuture<std::optional<FilterAnnotation>> future;
+
+  client_->ExtractFilterAnnotation(GURL(kTestExtractUrl), future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  SimulateNetworkError(test_url_loader_factory_.GetPendingRequest(0));
+  EXPECT_FALSE(future.Take().has_value());
+}
+
+TEST_F(AnnotationIndexClientImplTest,
+       ExtractFilterAnnotation_InvalidResponse_ReturnsNullopt) {
+  base::test::TestFuture<std::optional<FilterAnnotation>> future;
+
+  client_->ExtractFilterAnnotation(GURL(kTestExtractUrl), future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  SimulateInvalidResponse(test_url_loader_factory_.GetPendingRequest(0));
+  EXPECT_FALSE(future.Take().has_value());
+}
+
+TEST_F(AnnotationIndexClientImplTest,
+       ExtractFilterAnnotation_EmptyResponse_ReturnsNullopt) {
+  base::test::TestFuture<std::optional<FilterAnnotation>> future;
+
+  client_->ExtractFilterAnnotation(GURL(kTestExtractUrl), future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  SimulateEmptyResponse(test_url_loader_factory_.GetPendingRequest(0));
+  EXPECT_FALSE(future.Take().has_value());
+}
+
+TEST_F(AnnotationIndexClientImplTest,
+       ExtractFilterAnnotation_Timeout_ReturnsNullopt) {
+  base::test::TestFuture<std::optional<FilterAnnotation>> future;
+
+  client_->ExtractFilterAnnotation(GURL(kTestExtractUrl), future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  SimulateTimeout(test_url_loader_factory_.GetPendingRequest(0));
+  EXPECT_FALSE(future.Take().has_value());
+}
+
+TEST_F(AnnotationIndexClientImplTest, ApiKeyIncludedInRequest) {
+  base::test::TestFuture<std::optional<std::vector<std::string>>> future;
+
+  client_->GetSupportedTaskTypesForDomain(kTestDomain, future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  network::TestURLLoaderFactory::PendingRequest* pending_request =
+      test_url_loader_factory_.GetPendingRequest(0);
+  EXPECT_EQ(
+      google_apis::test_util::GetAPIKeyFromRequest(pending_request->request),
+      kDummyApiKey);
+}
+
+TEST_F(AnnotationIndexClientImplTest, NoApiKeyForNonGoogleDomains) {
+  OverrideBaseUrlWithSwitch(kTestNonGoogleApiUrl);
+  base::test::TestFuture<std::optional<std::vector<std::string>>> future;
+
+  client_->GetSupportedTaskTypesForDomain(kTestDomain, future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  network::TestURLLoaderFactory::PendingRequest* pending_request =
+      test_url_loader_factory_.GetPendingRequest(0);
+  EXPECT_FALSE(google_apis::test_util::HasAPIKey(pending_request->request));
+}
+
+TEST_F(AnnotationIndexClientImplTest, BaseUrlOverriddenBySwitch) {
+  OverrideBaseUrlWithSwitch(kTestSwitchApiUrl);
+  base::test::TestFuture<std::optional<std::vector<std::string>>> future;
+
+  client_->GetSupportedTaskTypesForDomain(kTestDomain, future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  network::TestURLLoaderFactory::PendingRequest* pending_request =
+      test_url_loader_factory_.GetPendingRequest(0);
+  EXPECT_TRUE(
+      pending_request->request.url.spec().starts_with(kTestSwitchApiUrl));
+}
+
+TEST_F(AnnotationIndexClientImplTest, InvalidBaseUrlFailsQuickly) {
+  OverrideBaseUrlWithSwitch(kTestInvalidUrl);
+  base::test::TestFuture<std::optional<std::vector<std::string>>> future;
+
+  client_->GetSupportedTaskTypesForDomain(kTestDomain, future.GetCallback());
+
+  EXPECT_EQ(test_url_loader_factory_.NumPending(), 0);
+  EXPECT_FALSE(future.Take().has_value());
+}
+
+TEST_F(AnnotationIndexClientImplTest, HandlesConcurrentRequests) {
+  GetSupportedTasksResponse proto_response1 =
+      CreateSupportedTasksResponse({kTask1});
+  GetSupportedTasksResponse proto_response2 =
+      CreateSupportedTasksResponse({kTask2});
+  base::test::TestFuture<std::optional<std::vector<std::string>>> future1;
+  base::test::TestFuture<std::optional<std::vector<std::string>>> future2;
+
+  client_->GetSupportedTaskTypesForDomain(kTestDomain1, future1.GetCallback());
+  client_->GetSupportedTaskTypesForDomain(kTestDomain2, future2.GetCallback());
+
+  EXPECT_EQ(test_url_loader_factory_.NumPending(), 2);
+  test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+      test_url_loader_factory_.GetPendingRequest(0),
+      proto_response1.SerializeAsString());
+  test_url_loader_factory_.SimulateResponseWithoutRemovingFromPendingList(
+      test_url_loader_factory_.GetPendingRequest(1),
+      proto_response2.SerializeAsString());
+  auto result1 = future1.Take();
+  ASSERT_TRUE(result1);
+  ASSERT_EQ(result1->size(), 1u);
+  EXPECT_EQ((*result1)[0], kTask1);
+  auto result2 = future2.Take();
+  ASSERT_TRUE(result2);
+  ASSERT_EQ(result2->size(), 1u);
+  EXPECT_EQ((*result2)[0], kTask2);
+}
+
+TEST_F(AnnotationIndexClientImplTest, LoaderCleanedUpAfterCompletion) {
+  base::test::TestFuture<std::optional<std::vector<std::string>>> future;
+
+  client_->GetSupportedTaskTypesForDomain(kTestDomain, future.GetCallback());
+
+  ASSERT_EQ(test_url_loader_factory_.NumPending(), 1);
+  SimulateEmptyResponse(test_url_loader_factory_.GetPendingRequest(0));
+  EXPECT_TRUE(future.Take().has_value());
 }
 
 }  // namespace
diff --git a/components/one_time_tokens/core/browser/BUILD.gn b/components/one_time_tokens/core/browser/BUILD.gn
index 4203215..c2420ec 100644
--- a/components/one_time_tokens/core/browser/BUILD.gn
+++ b/components/one_time_tokens/core/browser/BUILD.gn
@@ -5,8 +5,11 @@
 
 static_library("browser") {
   sources = [
+    "email_one_time_token_fetch_coordinator.cc",
+    "email_one_time_token_fetch_coordinator.h",
     "email_one_time_token_fetcher.cc",
     "email_one_time_token_fetcher.h",
+    "encrypted_message_reference.h",
     "gmail_otp_backend.cc",
     "gmail_otp_backend.h",
     "one_time_token.cc",
@@ -55,6 +58,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "email_one_time_token_fetch_coordinator_unittest.cc",
     "email_one_time_token_fetcher_unittest.cc",
     "gmail_otp_backend_unittest.cc",
     "one_time_token_cache_unittest.cc",
diff --git a/components/one_time_tokens/core/browser/email_one_time_token_fetch_coordinator.cc b/components/one_time_tokens/core/browser/email_one_time_token_fetch_coordinator.cc
new file mode 100644
index 0000000..aafa32d
--- /dev/null
+++ b/components/one_time_tokens/core/browser/email_one_time_token_fetch_coordinator.cc
@@ -0,0 +1,27 @@
+// 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 "components/one_time_tokens/core/browser/email_one_time_token_fetch_coordinator.h"
+
+namespace one_time_tokens {
+
+EmailOneTimeTokenFetchCoordinator::EmailOneTimeTokenFetchCoordinator(
+    Delegate& delegate)
+    : delegate_(delegate) {}
+
+EmailOneTimeTokenFetchCoordinator::~EmailOneTimeTokenFetchCoordinator() =
+    default;
+
+void EmailOneTimeTokenFetchCoordinator::SignalNetworkRequestNeeded(
+    const EncryptedMessageReference& reference) {
+  // TODO(crbug.com/478840436): Replace pass-through with real implementation.
+  delegate_->OnCanSendNetworkRequest(reference);
+}
+
+void EmailOneTimeTokenFetchCoordinator::InformOfNetworkRequestFinished(
+    const EncryptedMessageReference& reference) {
+  // TODO(crbug.com/478840436): Implement.
+}
+
+}  // namespace one_time_tokens
diff --git a/components/one_time_tokens/core/browser/email_one_time_token_fetch_coordinator.h b/components/one_time_tokens/core/browser/email_one_time_token_fetch_coordinator.h
new file mode 100644
index 0000000..83b5cde
--- /dev/null
+++ b/components/one_time_tokens/core/browser/email_one_time_token_fetch_coordinator.h
@@ -0,0 +1,48 @@
+// 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 COMPONENTS_ONE_TIME_TOKENS_CORE_BROWSER_EMAIL_ONE_TIME_TOKEN_FETCH_COORDINATOR_H_
+#define COMPONENTS_ONE_TIME_TOKENS_CORE_BROWSER_EMAIL_ONE_TIME_TOKEN_FETCH_COORDINATOR_H_
+
+#include "base/memory/raw_ref.h"
+#include "components/one_time_tokens/core/browser/encrypted_message_reference.h"
+
+namespace one_time_tokens {
+
+// Coordinates the lifecycle of EmailOneTimeToken requests, including
+// concurrency control and de-duplication.
+class EmailOneTimeTokenFetchCoordinator {
+ public:
+  // Delegate interface to be implemented by GmailOtpBackendImpl.
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+
+    // Called when the coordinator authorizes a network request to be sent.
+    virtual void OnCanSendNetworkRequest(
+        const EncryptedMessageReference& reference) = 0;
+  };
+
+  explicit EmailOneTimeTokenFetchCoordinator(Delegate& delegate);
+  EmailOneTimeTokenFetchCoordinator(const EmailOneTimeTokenFetchCoordinator&) =
+      delete;
+  EmailOneTimeTokenFetchCoordinator& operator=(
+      const EmailOneTimeTokenFetchCoordinator&) = delete;
+  ~EmailOneTimeTokenFetchCoordinator();
+
+  // Signals that a network request is needed for a specific reference.
+  void SignalNetworkRequestNeeded(const EncryptedMessageReference& reference);
+
+  // Informs the coordinator that a network request for a specific reference
+  // has finished.
+  void InformOfNetworkRequestFinished(
+      const EncryptedMessageReference& reference);
+
+ private:
+  const raw_ref<Delegate> delegate_;
+};
+
+}  // namespace one_time_tokens
+
+#endif  // COMPONENTS_ONE_TIME_TOKENS_CORE_BROWSER_EMAIL_ONE_TIME_TOKEN_FETCH_COORDINATOR_H_
diff --git a/components/one_time_tokens/core/browser/email_one_time_token_fetch_coordinator_unittest.cc b/components/one_time_tokens/core/browser/email_one_time_token_fetch_coordinator_unittest.cc
new file mode 100644
index 0000000..99d6514
--- /dev/null
+++ b/components/one_time_tokens/core/browser/email_one_time_token_fetch_coordinator_unittest.cc
@@ -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.
+
+#include "components/one_time_tokens/core/browser/email_one_time_token_fetch_coordinator.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace one_time_tokens {
+
+namespace {
+
+class MockDelegate : public EmailOneTimeTokenFetchCoordinator::Delegate {
+ public:
+  MOCK_METHOD(void,
+              OnCanSendNetworkRequest,
+              (const EncryptedMessageReference& reference),
+              (override));
+};
+
+class EmailOneTimeTokenFetchCoordinatorTest : public testing::Test {
+ public:
+  EmailOneTimeTokenFetchCoordinatorTest() : coordinator_(mock_delegate_) {}
+
+ protected:
+  MockDelegate mock_delegate_;
+  EmailOneTimeTokenFetchCoordinator coordinator_;
+};
+
+// Tests that the coordinator signals the delegate when a request is needed.
+// This confirms the current pass-through behavior.
+TEST_F(EmailOneTimeTokenFetchCoordinatorTest, SignalNetworkRequestNeeded) {
+  const EncryptedMessageReference reference("test_reference");
+  EXPECT_CALL(mock_delegate_, OnCanSendNetworkRequest(testing::Eq(reference)));
+
+  coordinator_.SignalNetworkRequestNeeded(reference);
+}
+
+}  // namespace
+
+}  // namespace one_time_tokens
diff --git a/components/one_time_tokens/core/browser/encrypted_message_reference.h b/components/one_time_tokens/core/browser/encrypted_message_reference.h
new file mode 100644
index 0000000..dbfcc9ca
--- /dev/null
+++ b/components/one_time_tokens/core/browser/encrypted_message_reference.h
@@ -0,0 +1,19 @@
+// 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 COMPONENTS_ONE_TIME_TOKENS_CORE_BROWSER_ENCRYPTED_MESSAGE_REFERENCE_H_
+#define COMPONENTS_ONE_TIME_TOKENS_CORE_BROWSER_ENCRYPTED_MESSAGE_REFERENCE_H_
+
+#include <string>
+
+#include "base/types/strong_alias.h"
+
+namespace one_time_tokens {
+
+using EncryptedMessageReference =
+    base::StrongAlias<class EncryptedMessageReferenceTag, std::string>;
+
+}  // namespace one_time_tokens
+
+#endif  // COMPONENTS_ONE_TIME_TOKENS_CORE_BROWSER_ENCRYPTED_MESSAGE_REFERENCE_H_
diff --git a/components/one_time_tokens/core/browser/gmail_otp_backend.cc b/components/one_time_tokens/core/browser/gmail_otp_backend.cc
index e8514d8..f5da1113 100644
--- a/components/one_time_tokens/core/browser/gmail_otp_backend.cc
+++ b/components/one_time_tokens/core/browser/gmail_otp_backend.cc
@@ -26,7 +26,10 @@
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     signin::IdentityManager& identity_manager)
     : url_loader_factory_(std::move(url_loader_factory)),
-      identity_manager_(identity_manager) {}
+      identity_manager_(identity_manager),
+      coordinator_(std::make_unique<EmailOneTimeTokenFetchCoordinator>(*this)) {
+}
+
 GmailOtpBackendImpl::~GmailOtpBackendImpl() = default;
 
 ExpiringSubscription GmailOtpBackendImpl::Subscribe(base::Time expiration,
@@ -39,14 +42,17 @@
 }
 
 void GmailOtpBackendImpl::OnIncomingOneTimeTokenBackendTickle(
-    const GmailOtpBackendImpl::EncryptedMessageReference&
-        encrypted_message_reference) {
-  RetrieveGmailOtp(encrypted_message_reference);
+    const EncryptedMessageReference& encrypted_message_reference) {
+  coordinator_->SignalNetworkRequestNeeded(encrypted_message_reference);
+}
+
+void GmailOtpBackendImpl::OnCanSendNetworkRequest(
+    const EncryptedMessageReference& reference) {
+  RetrieveGmailOtp(reference);
 }
 
 void GmailOtpBackendImpl::RetrieveGmailOtp(
-    const GmailOtpBackendImpl::EncryptedMessageReference&
-        encrypted_message_reference) {
+    const EncryptedMessageReference& encrypted_message_reference) {
   // TODO(crbug.com/478840436) Fix the race condition where a second tickle
   // arrives while a pending request is in flight. The solution is probably
   // just to remove the has_pending_request_ from this class. Unlike SMS OTPs
@@ -70,6 +76,9 @@
     std::unique_ptr<EmailOneTimeTokenFetcher> request,
     base::expected<OneTimeToken, OneTimeTokenRetrievalError> reply) {
   has_pending_request_ = false;
+
+  // TODO(crbug.com/478840436): Inform coordinator about finished request.
+
   if (!reply.has_value()) {
     subscription_manager_.Notify(base::unexpected(reply.error()));
     return;
diff --git a/components/one_time_tokens/core/browser/gmail_otp_backend.h b/components/one_time_tokens/core/browser/gmail_otp_backend.h
index 620d7c4e..2c0ba7e 100644
--- a/components/one_time_tokens/core/browser/gmail_otp_backend.h
+++ b/components/one_time_tokens/core/browser/gmail_otp_backend.h
@@ -5,13 +5,14 @@
 #ifndef COMPONENTS_ONE_TIME_TOKENS_CORE_BROWSER_GMAIL_OTP_BACKEND_H_
 #define COMPONENTS_ONE_TIME_TOKENS_CORE_BROWSER_GMAIL_OTP_BACKEND_H_
 
-#include <string>
+#include <memory>
 
 #include "base/functional/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/types/expected.h"
-#include "base/types/strong_alias.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/one_time_tokens/core/browser/email_one_time_token_fetch_coordinator.h"
+#include "components/one_time_tokens/core/browser/encrypted_message_reference.h"
 #include "components/one_time_tokens/core/browser/one_time_token.h"
 #include "components/one_time_tokens/core/browser/one_time_token_retrieval_error.h"
 #include "components/one_time_tokens/core/browser/util/expiring_subscription.h"
@@ -36,9 +37,6 @@
       void(base::expected<OneTimeToken, OneTimeTokenRetrievalError>);
   using Callback = base::RepeatingCallback<CallbackSignature>;
 
-  using EncryptedMessageReference =
-      base::StrongAlias<class EncryptedMessageReferenceTag, std::string>;
-
   ~GmailOtpBackend() override;
 
   // Creates a new instance of the backend.
@@ -58,7 +56,8 @@
 // Concrete implementation of GmailOtpBackend that provides a fake OTP
 // response. This is intended for use in testing and development environments
 // where a real backend is not available.
-class GmailOtpBackendImpl : public GmailOtpBackend {
+class GmailOtpBackendImpl : public GmailOtpBackend,
+                            public EmailOneTimeTokenFetchCoordinator::Delegate {
  public:
   GmailOtpBackendImpl(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
@@ -69,12 +68,14 @@
                                  Callback callback) override;
 
   void OnIncomingOneTimeTokenBackendTickle(
-      const GmailOtpBackend::EncryptedMessageReference&
-          encrypted_message_reference) override;
+      const EncryptedMessageReference& encrypted_message_reference) override;
+
+  void OnCanSendNetworkRequest(
+      const EncryptedMessageReference& reference) override;
 
  private:
-  void RetrieveGmailOtp(const GmailOtpBackendImpl::EncryptedMessageReference&
-                            encrypted_message_reference);
+  void RetrieveGmailOtp(
+      const EncryptedMessageReference& encrypted_message_reference);
 
   void OnResponseFromGmailOtpBackend(
       std::unique_ptr<EmailOneTimeTokenFetcher> request,
@@ -87,6 +88,9 @@
   // Handles subscriptions to the `GmailOtpBackend`.
   ExpiringSubscriptionManager<CallbackSignature> subscription_manager_;
 
+  // Policy for coordinating network requests.
+  std::unique_ptr<EmailOneTimeTokenFetchCoordinator> coordinator_;
+
   // Indicates whether there is currently a request in flight to retrieve a
   // Gmail OTP. This prevents multiple concurrent requests. Timeouts for the OTP
   // itself are handled by the consumer of the ExpiringSubscription, not by this
diff --git a/components/one_time_tokens/core/browser/gmail_otp_backend_unittest.cc b/components/one_time_tokens/core/browser/gmail_otp_backend_unittest.cc
index 405a1ab..d3970e4 100644
--- a/components/one_time_tokens/core/browser/gmail_otp_backend_unittest.cc
+++ b/components/one_time_tokens/core/browser/gmail_otp_backend_unittest.cc
@@ -48,7 +48,7 @@
       base::Time::Now() + base::Minutes(1), future.GetRepeatingCallback());
 
   backend_.OnIncomingOneTimeTokenBackendTickle(
-      GmailOtpBackend::EncryptedMessageReference("encrypted_reference"));
+      EncryptedMessageReference("encrypted_reference"));
   identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
       "access_token", base::Time::Now() + base::Hours(1));
 
@@ -81,7 +81,7 @@
   // No subscription created.
 
   backend_.OnIncomingOneTimeTokenBackendTickle(
-      GmailOtpBackend::EncryptedMessageReference("encrypted_reference"));
+      EncryptedMessageReference("encrypted_reference"));
 
   // Verify that no network request was made.
   EXPECT_EQ(test_url_loader_factory_.NumPending(), 0);
@@ -101,14 +101,14 @@
 
   // First tickle starts a request.
   backend_.OnIncomingOneTimeTokenBackendTickle(
-      GmailOtpBackend::EncryptedMessageReference("ref1"));
+      EncryptedMessageReference("ref1"));
   identity_test_env_.WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
       "access_token", base::Time::Now() + base::Hours(1));
   EXPECT_EQ(test_url_loader_factory_.NumPending(), 1);
 
   // Second tickle should be ignored while a request is pending.
   backend_.OnIncomingOneTimeTokenBackendTickle(
-      GmailOtpBackend::EncryptedMessageReference("ref2"));
+      EncryptedMessageReference("ref2"));
   EXPECT_EQ(test_url_loader_factory_.NumPending(), 1);
 
   // Complete the pending request to avoid dangling pointers at test end.
diff --git a/components/one_time_tokens/core/browser/one_time_token_service_impl_unittest.cc b/components/one_time_tokens/core/browser/one_time_token_service_impl_unittest.cc
index e3620824..c830594 100644
--- a/components/one_time_tokens/core/browser/one_time_token_service_impl_unittest.cc
+++ b/components/one_time_tokens/core/browser/one_time_token_service_impl_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "base/types/expected.h"
+#include "components/one_time_tokens/core/browser/encrypted_message_reference.h"
 #include "components/one_time_tokens/core/browser/gmail_otp_backend.h"
 #include "components/one_time_tokens/core/browser/one_time_token.h"
 #include "components/one_time_tokens/core/browser/sms_otp_backend.h"
@@ -86,11 +87,10 @@
               (base::Time expiration, Callback callback),
               (override));
 
-  MOCK_METHOD(
-      void,
-      OnIncomingOneTimeTokenBackendTickle,
-      (const GmailOtpBackend::EncryptedMessageReference& encrypted_message_reference),
-      (override));
+  MOCK_METHOD(void,
+              OnIncomingOneTimeTokenBackendTickle,
+              (const EncryptedMessageReference& encrypted_message_reference),
+              (override));
 
   // Simulates the reception of an OTP. This will run all pending callbacks from
   // `Subscribe`.
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index 1feab29..003f3f9 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit 1feab29cd0b19345316dcba75410882454da4139
+Subproject commit 003f3f9444e566f3362c7d550ad394573136df35
diff --git a/components/permissions/permission_decision_auto_blocker.cc b/components/permissions/permission_decision_auto_blocker.cc
index 461c63d8..5cbea383 100644
--- a/components/permissions/permission_decision_auto_blocker.cc
+++ b/components/permissions/permission_decision_auto_blocker.cc
@@ -38,7 +38,7 @@
 #if BUILDFLAG(IS_ANDROID)
 // The number of times that users may ignore a permission prompt from an origin.
 // before it is automatically blocked. This is used for the Clapper UI.
-constexpr int kClapperIgnoresBeforeBlock = 1;
+constexpr int kClapperIgnoresBeforeBlock = 2;
 #endif
 
 // The number of times that users may dismiss a permission prompt that uses the
@@ -424,6 +424,7 @@
 #if BUILDFLAG(IS_ANDROID)
   if (base::FeatureList::IsEnabled(
           permissions::kPermissionsAndroidClapperLoud) &&
+      permission == ContentSettingsType::NOTIFICATIONS &&
       !ignored_prompt_was_quiet) {
     ignores_before_block = kClapperIgnoresBeforeBlock;
   }
diff --git a/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.cc b/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.cc
index 27bd3a7..3434dc2 100644
--- a/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.cc
+++ b/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.cc
@@ -79,7 +79,6 @@
       base::OnceCallback<void(const std::optional<std::string>&,
                               int,
                               int,
-                              scoped_refptr<net::HttpResponseHeaders>,
                               bool)>;
 
   explicit ObliviousHttpClient(OnCompletedCallback callback)
@@ -88,7 +87,7 @@
   ~ObliviousHttpClient() override {
     if (!called_) {
       std::move(callback_).Run(std::nullopt, net::ERR_FAILED,
-                               /*response_code=*/0, /*headers=*/nullptr,
+                               /*response_code=*/0,
                                /*ohttp_client_destructed_early=*/true);
     }
   }
@@ -104,7 +103,6 @@
     std::optional<std::string> response_body;
     int net_error;
     int response_code;
-    scoped_refptr<net::HttpResponseHeaders> response_headers;
     std::string histogram_suffix;
     if (status->is_net_error()) {
       net_error = status->get_net_error();
@@ -116,7 +114,6 @@
       histogram_suffix = "OuterResponseResult";
     } else {
       DCHECK(status->is_inner_response());
-      response_headers = std::move(status->get_inner_response()->headers);
       histogram_suffix = "InnerResponseResult";
       if (status->get_inner_response()->response_code != net::HTTP_OK) {
         net_error = net::ERR_HTTP_RESPONSE_CODE_FAILURE;
@@ -131,7 +128,6 @@
         ("SafeBrowsing.HPRT.Network." + histogram_suffix).c_str(), net_error,
         response_code);
     std::move(callback_).Run(response_body, net_error, response_code,
-                             response_headers,
                              /*ohttp_client_destructed_early=*/false);
   }
 
@@ -401,9 +397,8 @@
     const std::optional<std::string>& response_body,
     int net_error,
     int response_code,
-    scoped_refptr<net::HttpResponseHeaders> headers,
     bool ohttp_client_destructed_early) {
-  ohttp_key_service_->NotifyLookupResponse(ohttp_key, response_code, headers);
+  ohttp_key_service_->NotifyLookupResponse(ohttp_key, response_code);
 
   auto response_body_ptr =
       std::make_unique<std::string>(response_body.value_or(""));
diff --git a/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.h b/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.h
index 1415115..de0f014 100644
--- a/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.h
+++ b/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.h
@@ -29,7 +29,6 @@
 
 namespace net {
 struct NetworkTrafficAnnotationTag;
-class HttpResponseHeaders;
 }
 
 namespace safe_browsing {
@@ -223,7 +222,7 @@
 
   // Callback for requests sent via OHTTP. Most parameters are used by
   // |OnURLLoaderComplete|, see the description above |OnURLLoaderComplete| for
-  // details. |response_body|, |net_error|, |response_code|, |headers|, and
+  // details. |response_body|, |net_error|, |response_code|, and
   // |ohttp_client_destructed_early| are returned from the OHTTP client.
   // |ohttp_key| is sent to the key service.
   void OnOhttpComplete(const GURL& url,
@@ -237,7 +236,6 @@
                        const std::optional<std::string>& response_body,
                        int net_error,
                        int response_code,
-                       scoped_refptr<net::HttpResponseHeaders> headers,
                        bool ohttp_client_destructed_early);
 
   // Called when the response from the Safe Browsing V5 remote endpoint is
diff --git a/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service_unittest.cc b/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service_unittest.cc
index f89356a..3e0cf89 100644
--- a/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service_unittest.cc
+++ b/components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service_unittest.cc
@@ -147,10 +147,8 @@
     ohttp_key_ = ohttp_key;
   }
 
-  void NotifyLookupResponse(
-      const std::string& key,
-      int response_code,
-      scoped_refptr<net::HttpResponseHeaders> headers) override {
+  void NotifyLookupResponse(const std::string& key,
+                            int response_code) override {
     lookup_response_notified_ = true;
   }
 
diff --git a/components/safe_browsing/core/browser/hashprefix_realtime/ohttp_key_service.cc b/components/safe_browsing/core/browser/hashprefix_realtime/ohttp_key_service.cc
index 3c06cd9..920c85ae 100644
--- a/components/safe_browsing/core/browser/hashprefix_realtime/ohttp_key_service.cc
+++ b/components/safe_browsing/core/browser/hashprefix_realtime/ohttp_key_service.cc
@@ -62,10 +62,6 @@
 constexpr net::HttpStatusCode kKeyRelatedHttpErrorCode =
     net::HTTP_UNPROCESSABLE_CONTENT;
 
-// The header that the server sets if the server is able to decrypt the request,
-// but the key is outdated.
-constexpr char kKeyRotatedHeader[] = "X-OhttpPublickey-Rotated";
-
 // The maximum delayed time to fetch a new key if the key fetch is triggered
 // by the server.
 constexpr int kServerTriggeredFetchMaxDelayTimeSec = 60;
@@ -255,10 +251,8 @@
              FetchTriggerReason::kDuringHashRealTimeLookup);
 }
 
-void OhttpKeyService::NotifyLookupResponse(
-    const std::string& key,
-    int response_code,
-    scoped_refptr<net::HttpResponseHeaders> headers) {
+void OhttpKeyService::NotifyLookupResponse(const std::string& key,
+                                           int response_code) {
   // Skip server triggered fetch if:
   //   * The service is disabled. OR
   //   * The fetch is already scheduled. OR
@@ -295,21 +289,6 @@
             base::RandIntInclusive(0, kServerTriggeredFetchMaxDelayTimeSec)));
     return;
   }
-
-  if (response_code == net::HTTP_OK && headers &&
-      headers->HasHeader(kKeyRotatedHeader)) {
-    server_triggered_fetch_scheduled_ = true;
-    // The key is still valid, but it is close to expiration. It is a soft
-    // failure, so do not clear the key immediately.
-    base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
-        FROM_HERE,
-        base::BindOnce(&OhttpKeyService::MaybeStartServerTriggeredFetch,
-                       weak_factory_.GetWeakPtr(), key,
-                       FetchTriggerReason::kKeyRotatedHeader),
-        base::Seconds(
-            base::RandIntInclusive(0, kServerTriggeredFetchMaxDelayTimeSec)));
-    return;
-  }
 }
 
 void OhttpKeyService::StartFetch(Callback callback,
diff --git a/components/safe_browsing/core/browser/hashprefix_realtime/ohttp_key_service.h b/components/safe_browsing/core/browser/hashprefix_realtime/ohttp_key_service.h
index 6b6a4963..4f25b17 100644
--- a/components/safe_browsing/core/browser/hashprefix_realtime/ohttp_key_service.h
+++ b/components/safe_browsing/core/browser/hashprefix_realtime/ohttp_key_service.h
@@ -19,10 +19,6 @@
 
 class PrefService;
 
-namespace net {
-class HttpResponseHeaders;
-}  // namespace net
-
 namespace network {
 class SharedURLLoaderFactory;
 class SimpleURLLoader;
@@ -58,10 +54,10 @@
     // The key fetch is triggered because the response from real-time lookup
     // contains key related error code.
     kKeyRelatedHttpErrorCode = 2,
-    // The key fetch is triggered because the response from real-time lookup
-    // contains key rotated header.
-    kKeyRotatedHeader = 3,
-    kMaxValue = kKeyRotatedHeader
+    // OBSOLETE: The key fetch is triggered because the response from real-time
+    // lookup contains key rotated header.
+    kObsoleteKeyRotatedHeader = 3,
+    kMaxValue = kObsoleteKeyRotatedHeader
   };
 
   // The outcome of a key fetch.
@@ -103,13 +99,10 @@
   virtual void GetOhttpKey(Callback callback);
 
   // Notifies the key service with the response from the lookup request. |key|
-  // is used for the lookup request, |response_code| and |headers| are returned
+  // is used for the lookup request, and |response_code| is returned
   // from the lookup server. It may trigger a key fetch if the response contains
-  // key related error or header. This function is overridden in tests.
-  virtual void NotifyLookupResponse(
-      const std::string& key,
-      int response_code,
-      scoped_refptr<net::HttpResponseHeaders> headers);
+  // key related error. This function is overridden in tests.
+  virtual void NotifyLookupResponse(const std::string& key, int response_code);
 
   // KeyedService:
   // Called before the actual deletion of the object.
diff --git a/components/safe_browsing/core/browser/hashprefix_realtime/ohttp_key_service_unittest.cc b/components/safe_browsing/core/browser/hashprefix_realtime/ohttp_key_service_unittest.cc
index 49ecdaf..d31794f 100644
--- a/components/safe_browsing/core/browser/hashprefix_realtime/ohttp_key_service_unittest.cc
+++ b/components/safe_browsing/core/browser/hashprefix_realtime/ohttp_key_service_unittest.cc
@@ -64,16 +64,6 @@
 constexpr char kExpectedKeyFetchServerUrl[] =
     "https://safebrowsingohttpgateway.googleapis.com/v1/ohttp/hpkekeyconfig";
 
-scoped_refptr<net::HttpResponseHeaders> CreateSuccessHeaders() {
-  return net::HttpResponseHeaders::TryToCreate("HTTP/1.1 200 OK\r\n");
-}
-
-scoped_refptr<net::HttpResponseHeaders> CreateKeyRotatedHeaders() {
-  return net::HttpResponseHeaders::TryToCreate(
-      "HTTP/1.1 200 OK\r\n"
-      "X-OhttpPublickey-Rotated: yes\r\n");
-}
-
 }  // namespace
 
 class OhttpKeyServiceTest : public ::testing::Test {
@@ -848,42 +838,7 @@
 
 TEST_F(OhttpKeyServiceTest, NotifyLookupResponse_SuccessFetch) {
   SetupOldKeyAndPendingNewKey();
-  ohttp_key_service_->NotifyLookupResponse(TestOldOhttpKey(), net::HTTP_OK,
-                                           CreateSuccessHeaders());
-  FastForwardAndVerifyKeyValue(TestOldOhttpKey());
-}
-
-TEST_F(OhttpKeyServiceTest, NotifyLookupResponse_HeaderHint) {
-  SetupOldKeyAndPendingNewKey();
-  ohttp_key_service_->NotifyLookupResponse(TestOldOhttpKey(), net::HTTP_OK,
-                                           CreateKeyRotatedHeaders());
-  // Header hint is soft failure, the key is not immediately cleared.
-  EXPECT_EQ(ohttp_key_service_->get_ohttp_key_for_testing()->key,
-            TestOldOhttpKey());
-
-  FastForwardAndVerifyKeyValue(TestNewOhttpKey());
-
-  histogram_tester_.ExpectBucketCount(
-      "SafeBrowsing.HPRT.OhttpKeyService.FetchKeyTriggerReason",
-      /*sample=*/OhttpKeyService::FetchTriggerReason::kKeyRotatedHeader,
-      /*expected_count=*/1);
-}
-
-TEST_F(OhttpKeyServiceTest, NotifyLookupResponse_HeaderHintOnDifferentKey) {
-  SetupOldKeyAndPendingNewKey();
-  ohttp_key_service_->NotifyLookupResponse(TestOhttpKey(), net::HTTP_OK,
-                                           CreateKeyRotatedHeaders());
-
-  // The key is not updated because the server hint is on a different key.
-  FastForwardAndVerifyKeyValue(TestOldOhttpKey());
-}
-
-TEST_F(OhttpKeyServiceTest, NotifyLookupResponse_HeaderHintWithError) {
-  SetupOldKeyAndPendingNewKey();
-
-  ohttp_key_service_->NotifyLookupResponse(
-      TestOldOhttpKey(), net::HTTP_FORBIDDEN, CreateKeyRotatedHeaders());
-  // Header hint should only take effect when the response code is 200.
+  ohttp_key_service_->NotifyLookupResponse(TestOldOhttpKey(), net::HTTP_OK);
   FastForwardAndVerifyKeyValue(TestOldOhttpKey());
 }
 
@@ -891,8 +846,8 @@
   SetupOldKeyAndPendingNewKey();
   task_environment_.RunUntilIdle();
 
-  ohttp_key_service_->NotifyLookupResponse(
-      TestOldOhttpKey(), net::HTTP_UNPROCESSABLE_CONTENT, /*headers=*/nullptr);
+  ohttp_key_service_->NotifyLookupResponse(TestOldOhttpKey(),
+                                           net::HTTP_UNPROCESSABLE_CONTENT);
   // HTTP status error is a hard failure, the key should be cleared immediately.
   EXPECT_FALSE(ohttp_key_service_->get_ohttp_key_for_testing().has_value());
 
@@ -913,11 +868,18 @@
   task_environment_.RunUntilIdle();
   SetupSuccessResponse();
   SetupOldKeyAndPendingNewKey();
-  ohttp_key_service_->NotifyLookupResponse(TestOldOhttpKey(), net::HTTP_OK,
-                                           CreateKeyRotatedHeaders());
+  ohttp_key_service_->NotifyLookupResponse(TestOldOhttpKey(),
+                                           net::HTTP_UNPROCESSABLE_CONTENT);
 
-  // Still returns old key because the service is in backoff mode.
-  FastForwardAndVerifyKeyValue(TestOldOhttpKey());
+  // HTTP_UNPROCESSABLE_CONTENT is a hard failure, so the key is cleared
+  // immediately.
+  EXPECT_FALSE(ohttp_key_service_->get_ohttp_key_for_testing().has_value());
+
+  task_environment_.FastForwardBy(base::Minutes(1));
+  task_environment_.RunUntilIdle();
+
+  // Still empty because the service is in backoff mode and fetch fails.
+  EXPECT_FALSE(ohttp_key_service_->get_ohttp_key_for_testing().has_value());
 }
 
 TEST_F(OhttpKeyServiceTest,
@@ -927,22 +889,20 @@
   SetupOldKeyAndPendingNewKey();
   task_environment_.RunUntilIdle();
 
-  ohttp_key_service_->NotifyLookupResponse(
-      TestOldOhttpKey(), net::HTTP_UNPROCESSABLE_CONTENT, /*headers=*/nullptr);
+  ohttp_key_service_->NotifyLookupResponse(TestOldOhttpKey(),
+                                           net::HTTP_UNPROCESSABLE_CONTENT);
   // Histogram is not logged because it is not a new key.
   histogram_tester_.ExpectTotalCount(kFirstLookupHistogramName,
                                      /*expected_count=*/0);
 
   FastForwardAndVerifyKeyValue(TestNewOhttpKey());
 
-  ohttp_key_service_->NotifyLookupResponse(TestNewOhttpKey(), net::HTTP_OK,
-                                           CreateSuccessHeaders());
+  ohttp_key_service_->NotifyLookupResponse(TestNewOhttpKey(), net::HTTP_OK);
   histogram_tester_.ExpectUniqueSample(kFirstLookupHistogramName,
                                        /*sample=*/net::HTTP_OK,
                                        /*expected_bucket_count=*/1);
 
-  ohttp_key_service_->NotifyLookupResponse(TestNewOhttpKey(), net::HTTP_OK,
-                                           CreateSuccessHeaders());
+  ohttp_key_service_->NotifyLookupResponse(TestNewOhttpKey(), net::HTTP_OK);
   // Histogram is not logged again because it is not the first lookup response
   // with this key.
   histogram_tester_.ExpectTotalCount(kFirstLookupHistogramName,
diff --git a/components/search_engines/template_url.h b/components/search_engines/template_url.h
index ee3fe0c..a869959 100644
--- a/components/search_engines/template_url.h
+++ b/components/search_engines/template_url.h
@@ -902,8 +902,12 @@
   // OMNIBOX_API_EXTENSION.
   std::string GetExtensionId() const;
 
+  // Returns the resource ID base associated with this template URL, if it is
+  // provided from built-in data.
+  std::optional<std::string_view> GetBaseBuiltinResourceId() const;
+
   // Returns the resource ID for the logo (small / favicon style) associated
-  // with this template URL, or an empty string if none is associated with it.
+  // with this template URL, or a default image if none is associated with it.
   std::string GetBuiltinImageResourceId() const;
 
   // Returns the resource ID for the search engine description string associated
@@ -1068,10 +1072,6 @@
                             url::Parsed::ComponentType* search_terms_component,
                             url::Component* search_terms_position) const;
 
-  // Returns the resource ID base associated with this template URL, if it is
-  // provided from built-in data.
-  std::optional<std::string_view> GetBaseBuiltinResourceId() const;
-
   // Returns the built-in marketing snippet string for the search engine, or
   // `std::nullopt` if a marketing snippets are not included in this build of
   // unavailable for this search engine.
diff --git a/components/send_tab_to_self/fake_send_tab_to_self_model.cc b/components/send_tab_to_self/fake_send_tab_to_self_model.cc
index a787246..da31c38f 100644
--- a/components/send_tab_to_self/fake_send_tab_to_self_model.cc
+++ b/components/send_tab_to_self/fake_send_tab_to_self_model.cc
@@ -71,14 +71,6 @@
   return result;
 }
 
-void FakeSendTabToSelfModel::DeleteEntry(const std::string& guid) {
-  if (entries_.erase(guid)) {
-    for (auto& observer : observers_) {
-      observer.EntriesRemovedRemotely({guid});
-    }
-  }
-}
-
 void FakeSendTabToSelfModel::DismissEntry(const std::string& guid) {
   last_dismissed_guid_ = guid;
   std::map<std::string, std::unique_ptr<SendTabToSelfEntry>>::iterator it =
diff --git a/components/send_tab_to_self/fake_send_tab_to_self_model.h b/components/send_tab_to_self/fake_send_tab_to_self_model.h
index 4c611a1c..8d9c393b 100644
--- a/components/send_tab_to_self/fake_send_tab_to_self_model.h
+++ b/components/send_tab_to_self/fake_send_tab_to_self_model.h
@@ -40,7 +40,6 @@
       NavigationHistory navigation_history,
       base::OnceCallback<void(SendTabToSelfResult)> commit_confirmation)
       override;
-  void DeleteEntry(const std::string& guid) override;
   void DismissEntry(const std::string& guid) override;
   void MarkEntryOpened(const std::string& guid) override;
   bool IsReady() override;
diff --git a/components/send_tab_to_self/send_tab_to_self_bridge.cc b/components/send_tab_to_self/send_tab_to_self_bridge.cc
index 309de90..26dde647 100644
--- a/components/send_tab_to_self/send_tab_to_self_bridge.cc
+++ b/components/send_tab_to_self/send_tab_to_self_bridge.cc
@@ -445,19 +445,6 @@
   return result;
 }
 
-void SendTabToSelfBridge::DeleteEntry(const std::string& guid) {
-  // Assure that an entry with that guid exists.
-  if (GetEntryByGUID(guid) == nullptr) {
-    return;
-  }
-
-  std::unique_ptr<DataTypeStore::WriteBatch> batch = store_->CreateWriteBatch();
-
-  DeleteEntryWithBatch(guid, batch.get());
-
-  Commit(std::move(batch));
-}
-
 void SendTabToSelfBridge::DismissEntry(const std::string& guid) {
   SendTabToSelfEntry* entry = GetMutableEntryByGUID(guid);
   // Assure that an entry with that guid exists.
@@ -761,21 +748,26 @@
 }
 
 void SendTabToSelfBridge::DoGarbageCollection() {
-  std::vector<std::string> removed;
+  std::vector<std::string> removed_guids;
 
-  auto entry = entries_.begin();
-  while (entry != entries_.end()) {
-    DCHECK_EQ(entry->first, entry->second->GetGUID());
+  for (const auto& it : entries_) {
+    DCHECK_EQ(it.first, it.second->GetGUID());
 
-    std::string guid = entry->first;
-    bool expired = entry->second->IsExpired(clock_->Now());
-    entry++;
-    if (expired) {
-      DeleteEntry(guid);
-      removed.push_back(guid);
+    if (it.second->IsExpired(clock_->Now())) {
+      removed_guids.push_back(it.first);
     }
   }
-  NotifyRemoteSendTabToSelfEntryDeleted(removed);
+
+  if (removed_guids.empty()) {
+    return;
+  }
+
+  std::unique_ptr<DataTypeStore::WriteBatch> batch = store_->CreateWriteBatch();
+  for (const std::string& guid : removed_guids) {
+    DeleteEntryWithBatch(guid, batch.get());
+  }
+  Commit(std::move(batch));
+  NotifyRemoteSendTabToSelfEntryDeleted(removed_guids);
 }
 
 void SendTabToSelfBridge::DeleteEntryWithBatch(
diff --git a/components/send_tab_to_self/send_tab_to_self_bridge.h b/components/send_tab_to_self/send_tab_to_self_bridge.h
index 50d21055..fe596ad 100644
--- a/components/send_tab_to_self/send_tab_to_self_bridge.h
+++ b/components/send_tab_to_self/send_tab_to_self_bridge.h
@@ -96,7 +96,6 @@
       NavigationHistory navigation_history,
       base::OnceCallback<void(SendTabToSelfResult)> commit_confirmation)
       override;
-  void DeleteEntry(const std::string& guid) override;
   void DismissEntry(const std::string& guid) override;
   void MarkEntryOpened(const std::string& guid) override;
   bool IsReady() override;
diff --git a/components/send_tab_to_self/send_tab_to_self_model.h b/components/send_tab_to_self/send_tab_to_self_model.h
index 80d5eabc..b29b2003 100644
--- a/components/send_tab_to_self/send_tab_to_self_model.h
+++ b/components/send_tab_to_self/send_tab_to_self_model.h
@@ -65,10 +65,6 @@
       NavigationHistory navigation_history,
       base::OnceCallback<void(SendTabToSelfResult)> commit_confirmation) = 0;
 
-  // Remove entry with |guid| from entries. Allows clients to modify the state
-  // of the model as driven by user behaviors.
-  virtual void DeleteEntry(const std::string& guid) = 0;
-
   // Dismiss entry with key |guid|. Allows clients to modify the state
   // of the model as driven by user behaviors.
   virtual void DismissEntry(const std::string& guid) = 0;
diff --git a/components/sharing_message/proto/BUILD.gn b/components/sharing_message/proto/BUILD.gn
index 7d133bca..d2808fe 100644
--- a/components/sharing_message/proto/BUILD.gn
+++ b/components/sharing_message/proto/BUILD.gn
@@ -20,5 +20,6 @@
     "sharing_message.proto",
     "sharing_message_type.proto",
     "sms_fetch_message_test_proto3_optional.proto",
+    "timestamp.proto",
   ]
 }
diff --git a/components/sharing_message/proto/one_time_token_backend_notification.proto b/components/sharing_message/proto/one_time_token_backend_notification.proto
index 3d08f6af..9dbcf19 100644
--- a/components/sharing_message/proto/one_time_token_backend_notification.proto
+++ b/components/sharing_message/proto/one_time_token_backend_notification.proto
@@ -12,6 +12,8 @@
 // Required in Chrome.
 option optimize_for = LITE_RUNTIME;
 
+import "timestamp.proto";
+
 // Message indicating that there's a one time token available for the client to
 // retrieve.
 // Note, that this message is designed not to carry the actual one time token,
@@ -28,4 +30,10 @@
   // Encrypted reference to the email that contains the OTP.
   // Clients should treat this as an opaque blob and pass it to the backend.
   bytes encrypted_message_reference = 1;
+
+  // Time when the OTP message was created on the email sender's side.
+  Timestamp otp_created_timestamp = 2;
+
+  // Time when the OTP message was received by the Gmail SMTP servers.
+  Timestamp email_received_timestamp = 3;
 }
diff --git a/components/sharing_message/proto/timestamp.proto b/components/sharing_message/proto/timestamp.proto
new file mode 100644
index 0000000..f247c424
--- /dev/null
+++ b/components/sharing_message/proto/timestamp.proto
@@ -0,0 +1,24 @@
+// 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.
+
+syntax = "proto3";
+
+package components_sharing_message;
+
+option optimize_for = LITE_RUNTIME;
+
+// A copy of google/protobuf/timestamp.proto, because we cannot import it.
+// This message must be wire-compatible with google/protobuf/timestamp.proto,
+// Do not edit this unless the original message has changed.
+message Timestamp {
+  // Represents seconds of UTC time since Unix epoch
+  // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
+  // 9999-12-31T23:59:59Z inclusive.
+  int64 seconds = 1;
+  // Non-negative fractions of a second at nanosecond resolution. Negative
+  // second values with fractions must still have non-negative nanos values
+  // that count forward in time. Must be from 0 to 999,999,999
+  // inclusive.
+  int32 nanos = 2;
+}
diff --git a/components/webauthn/android/webauthn_cred_man_delegate_factory.cc b/components/webauthn/android/webauthn_cred_man_delegate_factory.cc
index a5364ad..8e9bc935 100644
--- a/components/webauthn/android/webauthn_cred_man_delegate_factory.cc
+++ b/components/webauthn/android/webauthn_cred_man_delegate_factory.cc
@@ -10,7 +10,6 @@
 #include "base/containers/flat_map.h"
 #include "components/webauthn/android/webauthn_client_android.h"
 #include "components/webauthn/android/webauthn_cred_man_delegate.h"
-#include "components/webauthn/features.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
@@ -48,9 +47,7 @@
   // If the embedder did not initialize the client handling webauthn requests,
   // the delegate won't be of much use. The unavailable delegate can be used as
   // a good signal that conditional requests cannot be handled.
-  if (base::FeatureList::IsEnabled(
-          features::kWebAuthnConditionalUiSuppressedOnWebView) &&
-      !WebAuthnClientAndroid::HasClient()) {
+  if (!WebAuthnClientAndroid::HasClient()) {
     return nullptr;
   }
 
diff --git a/components/webauthn/features.cc b/components/webauthn/features.cc
index c793b399..6199edfa 100644
--- a/components/webauthn/features.cc
+++ b/components/webauthn/features.cc
@@ -24,10 +24,6 @@
              "WebAuthenticationAndroidCredManRequestExtraBundle",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Enabled by default in M145. Remove in or after M148.
-BASE_FEATURE(kWebAuthnConditionalUiSuppressedOnWebView,
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if !BUILDFLAG(IS_ANDROID)
diff --git a/components/webauthn/features.h b/components/webauthn/features.h
index b5722f48..abc37cc 100644
--- a/components/webauthn/features.h
+++ b/components/webauthn/features.h
@@ -26,11 +26,6 @@
 COMPONENT_EXPORT(WEBAUTHN)
 BASE_DECLARE_FEATURE(kWebAuthnAndroidCredManRequestExtraBundle);
 
-// This flag ensures the CredMan UI is suppressed on WebView by returning a
-// `WebAuthnCredManDelegates` only if a `WebAuthnClientAndroid` is available.
-COMPONENT_EXPORT(WEBAUTHN)
-BASE_DECLARE_FEATURE(kWebAuthnConditionalUiSuppressedOnWebView);
-
 #endif  // BUILDFLAG(IS_ANDROID)
 
 #if !BUILDFLAG(IS_ANDROID)
diff --git a/content/PRESUBMIT.py b/content/PRESUBMIT.py
new file mode 100644
index 0000000..539a9c9
--- /dev/null
+++ b/content/PRESUBMIT.py
@@ -0,0 +1,64 @@
+# 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.
+
+"""Presubmit script for content/.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+_WARN_AGAINST_DCHECK_PREFIXES = (
+    'content/browser/renderer_host/navigation_request.h',
+    'content/browser/renderer_host/navigation_request.cc',
+    'content/browser/renderer_host/render_frame_host_android.cc',
+    'content/browser/renderer_host/render_frame_host_csp_context.cc',
+    'content/browser/renderer_host/render_frame_host_factory.cc',
+    'content/browser/renderer_host/render_frame_host_impl.h',
+    'content/browser/renderer_host/render_frame_host_impl.cc',
+    'content/browser/renderer_host/render_frame_host_manager.h',
+    'content/browser/renderer_host/render_frame_host_manager.cc',
+
+    # TODO(crbug.com/497761255): Enable this for:
+    # - content/browser/ (root files)
+    # - content/browser/renderer_host/
+    # - content/browser/loader/
+    # - content/browser/network/
+    # - content/browser/security/
+)
+
+def _WarnAgainstDCHECK(input_api, output_api):
+  """
+  Warn against adding new DCHECKs. CHECKS are preferred in these files, because
+  they are cheap and cause an immediate crash, as opposed to a potential
+  vulnerability that could be exploited in the wild.
+
+  See go/check-content-navigation for details.
+  """
+  problems = []
+
+  def FilterFile(affected_file):
+    path = affected_file.LocalPath()
+    for prefix in _WARN_AGAINST_DCHECK_PREFIXES:
+      if path.startswith(prefix):
+        return True
+    return False
+
+  dcheck_re = input_api.re.compile(r'\bDCHECK(?!_IS_ON\b)')
+  for f in input_api.AffectedSourceFiles(FilterFile):
+    for line_num, line in f.ChangedContents():
+      if dcheck_re.search(line):
+        problems.append(f'{f.LocalPath()}:{line_num}: {line.strip()}')
+
+  if problems:
+    return [output_api.PresubmitPromptWarning(
+        'DCHECK is discouraged in this file. CHECKs are cheap and are '
+        'preferred when possible.',
+        problems)]
+  return []
+
+def CheckChangeOnUpload(input_api, output_api):
+  return _WarnAgainstDCHECK(input_api, output_api)
+
+def CheckChangeOnCommit(input_api, output_api):
+  return _WarnAgainstDCHECK(input_api, output_api)
diff --git a/content/PRESUBMIT_test.py b/content/PRESUBMIT_test.py
new file mode 100755
index 0000000..ce096fc
--- /dev/null
+++ b/content/PRESUBMIT_test.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+# 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 os
+import sys
+import unittest
+
+import PRESUBMIT
+
+sys.path.append(
+    os.path.join(os.path.dirname(os.path.abspath(__file__)), '..'))
+from PRESUBMIT_test_mocks import (MockInputApi, MockOutputApi, MockAffectedFile)
+
+class DCheckTest(unittest.TestCase):
+  def testDCheckInNavigationRequest(self):
+    diff = [
+        '  DCHECK(foo);',
+        '  DCHECK_EQ(foo, bar);',
+        '  DCHECK_NE(foo, bar);'
+    ]
+    input_api = MockInputApi()
+    input_api.files = [
+        MockAffectedFile(
+            'content/browser/renderer_host/navigation_request.cc', diff)
+    ]
+    errors = PRESUBMIT._WarnAgainstDCHECK(input_api, MockOutputApi())
+    self.assertEqual(1, len(errors))
+    self.assertEqual(3, len(errors[0].items))
+    self.assertIn('DCHECK is discouraged in this file', errors[0].message)
+    self.assertIn('CHECKs are cheap and are preferred when possible',
+                  errors[0].message)
+
+  def testDCheckInOtherFile(self):
+    diff = ['  DCHECK(foo);']
+    input_api = MockInputApi()
+    input_api.files = [
+        MockAffectedFile('content/browser/foo.cc', diff)
+    ]
+    errors = PRESUBMIT._WarnAgainstDCHECK(input_api, MockOutputApi())
+    self.assertEqual(0, len(errors))
+
+  def testDCheckIsOnIgnored(self):
+    diff = ['#if DCHECK_IS_ON()']
+    input_api = MockInputApi()
+    input_api.files = [
+        MockAffectedFile(
+            'content/browser/renderer_host/navigation_request.cc', diff)
+    ]
+    errors = PRESUBMIT._WarnAgainstDCHECK(input_api, MockOutputApi())
+    self.assertEqual(0, len(errors))
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/content/browser/devtools/devtools_instrumentation.cc b/content/browser/devtools/devtools_instrumentation.cc
index 932fac9..e430ad51 100644
--- a/content/browser/devtools/devtools_instrumentation.cc
+++ b/content/browser/devtools/devtools_instrumentation.cc
@@ -2571,6 +2571,13 @@
         kFormModelContextParameterMissingTitleAndDescription:
       return protocol::Audits::GenericIssueErrorTypeEnum::
           FormModelContextParameterMissingTitleAndDescription;
+    case blink::mojom::GenericIssueErrorType::kFormModelContextMissingToolName:
+      return protocol::Audits::GenericIssueErrorTypeEnum::
+          FormModelContextMissingToolName;
+    case blink::mojom::GenericIssueErrorType::
+        kFormModelContextMissingToolDescription:
+      return protocol::Audits::GenericIssueErrorTypeEnum::
+          FormModelContextMissingToolDescription;
   }
 }
 
diff --git a/content/browser/renderer_host/cookie_utils.cc b/content/browser/renderer_host/cookie_utils.cc
index d6a8e9f..d280802 100644
--- a/content/browser/renderer_host/cookie_utils.cc
+++ b/content/browser/renderer_host/cookie_utils.cc
@@ -17,6 +17,7 @@
 #include "content/browser/devtools/devtools_instrumentation.h"
 #include "content/browser/navigation_or_document_handle.h"
 #include "content/browser/renderer_host/navigation_request.h"
+#include "content/browser/renderer_host/page_impl.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/cookie_access_details.h"
@@ -557,10 +558,16 @@
     GetContentClient()->browser()->LogWebFeatureForCurrentPage(
         rfh, blink::mojom::WebFeature::kPartitionedCookies);
   }
-  if (valid_partitioned_cookies_for_ukm_exist) {
-    ukm::SourceId source_id = rfh->GetPageUkmSourceId();
-    RecordPartitionedCookieUseV3UKM(rfh, source_id);
+  if (valid_partitioned_cookies_for_ukm_exist &&
+      !rfh->IsInLifecycleState(
+          RenderFrameHost::LifecycleState::kPrerendering)) {
+    PageImpl& page = rfh->GetPage();
+    if (!page.has_recorded_partitioned_cookie_use()) {
+      RecordPartitionedCookieUseV3UKM(rfh, rfh->GetPageUkmSourceId());
+      page.set_has_recorded_partitioned_cookie_use(true);
+    }
   }
+
   if (partitioned_cookies_exist && !httponly_cookie_names.empty()) {
     bool has_shadowed_cookie = false;
     for (const auto& cookie_name : httponly_cookie_names) {
diff --git a/content/browser/renderer_host/navigation_throttle_registry_impl.cc b/content/browser/renderer_host/navigation_throttle_registry_impl.cc
index fd5859b..6f05f973 100644
--- a/content/browser/renderer_host/navigation_throttle_registry_impl.cc
+++ b/content/browser/renderer_host/navigation_throttle_registry_impl.cc
@@ -278,8 +278,9 @@
 void NavigationThrottleRegistryImpl::AddThrottle(
     std::unique_ptr<NavigationThrottle> navigation_throttle) {
   CHECK(navigation_throttle);
-  TRACE_EVENT1("navigation", "NavigationThrottleRegistryImpl::AddThrottle",
-               "navigation_throttle", navigation_throttle->GetNameForLogging());
+  TRACE_EVENT(TRACE_DISABLED_BY_DEFAULT("navigation"),
+              "NavigationThrottleRegistryImpl::AddThrottle",
+              "navigation_throttle", navigation_throttle->GetNameForLogging());
   CHECK(!navigation_request_->IsInitialWebUISyncNavigation());
   throttles_.push_back(std::move(navigation_throttle));
 }
diff --git a/content/browser/renderer_host/navigation_throttle_runner.cc b/content/browser/renderer_host/navigation_throttle_runner.cc
index d74886d..b26a738 100644
--- a/content/browser/renderer_host/navigation_throttle_runner.cc
+++ b/content/browser/renderer_host/navigation_throttle_runner.cc
@@ -167,9 +167,10 @@
     CHECK_EQ(throttles.size(), 0u);
   }
   for (size_t i = next_index_; i < throttles.size(); ++i) {
-    TRACE_EVENT0("navigation",
-                 "NavigationThrottleRunner::ProcessInternal.loop");
-    TRACE_EVENT_BEGIN("navigation", GetEventName(current_event_), "throttle",
+    TRACE_EVENT(TRACE_DISABLED_BY_DEFAULT("navigation"),
+                "NavigationThrottleRunner::ProcessInternal.loop");
+    TRACE_EVENT_BEGIN(TRACE_DISABLED_BY_DEFAULT("navigation"),
+                      GetEventName(current_event_), "throttle",
                       throttles[i]->GetNameForLogging());
 
     base::Time start = base::Time::Now();
@@ -179,12 +180,14 @@
       // The NavigationThrottle execution has destroyed this
       // NavigationThrottleRunner. Return immediately.
       // GetEventName(current_event_)
-      TRACE_EVENT_END("navigation", "result", "deleted");
+      TRACE_EVENT_END(TRACE_DISABLED_BY_DEFAULT("navigation"), "result",
+                      "deleted");
       return;
     }
     RecordExecutionTimeHistogram(current_event_, start);
     // GetEventName(current_event_)
-    TRACE_EVENT_END("navigation", "result", result.action());
+    TRACE_EVENT_END(TRACE_DISABLED_BY_DEFAULT("navigation"), "result",
+                    result.action());
 
     switch (result.action()) {
       case NavigationThrottle::PROCEED:
diff --git a/content/browser/renderer_host/page_impl.h b/content/browser/renderer_host/page_impl.h
index 22cf731..50c3a239 100644
--- a/content/browser/renderer_host/page_impl.h
+++ b/content/browser/renderer_host/page_impl.h
@@ -91,6 +91,13 @@
     is_on_load_completed_in_main_document_ = completed;
   }
 
+  bool has_recorded_partitioned_cookie_use() const {
+    return has_recorded_partitioned_cookie_use_;
+  }
+  void set_has_recorded_partitioned_cookie_use(bool recorded) {
+    has_recorded_partitioned_cookie_use_ = recorded;
+  }
+
   std::optional<base::TimeDelta> GetFirstContentfulPaintInMainDocumentDuration()
       const {
     return first_contentful_paint_in_main_document_duration_;
@@ -307,6 +314,10 @@
   // run for the main document.
   bool is_on_load_completed_in_main_document_ = false;
 
+  // True if we have already recorded the PartitionedCookiePresent UKM event
+  // for this page.
+  bool has_recorded_partitioned_cookie_use_ = false;
+
   // Time taken for first contentful paint to occur.
   std::optional<base::TimeDelta>
       first_contentful_paint_in_main_document_duration_;
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 7572657f..fd4b588c 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -450,6 +450,8 @@
     "management_policy.cc",
     "management_policy.h",
     "manifest_check_level.h",
+    "manifest_v2_experiment_manager.cc",
+    "manifest_v2_experiment_manager.h",
     "media_capture_util.cc",
     "media_capture_util.h",
     "message_service_api.cc",
@@ -891,6 +893,8 @@
     "webstore_data_fetcher.h",
     "webstore_data_fetcher_delegate.cc",
     "webstore_data_fetcher_delegate.h",
+    "webstore_install_helper.cc",
+    "webstore_install_helper.h",
     "webstore_installer.cc",
     "webstore_installer.h",
   ]
@@ -900,6 +904,7 @@
     ":cws_item_service_proto",
     "//base",
     "//components/download/public/common:public",
+    "//components/image_fetcher/core",
     "//components/update_client",
     "//content/public/browser",
     "//extensions/buildflags",
@@ -907,6 +912,7 @@
     "//extensions/strings",
     "//google_apis/common:request_util",
     "//net/traffic_annotation",
+    "//services/data_decoder/public/cpp",
     "//ui/base",
     "//url",
   ]
diff --git a/extensions/browser/DEPS b/extensions/browser/DEPS
index 785af45b..72e4b9f2 100644
--- a/extensions/browser/DEPS
+++ b/extensions/browser/DEPS
@@ -6,6 +6,7 @@
   "+components/custom_handlers",
   "+components/download",
   "+components/guest_view",
+  "+components/image_fetcher",
   "+components/input",
   "+components/javascript_dialogs",
   "+components/keyed_service",
diff --git a/extensions/browser/extension_management_client.h b/extensions/browser/extension_management_client.h
index 8e72005..a8c97d8 100644
--- a/extensions/browser/extension_management_client.h
+++ b/extensions/browser/extension_management_client.h
@@ -64,6 +64,15 @@
       int manifest_version,
       const std::string& extension_id,
       Manifest::Type manifest_type) = 0;
+
+  // Checks if the specified manifest version is permitted for an extension,
+  // based on its ID and manifest type.
+  virtual bool IsAllowedManifestVersion(int manifest_version,
+                                        const std::string& extension_id,
+                                        Manifest::Type manifest_type) = 0;
+
+  // Checks if the manifest version of the given extension is permitted.
+  virtual bool IsAllowedManifestVersion(const Extension* extension) = 0;
 };
 
 }  // namespace extensions
diff --git a/extensions/browser/extensions_browser_client.cc b/extensions/browser/extensions_browser_client.cc
index 09410bd..891b440 100644
--- a/extensions/browser/extensions_browser_client.cc
+++ b/extensions/browser/extensions_browser_client.cc
@@ -367,4 +367,14 @@
   return temp_dir;
 }
 
+std::unique_ptr<image_fetcher::ImageDecoder>
+ExtensionsBrowserClient::CreateImageDecoder() {
+  return nullptr;
+}
+
+bool ExtensionsBrowserClient::CanUseNonComponentExtensions(
+    content::BrowserContext* context) {
+  return true;
+}
+
 }  // namespace extensions
diff --git a/extensions/browser/extensions_browser_client.h b/extensions/browser/extensions_browser_client.h
index ea3195f7..9efeb731 100644
--- a/extensions/browser/extensions_browser_client.h
+++ b/extensions/browser/extensions_browser_client.h
@@ -52,6 +52,10 @@
 class DownloadItem;
 }  // namespace download
 
+namespace image_fetcher {
+class ImageDecoder;
+}  // namespace image_fetcher
+
 namespace mojo {
 template <typename>
 class BinderMapWithContext;
@@ -204,9 +208,20 @@
   // - if `context` is a System Profile: returns null.
   // - if `context` is Original: returns itself.
   // - if `context` is OTR: returns the associated parent context.
+  // - if `context` is ash internals: returns the associated parent context.
   virtual content::BrowserContext* GetContextRedirectedToOriginal(
       content::BrowserContext* context) = 0;
 
+  // Similar to GetContextRedirectedToOriginal(), but additionally filters out
+  // ash-internal profiles.
+  // - if `context` is a System Profile: returns null.
+  // - if `context` is Original: returns itself.
+  // - if `context` is OTR: returns the associated parent context.
+  // - if `context` is ash internals: returns null.
+  virtual content::BrowserContext*
+  GetContextRedirectedToOriginalWithoutAshInternals(
+      content::BrowserContext* context) = 0;
+
   // - if `context` is a System Profile: returns null.
   // - if `context` is Original: returns itself.
   // - if `context` is OTR: returns itself.
@@ -660,6 +675,13 @@
       content::BrowserContext* context,
       const download::DownloadItem& download);
 
+  // Creates an implementation of image_fetcher::ImageDecoder.
+  virtual std::unique_ptr<image_fetcher::ImageDecoder> CreateImageDecoder();
+
+  // Returns whether the given browser context is allowed to use non-component
+  // extensions.
+  virtual bool CanUseNonComponentExtensions(content::BrowserContext* context);
+
  protected:
   std::unique_ptr<ExtensionAssetsManager> assets_manager_;
 
diff --git a/chrome/browser/extensions/manifest_v2_experiment_manager.cc b/extensions/browser/manifest_v2_experiment_manager.cc
similarity index 93%
rename from chrome/browser/extensions/manifest_v2_experiment_manager.cc
rename to extensions/browser/manifest_v2_experiment_manager.cc
index d39bb7b0..4fa8e5eb 100644
--- a/chrome/browser/extensions/manifest_v2_experiment_manager.cc
+++ b/extensions/browser/manifest_v2_experiment_manager.cc
@@ -2,26 +2,25 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
+#include "extensions/browser/manifest_v2_experiment_manager.h"
 
 #include "base/auto_reset.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/one_shot_event.h"
 #include "base/strings/stringprintf.h"
 #include "base/types/pass_key.h"
-#include "chrome/browser/extensions/chrome_extension_system_factory.h"
-#include "chrome/browser/extensions/extension_management.h"
-#include "chrome/browser/extensions/profile_util.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_keyed_service_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/browser_context.h"
 #include "extensions/browser/disable_reason.h"
+#include "extensions/browser/extension_management_client.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_prefs_factory.h"
 #include "extensions/browser/extension_registrar.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_factory.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/browser/extension_system_provider.h"
 #include "extensions/browser/extensions_browser_client.h"
 #include "extensions/browser/mv2_experiment_stage.h"
 #include "extensions/browser/pref_names.h"
@@ -94,7 +93,8 @@
     "mv2_deprecation_user_re_enabled", PrefType::kBool,
     PrefScope::kExtensionSpecific};
 
-class ManifestV2ExperimentManagerFactory : public ProfileKeyedServiceFactory {
+class ManifestV2ExperimentManagerFactory
+    : public BrowserContextKeyedServiceFactory {
  public:
   ManifestV2ExperimentManagerFactory();
   ManifestV2ExperimentManagerFactory(
@@ -108,23 +108,20 @@
 
  private:
   // BrowserContextKeyedServiceFactory:
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
   std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
       content::BrowserContext* context) const override;
   bool ServiceIsCreatedWithBrowserContext() const override;
 };
 
 ManifestV2ExperimentManagerFactory::ManifestV2ExperimentManagerFactory()
-    : ProfileKeyedServiceFactory(
+    : BrowserContextKeyedServiceFactory(
           "ManifestV2ExperimentManager",
-          ProfileSelections::Builder()
-              .WithRegular(ProfileSelection::kRedirectedToOriginal)
-              .WithGuest(ProfileSelection::kRedirectedToOriginal)
-              .WithAshInternals(ProfileSelection::kNone)
-              .Build()) {
-  DependsOn(ExtensionManagementFactory::GetInstance());
+          BrowserContextDependencyManager::GetInstance()) {
   DependsOn(ExtensionPrefsFactory::GetInstance());
-  DependsOn(ChromeExtensionSystemFactory::GetInstance());
   DependsOn(ExtensionRegistryFactory::GetInstance());
+  DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
 }
 
 ManifestV2ExperimentManager*
@@ -134,6 +131,13 @@
       GetServiceForBrowserContext(browser_context, /*create=*/true));
 }
 
+content::BrowserContext*
+ManifestV2ExperimentManagerFactory::GetBrowserContextToUse(
+    content::BrowserContext* browser_context) const {
+  return ExtensionsBrowserClient::Get()
+      ->GetContextRedirectedToOriginalWithoutAshInternals(browser_context);
+}
+
 std::unique_ptr<KeyedService>
 ManifestV2ExperimentManagerFactory::BuildServiceInstanceForBrowserContext(
     content::BrowserContext* context) const {
@@ -275,8 +279,7 @@
 
   // Listen to management policy changes. `Unretained` is safe since the
   // `pref_change_registrar` is owned by this class.
-  pref_change_registrar_.Init(
-      Profile::FromBrowserContext(browser_context_)->GetPrefs());
+  pref_change_registrar_.Init(user_prefs::UserPrefs::Get(browser_context_));
   pref_change_registrar_.Add(
       pref_names::kManifestV2Availability,
       base::BindRepeating(
@@ -323,11 +326,12 @@
   // Unpacked extensions are special-cased (since developers need to be able to
   // continue supporting them). Check if unpacked extensions are blocked, either
   // by the experiment phase or by enterprise policy.
-  ExtensionManagement* extension_management =
-      ExtensionManagementFactory::GetForBrowserContext(browser_context_);
+  auto* extension_mgmt_client =
+      ExtensionsBrowserClient::Get()->GetExtensionManagementClient(
+          browser_context_);
   if (Manifest::IsUnpackedLocation(manifest_location) &&
       !ShouldBlockUnpackedExtensions(experiment_stage_) &&
-      extension_management->IsAllowedManifestVersion(
+      extension_mgmt_client->IsAllowedManifestVersion(
           manifest_version, extension_id, manifest_type)) {
     return false;
   }
@@ -528,8 +532,8 @@
     return;
   }
 
-  if (!profile_util::ProfileCanUseNonComponentExtensions(
-          Profile::FromBrowserContext(browser_context_))) {
+  if (!ExtensionsBrowserClient::Get()->CanUseNonComponentExtensions(
+          browser_context_)) {
     // Don't report metrics if the user can't install extensions in this
     // profile.
     return;
@@ -663,20 +667,24 @@
   DisableAffectedExtensions();
 }
 
-bool ManifestV2ExperimentManager::DidUserReEnableExtensionForTesting(
-    const ExtensionId& extension_id) {
+bool ManifestV2ExperimentManager::
+    DidUserReEnableExtensionForTesting(  // IN-TEST
+        const ExtensionId& extension_id) {
   return DidUserReEnableExtension(extension_id);
 }
 
-void ManifestV2ExperimentManager::DisableAffectedExtensionsForTesting() {
+void ManifestV2ExperimentManager::
+    DisableAffectedExtensionsForTesting() {  // IN-TEST
   DisableAffectedExtensions();
 }
 
-void ManifestV2ExperimentManager::EmitMetricsForProfileReadyForTesting() {
+void ManifestV2ExperimentManager::
+    EmitMetricsForProfileReadyForTesting() {  // IN-TEST
   EmitMetricsForProfileReady();
 }
 
-base::AutoReset<bool> ManifestV2ExperimentManager::AllowMV2ExtensionsForTesting(
+base::AutoReset<bool>
+ManifestV2ExperimentManager::AllowMV2ExtensionsForTesting(  // IN-TEST
     base::PassKey<ScopedTestMV2Enabler> pass_key) {
   return base::AutoReset<bool>(&g_allow_mv2_for_testing, true);
 }
diff --git a/chrome/browser/extensions/manifest_v2_experiment_manager.h b/extensions/browser/manifest_v2_experiment_manager.h
similarity index 97%
rename from chrome/browser/extensions/manifest_v2_experiment_manager.h
rename to extensions/browser/manifest_v2_experiment_manager.h
index f2d8de28..abd1634 100644
--- a/chrome/browser/extensions/manifest_v2_experiment_manager.h
+++ b/extensions/browser/manifest_v2_experiment_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_EXTENSIONS_MANIFEST_V2_EXPERIMENT_MANAGER_H_
-#define CHROME_BROWSER_EXTENSIONS_MANIFEST_V2_EXPERIMENT_MANAGER_H_
+#ifndef EXTENSIONS_BROWSER_MANIFEST_V2_EXPERIMENT_MANAGER_H_
+#define EXTENSIONS_BROWSER_MANIFEST_V2_EXPERIMENT_MANAGER_H_
 
 #include "base/auto_reset.h"
 #include "base/callback_list.h"
@@ -230,4 +230,4 @@
 
 }  // namespace extensions
 
-#endif  // CHROME_BROWSER_EXTENSIONS_MANIFEST_V2_EXPERIMENT_MANAGER_H_
+#endif  // EXTENSIONS_BROWSER_MANIFEST_V2_EXPERIMENT_MANAGER_H_
diff --git a/extensions/browser/test_extensions_browser_client.cc b/extensions/browser/test_extensions_browser_client.cc
index e2c0b6a..d6340c30 100644
--- a/extensions/browser/test_extensions_browser_client.cc
+++ b/extensions/browser/test_extensions_browser_client.cc
@@ -70,6 +70,15 @@
       Manifest::Type manifest_type) override {
     return false;
   }
+
+  bool IsAllowedManifestVersion(int manifest_version,
+                                const std::string& extension_id,
+                                Manifest::Type manifest_type) override {
+    return false;
+  }
+  bool IsAllowedManifestVersion(const Extension* extension) override {
+    return false;
+  }
 };
 
 // A KioskDelegate that returns safe/null defaults.
@@ -170,6 +179,12 @@
   return GetOriginalContext(context);
 }
 
+content::BrowserContext*
+TestExtensionsBrowserClient::GetContextRedirectedToOriginalWithoutAshInternals(
+    content::BrowserContext* context) {
+  return GetOriginalContext(context);
+}
+
 content::BrowserContext* TestExtensionsBrowserClient::GetContextOwnInstance(
     content::BrowserContext* context) {
   return context;
diff --git a/extensions/browser/test_extensions_browser_client.h b/extensions/browser/test_extensions_browser_client.h
index 35476d4..ac13d49 100644
--- a/extensions/browser/test_extensions_browser_client.h
+++ b/extensions/browser/test_extensions_browser_client.h
@@ -85,6 +85,8 @@
       content::BrowserContext* context) override;
   content::BrowserContext* GetContextRedirectedToOriginal(
       content::BrowserContext* context) override;
+  content::BrowserContext* GetContextRedirectedToOriginalWithoutAshInternals(
+      content::BrowserContext* context) override;
   content::BrowserContext* GetContextOwnInstance(
       content::BrowserContext* context) override;
   content::BrowserContext* GetContextForOriginalOnly(
diff --git a/chrome/browser/extensions/webstore_install_helper.cc b/extensions/browser/webstore_install_helper.cc
similarity index 66%
rename from chrome/browser/extensions/webstore_install_helper.cc
rename to extensions/browser/webstore_install_helper.cc
index ccf8f66b..b0baf424 100644
--- a/chrome/browser/extensions/webstore_install_helper.cc
+++ b/extensions/browser/webstore_install_helper.cc
@@ -2,19 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/extensions/webstore_install_helper.h"
+#include "extensions/browser/webstore_install_helper.h"
 
 #include <memory>
 
 #include "base/functional/bind.h"
 #include "base/values.h"
-#include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/browser/extensions_browser_client.h"
 #include "extensions/buildflags/buildflags.h"
 #include "net/base/load_flags.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/referrer_policy.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
+#include "ui/gfx/image/image.h"
 
 static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
 
@@ -43,7 +43,7 @@
 WebstoreInstallHelper::~WebstoreInstallHelper() = default;
 
 void WebstoreInstallHelper::Start(
-    network::mojom::URLLoaderFactory* loader_factory) {
+    scoped_refptr<network::SharedURLLoaderFactory> loader_factory) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
   data_decoder::DataDecoder::ParseJsonIsolated(
@@ -52,9 +52,7 @@
   if (icon_url_.is_empty()) {
     icon_decode_complete_ = true;
   } else {
-    // No existing |icon_fetcher_| to avoid unbalanced AddRef().
     CHECK(!icon_fetcher_.get());
-    AddRef();  // Balanced in OnFetchComplete().
 
     net::NetworkTrafficAnnotationTag traffic_annotation =
         net::DefineNetworkTrafficAnnotation("webstore_install_helper", R"(
@@ -72,6 +70,15 @@
               "The url of the icon for the extension, which includes the "
               "extension id."
             destination: GOOGLE_OWNED_SERVICE
+            internal {
+              contacts {
+                owners: "//extensions/OWNERS"
+              }
+            }
+            user_data {
+              type: NONE
+            }
+            last_reviewed: "2026-04-03"
           }
           policy {
             cookies_allowed: NO
@@ -82,34 +89,49 @@
             policy_exception_justification:
               "Not implemented, considered not useful."
           })");
-
-    icon_fetcher_ =
-        std::make_unique<BitmapFetcher>(icon_url_, this, traffic_annotation);
-    icon_fetcher_->Init(
-        net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN,
-        network::mojom::CredentialsMode::kOmit);
-    icon_fetcher_->Start(loader_factory);
+    icon_fetcher_ = std::make_unique<image_fetcher::ImageFetcherImpl>(
+        ExtensionsBrowserClient::Get()->CreateImageDecoder(), loader_factory);
+    image_fetcher::ImageFetcherParams params(traffic_annotation,
+                                             "WebstoreInstallHelper");
+    icon_fetcher_->FetchImage(
+        icon_url_,
+        base::BindOnce(&WebstoreInstallHelper::OnFetchComplete, this),
+        std::move(params));
   }
 }
 
-void WebstoreInstallHelper::OnFetchComplete(const GURL& url,
-                                            const SkBitmap* image) {
+void WebstoreInstallHelper::OnFetchComplete(
+    const gfx::Image& fetched_image,
+    const image_fetcher::RequestMetadata& metadata) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  // OnFetchComplete should only be called as icon_fetcher_ delegate to avoid
-  // unbalanced Release().
+  // OnFetchComplete should only be invoked as a callback of `icon_fetcher_`.
   CHECK(icon_fetcher_.get());
 
-  if (image)
-    icon_ = *image;
-  icon_decode_complete_ = true;
-  if (icon_.empty()) {
+  if (metadata.http_response_code ==
+          image_fetcher::RequestMetadata::ResponseCode::RESPONSE_CODE_INVALID ||
+      fetched_image.IsEmpty()) {
     error_ = kImageDecodeError;
     parse_error_ = Delegate::ICON_ERROR;
+  } else {
+    icon_ = fetched_image.AsBitmap();
   }
-  icon_fetcher_.reset();
+
+  icon_decode_complete_ = true;
 
   ReportResultsIfComplete();
-  Release();  // Balanced in Start().
+
+  // `icon_fetcher_` must remain valid after returning to the caller so that
+  // any ongoing work can complete safely.
+  // Keep this object a reference to `this` until ReleaseIconFetcher runs.
+  // Otherwise, this object could be destroyed immediately after this point.
+  // Note that it may be deleted as soon as the callback executes.
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE,
+      base::BindOnce(&WebstoreInstallHelper::ReleaseIconFetcher, this));
+}
+
+void WebstoreInstallHelper::ReleaseIconFetcher() {
+  icon_fetcher_.reset();
 }
 
 void WebstoreInstallHelper::OnJSONParsed(
@@ -130,13 +152,15 @@
 void WebstoreInstallHelper::ReportResultsIfComplete() {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
-  if (!icon_decode_complete_ || !manifest_parse_complete_)
+  if (!icon_decode_complete_ || !manifest_parse_complete_) {
     return;
+  }
 
-  if (error_.empty() && parsed_manifest_)
+  if (error_.empty() && parsed_manifest_) {
     delegate_->OnWebstoreParseSuccess(id_, icon_, std::move(*parsed_manifest_));
-  else
+  } else {
     delegate_->OnWebstoreParseFailure(id_, parse_error_, error_);
+  }
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/webstore_install_helper.h b/extensions/browser/webstore_install_helper.h
similarity index 73%
rename from chrome/browser/extensions/webstore_install_helper.h
rename to extensions/browser/webstore_install_helper.h
index 0895d12..aceeb23 100644
--- a/chrome/browser/extensions/webstore_install_helper.h
+++ b/extensions/browser/webstore_install_helper.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_EXTENSIONS_WEBSTORE_INSTALL_HELPER_H_
-#define CHROME_BROWSER_EXTENSIONS_WEBSTORE_INSTALL_HELPER_H_
+#ifndef EXTENSIONS_BROWSER_WEBSTORE_INSTALL_HELPER_H_
+#define EXTENSIONS_BROWSER_WEBSTORE_INSTALL_HELPER_H_
 
 #include <memory>
 #include <optional>
@@ -12,30 +12,22 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/values.h"
-#include "chrome/browser/bitmap_fetcher/bitmap_fetcher_delegate.h"
+#include "components/image_fetcher/core/image_fetcher_impl.h"
 #include "extensions/buildflags/buildflags.h"
 #include "services/data_decoder/public/cpp/data_decoder.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "url/gurl.h"
 
 static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
 
-class BitmapFetcher;
-
-namespace network {
-namespace mojom {
-class URLLoaderFactory;
-}
-}  // namespace network
-
 namespace extensions {
 
 // This is a class to help dealing with webstore-provided data. It manages
 // sending work to the utility process for parsing manifests and
 // fetching/decoding icon data. Clients must implement the
 // WebstoreInstallHelper::Delegate interface to receive the parsed data.
-class WebstoreInstallHelper : public base::RefCounted<WebstoreInstallHelper>,
-                              public BitmapFetcherDelegate {
+class WebstoreInstallHelper : public base::RefCounted<WebstoreInstallHelper> {
  public:
   class Delegate {
    public:
@@ -49,10 +41,9 @@
 
     // Called to indicate a parse failure. The `result_code` parameter should
     // indicate whether the problem was with the manifest or icon.
-    virtual void OnWebstoreParseFailure(
-        const std::string& id,
-        InstallHelperResultCode result_code,
-        const std::string& error_message) = 0;
+    virtual void OnWebstoreParseFailure(const std::string& id,
+                                        InstallHelperResultCode result_code,
+                                        const std::string& error_message) = 0;
 
    protected:
     virtual ~Delegate() = default;
@@ -63,18 +54,22 @@
                         const std::string& id,
                         const std::string& manifest,
                         const GURL& icon_url);
-  void Start(network::mojom::URLLoaderFactory* loader_factory);
+  void Start(scoped_refptr<network::SharedURLLoaderFactory> loader_factory);
 
  private:
   friend class base::RefCounted<WebstoreInstallHelper>;
 
-  ~WebstoreInstallHelper() override;
+  ~WebstoreInstallHelper();
 
   // Callback for the DataDecoder.
   void OnJSONParsed(data_decoder::DataDecoder::ValueOrError result);
 
-  // Implementing the BitmapFetcherDelegate interface.
-  void OnFetchComplete(const GURL& url, const SkBitmap* image) override;
+  void OnFetchComplete(const gfx::Image& fetched_image,
+                       const image_fetcher::RequestMetadata& metadata);
+
+  // This is invoked as a callback holding a retained reference to `this`.
+  // The object may be destroyed immediately after this method returns.
+  void ReleaseIconFetcher();
 
   void ReportResultsIfComplete();
 
@@ -90,7 +85,7 @@
   // If `icon_url_` is non-empty, it needs to be fetched and decoded into an
   // SkBitmap.
   GURL icon_url_;
-  std::unique_ptr<BitmapFetcher> icon_fetcher_;
+  std::unique_ptr<image_fetcher::ImageFetcher> icon_fetcher_;
 
   // Flags for whether we're done doing icon decoding and manifest parsing.
   bool icon_decode_complete_;
@@ -110,4 +105,4 @@
 
 }  // namespace extensions
 
-#endif  // CHROME_BROWSER_EXTENSIONS_WEBSTORE_INSTALL_HELPER_H_
+#endif  // EXTENSIONS_BROWSER_WEBSTORE_INSTALL_HELPER_H_
diff --git a/extensions/shell/browser/shell_extensions_browser_client.cc b/extensions/shell/browser/shell_extensions_browser_client.cc
index d4c2b0c..833ce54 100644
--- a/extensions/shell/browser/shell_extensions_browser_client.cc
+++ b/extensions/shell/browser/shell_extensions_browser_client.cc
@@ -102,6 +102,12 @@
   return context;
 }
 
+content::BrowserContext*
+ShellExtensionsBrowserClient::GetContextRedirectedToOriginalWithoutAshInternals(
+    content::BrowserContext* context) {
+  return context;
+}
+
 content::BrowserContext* ShellExtensionsBrowserClient::GetContextOwnInstance(
     content::BrowserContext* context) {
   return context;
diff --git a/extensions/shell/browser/shell_extensions_browser_client.h b/extensions/shell/browser/shell_extensions_browser_client.h
index 9422545..b8787ad 100644
--- a/extensions/shell/browser/shell_extensions_browser_client.h
+++ b/extensions/shell/browser/shell_extensions_browser_client.h
@@ -51,6 +51,8 @@
       content::BrowserContext* context) override;
   content::BrowserContext* GetContextRedirectedToOriginal(
       content::BrowserContext* context) override;
+  content::BrowserContext* GetContextRedirectedToOriginalWithoutAshInternals(
+      content::BrowserContext* context) override;
   content::BrowserContext* GetContextOwnInstance(
       content::BrowserContext* context) override;
   content::BrowserContext* GetContextForOriginalOnly(
diff --git a/extensions/strings/extensions_strings.grd b/extensions/strings/extensions_strings.grd
index 6958fe8e..5d7ce50 100644
--- a/extensions/strings/extensions_strings.grd
+++ b/extensions/strings/extensions_strings.grd
@@ -498,6 +498,20 @@
       <message name="IDS_WEBSTORE_DOWNLOAD_ACCESS_DENIED" desc="Text displayed in the error prompt when a CRX cannot be downloaded due to an authentication error.">
         Access denied.
       </message>
+
+       <!-- Error messages for Web Store API -->
+     <message name="IDS_EXTENSIONS_SUPERVISED_USER_PARENTAL_PERMISSION_FAILURE" desc="The error information displayed in extra details if the parent permission request flow fails unexpectedly when a supervised user attempts to install or enable an extension. Note that this does not include the case where a parent fails to enter their password.">
+        Parent permission request failed.
+      </message>
+      <message name="IDS_EXTENSIONS_SUPERVISED_USER_BLOCKED_BY_PARENT" desc="The information displayed in extra details if the parent blocks or cancels the installation flow for an extension.">
+        Your parent didn't approve this extension
+      </message>
+
+      <!-- Extension/App install dialog strings -->
+      <message name="IDS_EXTENSION_PROMPT_MESSAGE_FROM_ADMIN" desc="Second line in the content area of the extension of app blocked prompt. It gives user the additional message which comes from their administrator.">
+        From your administrator: <ph name="ADMIN_MESSAGE">$1<ex>Please visit www.example.com/extension for more information.</ex></ph>
+      </message>
+
     </messages>
   </release>
 </grit>
diff --git a/internal b/internal
index f9143bd..572bdfc 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit f9143bd3d8c2762c7cf11c1eb1d7e23d2c94ec26
+Subproject commit 572bdfc0045579bddb74a4c981aa48805106aad3
diff --git a/ios/chrome/app/task_request_user_activity.mm b/ios/chrome/app/task_request_user_activity.mm
index 49b4c927..dba628a 100644
--- a/ios/chrome/app/task_request_user_activity.mm
+++ b/ios/chrome/app/task_request_user_activity.mm
@@ -41,6 +41,7 @@
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
 #import "ios/chrome/browser/shared/public/commands/lens_commands.h"
 #import "ios/chrome/browser/shared/public/commands/open_lens_input_selection_command.h"
+#import "ios/chrome/browser/shared/public/commands/quick_delete_commands.h"
 #import "ios/chrome/browser/shared/public/commands/scene_commands.h"
 #import "ios/chrome/browser/shared/public/commands/settings_commands.h"
 #import "ios/chrome/browser/url_loading/model/url_loading_params.h"
@@ -332,6 +333,15 @@
   }
 }
 
+// Opens quick delete to clear browsing data.
+void OpenClearBrowsingDataWithBrowser(base::WeakPtr<Browser> weak_browser) {
+  if (Browser* browser = weak_browser.get()) {
+    id<QuickDeleteCommands> handler = HandlerForProtocol(
+        browser->GetCommandDispatcher(), QuickDeleteCommands);
+    [handler showQuickDeleteAndCanPerformRadialWipeAnimation:YES];
+  }
+}
+
 // Adds bookmarks to Chrome.
 void AddBookmarkToChromeWithIntent(INIntent* intent,
                                    base::WeakPtr<Browser> weak_browser) {
@@ -440,6 +450,8 @@
   id<TabOpening> tabOpener = sceneState.controller;
   Browser* browser =
       sceneState.browserProviderInterface.currentBrowserProvider.browser;
+  Browser* mainBrowser =
+      sceneState.browserProviderInterface.mainBrowserProvider.browser;
 
   switch (_userActivityType) {
     case UserActivityType::kHandoff:
@@ -537,7 +549,9 @@
       webpageGURLs.push_back(GURL(kChromeUINewTabURL));
       break;
     case UserActivityType::kClearBrowsingData:
-      // TODO(crbug.com/492115056): Add implementation.
+      completion = base::CallbackToBlock(base::BindRepeating(
+          &OpenClearBrowsingDataWithBrowser, mainBrowser->AsWeakPtr()));
+      webpageGURLs.push_back(GURL(kChromeUINewTabURL));
       break;
     case UserActivityType::kCredentialExchange:
       // TODO(crbug.com/492115056): Add implementation.
diff --git a/ios/chrome/browser/app_bar/ui/app_bar_view_controller.mm b/ios/chrome/browser/app_bar/ui/app_bar_view_controller.mm
index 9523a245..47a0c9a 100644
--- a/ios/chrome/browser/app_bar/ui/app_bar_view_controller.mm
+++ b/ios/chrome/browser/app_bar/ui/app_bar_view_controller.mm
@@ -4,6 +4,8 @@
 
 #import "ios/chrome/browser/app_bar/ui/app_bar_view_controller.h"
 
+#import <optional>
+
 #import "base/metrics/user_metrics.h"
 #import "base/metrics/user_metrics_action.h"
 #import "components/strings/grit/components_strings.h"
@@ -84,9 +86,8 @@
 
 // Returns the font size for the assistant button.
 UIFont* AssistantButtonFontSize(UITraitCollection* traitCollection) {
-  return PreferredFontForTextStyleWithMaxCategory(
-      UIFontTextStyleCaption2, traitCollection.preferredContentSizeCategory,
-      UIContentSizeCategoryExtraExtraExtraLarge);
+  return PreferredFontForTextStyle(UIFontTextStyleCaption2, UIFontWeightMedium,
+                                   std::nullopt);
 }
 
 }  // namespace
diff --git a/ios/chrome/browser/assistant/ui/assistant_container_layout_utils.h b/ios/chrome/browser/assistant/ui/assistant_container_layout_utils.h
index c4ef3bca..7b60053 100644
--- a/ios/chrome/browser/assistant/ui/assistant_container_layout_utils.h
+++ b/ios/chrome/browser/assistant/ui/assistant_container_layout_utils.h
@@ -31,15 +31,6 @@
 // Corner radius for all 4 corners.
 extern const CGFloat kAssistantSidePanelCornerRadius;
 
-// Shadow styling constants applied for the sheet and side panel.
-
-// Shadow opacity.
-extern const float kAssistantShadowOpacity;
-// Shadow blur radius.
-extern const CGFloat kAssistantShadowRadius;
-// Shadow offset.
-extern const CGSize kAssistantShadowOffset;
-
 // Animation constants for sheet and side panel.
 
 // Spring duration for sheet.
diff --git a/ios/chrome/browser/assistant/ui/assistant_container_layout_utils.mm b/ios/chrome/browser/assistant/ui/assistant_container_layout_utils.mm
index 3b05e74..c8280e5 100644
--- a/ios/chrome/browser/assistant/ui/assistant_container_layout_utils.mm
+++ b/ios/chrome/browser/assistant/ui/assistant_container_layout_utils.mm
@@ -17,6 +17,15 @@
 // Constants used for the container resizing animation.
 constexpr CGFloat kRubberBandCoefficient = 0.10;
 
+// Constants used for the side panel aesthetics.
+constexpr CGFloat kAssistantSidePanelBorderWidth = 1.0;
+constexpr CGFloat kAssistantSidePanelBorderAlpha = 0.46;
+
+// Constants used for the floating card shadow.
+const CGSize kAssistantSidePanelShadowOffset = {0, 13};
+constexpr CGFloat kAssistantSidePanelShadowRadius = 125.0;
+constexpr CGFloat kAssistantSidePanelShadowOpacity = 0.16;
+
 }  // namespace
 
 const CGFloat kMorphingBaseMargin = 10.0;
@@ -30,10 +39,6 @@
 const CGFloat kAssistantContainerMargin = 8.0;
 const CGFloat kAssistantSidePanelCornerRadius = 22.0;
 
-const float kAssistantShadowOpacity = 0.29f;
-const CGFloat kAssistantShadowRadius = 21.0;
-const CGSize kAssistantShadowOffset = {0, 11};
-
 const NSTimeInterval kAssistantSheetSpringDuration = 0.3;
 const NSTimeInterval kAssistantSidePanelAnimationDuration = 0.5;
 const NSTimeInterval kAssistantSidePanelInsetAnimationDuration = 0.2;
@@ -56,6 +61,8 @@
                                        bool active) {
   if (!active) {
     content_view.layer.cornerRadius = 0.0;
+    content_view.layer.borderWidth = 0.0;
+    content_view.layer.borderColor = nil;
     shadow_view.layer.shadowOpacity = 0.0;
     shadow_view.backgroundColor = [UIColor clearColor];
     return;
@@ -63,6 +70,11 @@
 
   content_view.layer.cornerRadius = kAssistantSidePanelCornerRadius;
   content_view.layer.cornerCurve = kCACornerCurveContinuous;
+  content_view.layer.borderWidth = kAssistantSidePanelBorderWidth;
+  content_view.layer.borderColor =
+      [[UIColor whiteColor]
+          colorWithAlphaComponent:kAssistantSidePanelBorderAlpha]
+          .CGColor;
 
   // The view must be opaque to cast a shadow.
   shadow_view.backgroundColor = [UIColor colorNamed:kSecondaryBackgroundColor];
@@ -71,9 +83,9 @@
   // TODO(crbug.com/494503434): Update the shadow color to a dynamic color or
   // handle dark mode properly later.
   shadow_view.layer.shadowColor = [UIColor blackColor].CGColor;
-  shadow_view.layer.shadowOffset = kAssistantShadowOffset;
-  shadow_view.layer.shadowRadius = kAssistantShadowRadius;
-  shadow_view.layer.shadowOpacity = kAssistantShadowOpacity;
+  shadow_view.layer.shadowOffset = kAssistantSidePanelShadowOffset;
+  shadow_view.layer.shadowRadius = kAssistantSidePanelShadowRadius;
+  shadow_view.layer.shadowOpacity = kAssistantSidePanelShadowOpacity;
 }
 
 NSInteger RubberBandDistance(NSInteger offset, NSInteger dimension) {
diff --git a/ios/chrome/browser/assistant/ui/assistant_container_view.mm b/ios/chrome/browser/assistant/ui/assistant_container_view.mm
index 0dd7bdbe..318ec90 100644
--- a/ios/chrome/browser/assistant/ui/assistant_container_view.mm
+++ b/ios/chrome/browser/assistant/ui/assistant_container_view.mm
@@ -17,6 +17,11 @@
 constexpr CGFloat kGrabberHeight = 4.0;
 constexpr CGFloat kGrabberTopMargin = 8.0;
 
+// Shadow styling.
+const float kAssistantShadowOpacity = 0.29f;
+const CGFloat kAssistantShadowRadius = 21.0;
+const CGSize kAssistantShadowOffset = {0, 11};
+
 }  // namespace
 
 @implementation AssistantContainerView {
diff --git a/ios/chrome/browser/badges/ui_bundled/incognito_badge_mediator.mm b/ios/chrome/browser/badges/ui_bundled/incognito_badge_mediator.mm
index 3c8dc41a..5d6239c8 100644
--- a/ios/chrome/browser/badges/ui_bundled/incognito_badge_mediator.mm
+++ b/ios/chrome/browser/badges/ui_bundled/incognito_badge_mediator.mm
@@ -161,9 +161,6 @@
 }
 
 - (void)maybeUpdateIncognitoBadgeVisibility {
-  if (!base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdateV2)) {
-    return;
-  }
   _consumer.disabled = [self isCurrentWebStateShowingNTP];
 }
 
diff --git a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
index 44f5daa0..3e526e1 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
+++ b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
@@ -1218,10 +1218,13 @@
 
   // TODO(crbug.com/40256480): Remove when BVC will no longer handle commands.
   [self.dispatcher stopDispatchingToTarget:_viewController];
-  [_viewController shutdown];
-  _viewController = nil;
+
+  // Stop layout coordinator first to follow the reverse order of start calls.
   [_browserLayoutCoordinator stop];
   _browserLayoutCoordinator = nil;
+
+  [_viewController shutdown];
+  _viewController = nil;
 }
 
 // Ensure BrowserViewController's view is created
@@ -5211,9 +5214,12 @@
 
 - (CGFloat)initialContentOffsetForOverscrollActionsController:
     (OverscrollActionsController*)controller {
-  return ios::provider::IsFullscreenSmoothScrollingSupported()
-             ? -[self headerInsetForOverscrollActionsController:controller]
-             : 0.0;
+  if (IsFullscreenRefactoringEnabled() ||
+      ios::provider::IsFullscreenSmoothScrollingSupported()) {
+    return -[self headerInsetForOverscrollActionsController:controller];
+  } else {
+    return 0.0;
+  }
 }
 
 - (FullscreenController*)fullscreenControllerForOverscrollActionsController:
diff --git a/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm b/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm
index e920d1c..f3ca010 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm
+++ b/ios/chrome/browser/browser_view/ui_bundled/browser_view_controller.mm
@@ -466,6 +466,11 @@
 }
 
 - (void)setBroadcasting:(BOOL)broadcasting {
+  if (IsFullscreenRefactoringEnabled()) {
+    // Broadcasting is not needed for FullscreenRefactoring.
+    return;
+  }
+
   if (_broadcasting == broadcasting) {
     return;
   }
@@ -1073,7 +1078,8 @@
   // change on rotation.
   [self updateToolbarState];
   // Resize horizontal viewport if Smooth Scrolling is on.
-  if (ios::provider::IsFullscreenSmoothScrollingSupported()) {
+  if (!IsFullscreenRefactoringEnabled() &&
+      ios::provider::IsFullscreenSmoothScrollingSupported()) {
     self.fullscreenController->ResizeHorizontalViewport();
   }
 
@@ -1394,7 +1400,8 @@
     // Make new content visible, resizing it first as the orientation may
     // have changed from the last time it was displayed.
     CGRect viewFrame = self.contentArea.bounds;
-    if (!ios::provider::IsFullscreenSmoothScrollingSupported()) {
+    if (!IsFullscreenRefactoringEnabled() &&
+        !ios::provider::IsFullscreenSmoothScrollingSupported()) {
       // If the Smooth Scrolling is on, the WebState view is not
       // resized, and should always match the bounds of the content area.  When
       // the provider is not initialized, viewport insets resize the webview, so
@@ -1427,18 +1434,13 @@
     } else {
       self.browserContentViewController.contentView = view;
       if (IsFullscreenRefactoringEnabled()) {
-        if (ios::provider::IsFullscreenSmoothScrollingSupported()) {
-          view.translatesAutoresizingMaskIntoConstraints = NO;
-          AddSameConstraints(self.browserContentViewController.view, view);
-        } else {
-          // TODO(crbug.com/483998779): Handle the rotation of the web content
-          // in a better way.
-          view.autoresizingMask = UIViewAutoresizingFlexibleWidth;
-        }
+        view.translatesAutoresizingMaskIntoConstraints = NO;
+        AddSameConstraints(self.browserContentViewController.view, view);
       }
     }
     // Resize horizontal viewport if Smooth Scrolling is on.
-    if (ios::provider::IsFullscreenSmoothScrollingSupported()) {
+    if (!IsFullscreenRefactoringEnabled() &&
+        ios::provider::IsFullscreenSmoothScrollingSupported()) {
       self.fullscreenController->ResizeHorizontalViewport();
     }
   }
@@ -1853,7 +1855,8 @@
 - (void)updateForFullscreenProgress:(CGFloat)progress {
   [self updateHeadersForFullscreenProgress:progress];
   [self updateFootersForFullscreenProgress:progress];
-  if (!ios::provider::IsFullscreenSmoothScrollingSupported()) {
+  if (!IsFullscreenRefactoringEnabled() &&
+      !ios::provider::IsFullscreenSmoothScrollingSupported()) {
     [self updateBrowserViewportForFullscreenProgress:progress];
   }
 }
@@ -1990,6 +1993,10 @@
 // Returns the height difference between the fully expanded and fully collapsed
 // primary toolbar.
 - (CGFloat)primaryToolbarHeightDelta {
+  if (IsFullscreenRefactoringEnabled()) {
+    return std::max(0.0, _fullscreenBrowserAgent->max_insets().top -
+                             _fullscreenBrowserAgent->min_insets().top);
+  }
   CGFloat fullyExpandedHeight =
       self.fullscreenController->GetMaxViewportInsets().top;
   CGFloat fullyCollapsedHeight =
@@ -2000,6 +2007,10 @@
 // Returns the height difference between the fully expanded and fully collapsed
 // secondary toolbar.
 - (CGFloat)secondaryToolbarHeightDelta {
+  if (IsFullscreenRefactoringEnabled()) {
+    return std::max(0.0, _fullscreenBrowserAgent->max_insets().bottom -
+                             _fullscreenBrowserAgent->min_insets().bottom);
+  }
   CGFloat fullyExpandedHeight =
       self.fullscreenController->GetMaxViewportInsets().bottom;
   CGFloat fullyCollapsedHeight =
@@ -2026,8 +2037,13 @@
     return;
   }
 
-  const CGFloat expandedToolbarHeight =
-      self.fullscreenController->GetMaxViewportInsets().bottom;
+  CGFloat expandedToolbarHeight;
+  if (IsFullscreenRefactoringEnabled()) {
+    expandedToolbarHeight = _fullscreenBrowserAgent->max_insets().bottom;
+  } else {
+    expandedToolbarHeight =
+        self.fullscreenController->GetMaxViewportInsets().bottom;
+  }
   if (!expandedToolbarHeight) {
     // If `expandedToolbarHeight` is 0, secondary toolbar is hidden. In that
     // case don't update it's height on fullscreen progress.
diff --git a/ios/chrome/browser/cobrowse/coordinator/assistant_aim_coordinator.mm b/ios/chrome/browser/cobrowse/coordinator/assistant_aim_coordinator.mm
index 7de7cf8..0d07ff3 100644
--- a/ios/chrome/browser/cobrowse/coordinator/assistant_aim_coordinator.mm
+++ b/ios/chrome/browser/cobrowse/coordinator/assistant_aim_coordinator.mm
@@ -139,11 +139,6 @@
 - (void)assistantAIMViewController:(AssistantAIMViewController*)viewController
        didShowKeyboardWithDuration:(NSTimeInterval)duration
                              curve:(UIViewAnimationCurve)curve {
-  // When the keyboard is shown, prevent collapsing before latching to the
-  // medium detent first.
-  [_containerHandler
-      setAssistantContainerDetents:{AssistantContainerDetent::kMedium,
-                                    AssistantContainerDetent::kLarge}];
   [_containerHandler
       animateAssistantContainerToDetent:AssistantContainerDetent::kLarge
                                duration:duration
@@ -152,20 +147,19 @@
 
 - (void)assistantAIMViewControllerDidHideKeyboard:
     (AssistantAIMViewController*)viewController {
-  // When the keyboard is dismissed, all detents are available.
-  [_containerHandler
-      setAssistantContainerDetents:{AssistantContainerDetent::kMinimized,
-                                    AssistantContainerDetent::kMedium,
-                                    AssistantContainerDetent::kLarge}];
 }
 
 - (void)assistantAIMViewControllerDidRequestEndEditing:
     (AssistantAIMViewController*)viewController {
-  [_inputPlateCoordinator endEditing];
+  [self dismissKeyboard];
 }
 
 #pragma mark - Private
 
+- (void)dismissKeyboard {
+  [_inputPlateCoordinator endEditing];
+}
+
 // Dismisses the assistant container safely.
 - (void)dismissAssistantContainerAnimated:(BOOL)animated {
   if (self.browser) {
@@ -204,14 +198,14 @@
   _currentDetent = newDetent;
   // Attempt to dismiss the keyboard when the sheet is collapsing.
   if (newDetent == AssistantContainerDetent::kMedium) {
-    [_inputPlateCoordinator endEditing];
+    [self dismissKeyboard];
   }
 }
 
 #pragma mark - AssistantAIMMediatorDelegate
 
 - (void)assistantAIMMediatorDidLoadQuery:(AssistantAIMMediator*)mediator {
-  [_inputPlateCoordinator endEditing];
+  [self dismissKeyboard];
 }
 
 - (BOOL)assistantContainer:(AssistantContainerViewController*)container
diff --git a/ios/chrome/browser/composebox/coordinator/composebox_coordinator.mm b/ios/chrome/browser/composebox/coordinator/composebox_coordinator.mm
index 2883b497..4fb193d 100644
--- a/ios/chrome/browser/composebox/coordinator/composebox_coordinator.mm
+++ b/ios/chrome/browser/composebox/coordinator/composebox_coordinator.mm
@@ -347,8 +347,7 @@
 - (BOOL)shouldUseIpadPresentationController {
   return IsComposeboxIpadEnabled() &&
          UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad &&
-         (base::ios::IsRunningOnIOS26OrLater() ||
-          IsRegularXRegularSizeClass(self.baseViewController.traitCollection));
+         IsRegularXRegularSizeClass(self.baseViewController.traitCollection);
 }
 
 // Represents the coordinator's view controller with no animation.
diff --git a/ios/chrome/browser/composebox/coordinator/composebox_input_plate_coordinator.mm b/ios/chrome/browser/composebox/coordinator/composebox_input_plate_coordinator.mm
index 2dc2bac4..48323d3 100644
--- a/ios/chrome/browser/composebox/coordinator/composebox_input_plate_coordinator.mm
+++ b/ios/chrome/browser/composebox/coordinator/composebox_input_plate_coordinator.mm
@@ -281,7 +281,9 @@
       didMoveToParentViewController:_viewController];
 
   [_omniboxCoordinator updateOmniboxState];
-  [_omniboxCoordinator focusOmnibox];
+  if (_entrypoint != ComposeboxEntrypoint::kCobrowse) {
+    [_omniboxCoordinator focusOmnibox];
+  }
 }
 
 - (void)stop {
diff --git a/ios/chrome/browser/composebox/ui/composebox_input_plate_view_controller.mm b/ios/chrome/browser/composebox/ui/composebox_input_plate_view_controller.mm
index ef8c2a8..1d88b66 100644
--- a/ios/chrome/browser/composebox/ui/composebox_input_plate_view_controller.mm
+++ b/ios/chrome/browser/composebox/ui/composebox_input_plate_view_controller.mm
@@ -2015,8 +2015,13 @@
 // Returns the title for the given input plate element of a certain type.
 - (NSString*)titleFor:(InputPlateString)element
                  type:(InputPlateStringType)type {
-  return [self serverStringForElement:element type:type]
-             ?: [self localFallbackForElement:element type:type];
+  NSString* serverString = [self serverStringForElement:element type:type];
+  BOOL useLocalFallback = [serverString length] == 0;
+  if (useLocalFallback) {
+    return [self localFallbackForElement:element type:type];
+  }
+
+  return serverString;
 }
 
 // Creates a new canvas button to be displayed in the input plate.
diff --git a/ios/chrome/browser/composebox/ui/composebox_view_controller.mm b/ios/chrome/browser/composebox/ui/composebox_view_controller.mm
index 5ae4b0a..ec974b1 100644
--- a/ios/chrome/browser/composebox/ui/composebox_view_controller.mm
+++ b/ios/chrome/browser/composebox/ui/composebox_view_controller.mm
@@ -504,8 +504,9 @@
           [_inputViewController.view.trailingAnchor
               constraintEqualToAnchor:_closeButton.leadingAnchor
                              constant:-kInputPlateTrailingPadding],
-          [_closeButton.centerYAnchor
-              constraintEqualToAnchor:_inputViewController.view.centerYAnchor]
+          [_closeButton.topAnchor
+              constraintEqualToAnchor:_inputViewController.view.topAnchor
+                             constant:0]
         ]];
 
         _closeButton.hidden = NO;
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index c9c01cd..a65f64f 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -1056,12 +1056,6 @@
     {"Force FRE", kForceBWGFirstTimeRun, nullptr},
     {"Skip new user delay", kSkipNewUserDelay, nullptr}};
 
-const FeatureEntry::FeatureParam kOmniboxMobileParityEnableFeedForGoogleOnly[] =
-    {{OmniboxFieldTrial::kMobileParityEnableFeedForGoogleOnly.name, "true"}};
-const FeatureEntry::FeatureVariation kOmniboxMobileParityVariations[] = {
-    {"- feed only when searching with Google",
-     kOmniboxMobileParityEnableFeedForGoogleOnly, nullptr}};
-
 const FeatureEntry::FeatureParam kPageActionMenuDirectEntryPoint[] = {
     {kPageActionMenuDirectEntryPointParam, "true"},
 };
@@ -1505,17 +1499,6 @@
      flag_descriptions::kOmniboxLocalHistoryZeroSuggestBeyondNTPDescription,
      flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(omnibox::kLocalHistoryZeroSuggestBeyondNTP)},
-    {"omnibox-mobile-parity-update",
-     flag_descriptions::kOmniboxMobileParityUpdateName,
-     flag_descriptions::kOmniboxMobileParityUpdateDescription, flags_ui::kOsIos,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(omnibox::kOmniboxMobileParityUpdate,
-                                    kOmniboxMobileParityVariations,
-                                    "OmniboxMobileParityUpdate")},
-    {"omnibox-mobile-parity-update-v2",
-     flag_descriptions::kOmniboxMobileParityUpdateV2Name,
-     flag_descriptions::kOmniboxMobileParityUpdateV2Description,
-     flags_ui::kOsIos,
-     FEATURE_VALUE_TYPE(omnibox::kOmniboxMobileParityUpdateV2)},
     {"force-startup-signin-promo",
      flag_descriptions::kForceStartupSigninPromoName,
      flag_descriptions::kForceStartupSigninPromoDescription, flags_ui::kOsIos,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 64699a1a..dcf76cd 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -1292,17 +1292,6 @@
 const char kOmniboxMlUrlSearchBlendingDescription[] =
     "Specifies how to blend URL ML scores and search traditional scores.";
 
-const char kOmniboxMobileParityUpdateName[] = "Omnibox Mobile parity update";
-const char kOmniboxMobileParityUpdateDescription[] =
-    "When set, applies certain assets to match Desktop visuals and "
-    "descriptions";
-
-const char kOmniboxMobileParityUpdateV2Name[] =
-    "Omnibox Mobile parity update V2";
-const char kOmniboxMobileParityUpdateV2Description[] =
-    "When set, applies certain assets to match Desktop visuals and "
-    "descriptions";
-
 const char kOmniboxOnClobberFocusTypeOnIOSName[] =
     "Omnibox On Clobber Focus Type On IOS";
 const char kOmniboxOnClobberFocusTypeOnIOSDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index ebb7fd6..a976988 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -799,12 +799,6 @@
 extern const char kOmniboxMlUrlSearchBlendingName[];
 extern const char kOmniboxMlUrlSearchBlendingDescription[];
 
-extern const char kOmniboxMobileParityUpdateName[];
-extern const char kOmniboxMobileParityUpdateDescription[];
-
-extern const char kOmniboxMobileParityUpdateV2Name[];
-extern const char kOmniboxMobileParityUpdateV2Description[];
-
 extern const char kOmniboxOnClobberFocusTypeOnIOSName[];
 extern const char kOmniboxOnClobberFocusTypeOnIOSDescription[];
 
diff --git a/ios/chrome/browser/fullscreen/ui_bundled/fullscreen_model.mm b/ios/chrome/browser/fullscreen/ui_bundled/fullscreen_model.mm
index 29355fa..bd0212b 100644
--- a/ios/chrome/browser/fullscreen/ui_bundled/fullscreen_model.mm
+++ b/ios/chrome/browser/fullscreen/ui_bundled/fullscreen_model.mm
@@ -40,6 +40,11 @@
 }  // namespace
 
 FullscreenModel::FullscreenModel() {
+  // TODO(crbug.com/500417603): This can be removed once all calls to
+  // FullscreenController are flag guarded.
+  if (IsFullscreenRefactoringEnabled()) {
+    return;
+  }
   UpdateSpeed();
   if (web::features::IsFullscreenScrollThresholdEnabled()) {
     scroll_threshold_ = GetFieldTrialParamByFeatureAsDouble(
diff --git a/ios/chrome/browser/fullscreen/ui_bundled/fullscreen_system_notification_observer.mm b/ios/chrome/browser/fullscreen/ui_bundled/fullscreen_system_notification_observer.mm
index ba99f6f..704593e 100644
--- a/ios/chrome/browser/fullscreen/ui_bundled/fullscreen_system_notification_observer.mm
+++ b/ios/chrome/browser/fullscreen/ui_bundled/fullscreen_system_notification_observer.mm
@@ -10,6 +10,7 @@
 #import "ios/chrome/browser/fullscreen/ui_bundled/fullscreen_controller.h"
 #import "ios/chrome/browser/fullscreen/ui_bundled/legacy_fullscreen_mediator.h"
 #import "ios/chrome/browser/fullscreen/ui_bundled/scoped_fullscreen_disabler.h"
+#import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/public/provider/chrome/browser/fullscreen/fullscreen_api.h"
 #import "ios/web/common/features.h"
 
@@ -36,6 +37,12 @@
 - (instancetype)initWithController:(FullscreenController*)controller
                           mediator:(LegacyFullscreenMediator*)mediator {
   if ((self = [super init])) {
+    // TODO(crbug.com/500417603): This can be removed once all calls to
+    // FullscreenController are flag guarded.
+    if (IsFullscreenRefactoringEnabled()) {
+      return self;
+    }
+
     _controller = controller;
     DCHECK(_controller);
     _mediator = mediator;
diff --git a/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm b/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm
index ed95e39..e20818ca 100644
--- a/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm
+++ b/ios/chrome/browser/location_bar/ui_bundled/location_bar_coordinator.mm
@@ -414,12 +414,9 @@
       ios::TemplateURLServiceFactory::GetForProfile(self.profile);
   self.mediator.consumer = self.viewController;
   self.mediator.webStateList = self.webStateList;
-  if (base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdate) ||
-      base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdateV2)) {
-    PlaceholderService* placeholderService =
-        ios::PlaceholderServiceFactory::GetForProfile(self.profile);
-    self.mediator.placeholderService = placeholderService;
-  }
+  PlaceholderService* placeholderService =
+      ios::PlaceholderServiceFactory::GetForProfile(self.profile);
+  self.mediator.placeholderService = placeholderService;
 
   self.viewController.mutator = self.mediator;
 
diff --git a/ios/chrome/browser/location_bar/ui_bundled/location_bar_mediator.mm b/ios/chrome/browser/location_bar/ui_bundled/location_bar_mediator.mm
index 602f8d3..f4c60e68 100644
--- a/ios/chrome/browser/location_bar/ui_bundled/location_bar_mediator.mm
+++ b/ios/chrome/browser/location_bar/ui_bundled/location_bar_mediator.mm
@@ -93,10 +93,8 @@
   }
   _webStateListObserver = nullptr;
   _searchEngineObserver = nullptr;
-  if (base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdate) ||
-      base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdateV2)) {
-    self.placeholderService = nullptr;
-  }
+  self.placeholderService = nullptr;
+
   _fullscreenUIElements = nil;
 }
 
@@ -180,8 +178,6 @@
 }
 
 - (void)setPlaceholderService:(PlaceholderService*)placeholderService {
-  CHECK((base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdate) ||
-         base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdateV2)));
   _placeholderService = placeholderService;
 
   if (!placeholderService) {
@@ -249,10 +245,6 @@
 #pragma mark - PlaceholderServiceObserving
 
 - (void)placeholderImageUpdated {
-  if (!base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdateV2)) {
-    return;
-  }
-
   __weak __typeof(self) weakSelf = self;
   if (self.placeholderService) {
     self.placeholderService->FetchDefaultSearchEngineIcon(
@@ -343,8 +335,7 @@
 
 /// Updates the placeholder.
 - (void)updatePlaceholderType {
-  if (base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdateV2) &&
-      [self isCurrentPageNTP]) {
+  if ([self isCurrentPageNTP]) {
     [self.consumer setPlaceholderType:LocationBarPlaceholderType::
                                           kDefaultSearchEngineIcon];
     return;
diff --git a/ios/chrome/browser/location_bar/ui_bundled/location_bar_mediator_unittest.mm b/ios/chrome/browser/location_bar/ui_bundled/location_bar_mediator_unittest.mm
index cc7c350..63838b9b 100644
--- a/ios/chrome/browser/location_bar/ui_bundled/location_bar_mediator_unittest.mm
+++ b/ios/chrome/browser/location_bar/ui_bundled/location_bar_mediator_unittest.mm
@@ -99,12 +99,8 @@
 }
 
 // Tests that the consumer is updated with the placeholder icon immediately
-// upon being set, provided the `omnibox::kOmniboxMobileParityUpdateV2` feature
-// is enabled.
+// upon being set.
 TEST_F(LocationBarMediatorTest, SetConsumerUpdatesPlaceholderIconImmediately) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(omnibox::kOmniboxMobileParityUpdateV2);
-
   id mock_consumer = OCMProtocolMock(@protocol(LocationBarConsumer));
 
   OCMExpect(
diff --git a/ios/chrome/browser/location_bar/ui_bundled/location_bar_view_controller.mm b/ios/chrome/browser/location_bar/ui_bundled/location_bar_view_controller.mm
index 4c2737b..c7e3824 100644
--- a/ios/chrome/browser/location_bar/ui_bundled/location_bar_view_controller.mm
+++ b/ios/chrome/browser/location_bar/ui_bundled/location_bar_view_controller.mm
@@ -359,14 +359,12 @@
   [self registerForTraitChanges:@[ UITraitHorizontalSizeClass.class ]
                      withAction:@selector(sizeClassDidChange)];
 
-  if (base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdateV2)) {
-    _defaultSearchEngineIconView = [[UIImageView alloc] init];
-    _defaultSearchEngineIconView.translatesAutoresizingMaskIntoConstraints = NO;
-    _defaultSearchEngineIconView.contentMode = UIViewContentModeCenter;
-    AddSizeConstraints(
-        _defaultSearchEngineIconView,
-        CGSizeMake(kOmniboxLeadingImageSize + 12.0f, kOmniboxLeadingImageSize));
-  }
+  _defaultSearchEngineIconView = [[UIImageView alloc] init];
+  _defaultSearchEngineIconView.translatesAutoresizingMaskIntoConstraints = NO;
+  _defaultSearchEngineIconView.contentMode = UIViewContentModeCenter;
+  AddSizeConstraints(
+      _defaultSearchEngineIconView,
+      CGSizeMake(kOmniboxLeadingImageSize + 12.0f, kOmniboxLeadingImageSize));
 
   if (IsProactiveSuggestionsFrameworkEnabled()) {
     _locationBarSteadyView.pageActionMenuHandler = self.pageActionMenuHandler;
@@ -823,12 +821,8 @@
 
 // Computes correct placeholder text.
 - (NSString*)searchOrTypeURLPlaceholderText {
-  if (base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdate)) {
-    return l10n_util::GetNSStringF(IDS_OMNIBOX_EMPTY_HINT_WITH_DSE_NAME,
-                                   self.searchProviderName.cr_UTF16String);
-  } else {
-    return l10n_util::GetNSString(IDS_OMNIBOX_EMPTY_HINT);
-  }
+  return l10n_util::GetNSStringF(IDS_OMNIBOX_EMPTY_HINT_WITH_DSE_NAME,
+                                 self.searchProviderName.cr_UTF16String);
 }
 
 #pragma mark - UIContextMenuInteractionDelegate
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm
index 3a577ab..4767aa44 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm
@@ -790,12 +790,10 @@
   NTPMediator.NTPContentDelegate = self;
   NTPMediator.headerConsumer = self.headerViewController;
   NTPMediator.consumer = self.NTPViewController;
-  if (base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdate) ||
-      base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdateV2)) {
-    PlaceholderService* placeholderService =
-        ios::PlaceholderServiceFactory::GetForProfile(self.profile);
-    NTPMediator.placeholderService = placeholderService;
-  }
+  PlaceholderService* placeholderService =
+      ios::PlaceholderServiceFactory::GetForProfile(self.profile);
+  NTPMediator.placeholderService = placeholderService;
+
   [NTPMediator setUp];
 }
 
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm
index 5904aa17..f130ad74 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view.mm
@@ -84,10 +84,8 @@
 const CGFloat kHintLabelFakeboxTrailingSpace = 12.0f;
 
 // The constants for the constraints the leading-edge aligned UI elements.
-const CGFloat kHintLabelFakeboxLeadingSpace = 28.0;
 const CGFloat kHintLabelFakeboxLeadingSpaceWithIcon = 42.0;
 const CGFloat kHintLabelFakeboxLeadingSpaceWithPlus = 46.0;
-const CGFloat kHintLabelOmniboxLeadingSpace = 20.0;
 const CGFloat kHintLabelOmniboxLeadingSpaceWithIcon = 42.0;
 const CGFloat kHintLabelOmniboxLeadingSpaceWithWithPlus = 52.0;
 
@@ -487,8 +485,7 @@
     [self createPlusButton];
     leadingView = self.plusButton;
     leadingViewYOffset = -3;
-  } else if (base::FeatureList::IsEnabled(
-                 omnibox::kOmniboxMobileParityUpdateV2)) {
+  } else {
     _logoView = [[UIImageView alloc] init];
     _logoView.contentMode = UIViewContentModeScaleAspectFit;
     leadingView = _logoView;
@@ -1325,22 +1322,16 @@
 - (CGFloat)hintLabelFakeboxLeadingSpace {
   if ([self shouldShowPlusButton]) {
     return kHintLabelFakeboxLeadingSpaceWithPlus;
-  } else if (base::FeatureList::IsEnabled(
-                 omnibox::kOmniboxMobileParityUpdateV2)) {
-    return kHintLabelFakeboxLeadingSpaceWithIcon;
   } else {
-    return kHintLabelFakeboxLeadingSpace;
+    return kHintLabelFakeboxLeadingSpaceWithIcon;
   }
 }
 
 - (CGFloat)hintLabelOmniboxLeadingSpace {
   if ([self shouldShowPlusButton]) {
     return kHintLabelOmniboxLeadingSpaceWithWithPlus;
-  } else if (base::FeatureList::IsEnabled(
-                 omnibox::kOmniboxMobileParityUpdateV2)) {
-    return kHintLabelOmniboxLeadingSpaceWithIcon;
   } else {
-    return kHintLabelOmniboxLeadingSpace;
+    return kHintLabelOmniboxLeadingSpaceWithIcon;
   }
 }
 
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.mm
index 98b206a..89199998 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.mm
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_header_view_controller.mm
@@ -590,9 +590,6 @@
 }
 
 - (void)setDefaultSearchEngineImage:(UIImage*)image {
-  if (!base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdateV2)) {
-    return;
-  }
   // The header view might not be created yet. Store the logo image until it is
   // consumed.
   if (!self.headerView) {
@@ -1254,12 +1251,8 @@
 
 // Returns the omnibox placeholder text.
 - (NSString*)placeholderText {
-  if (base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdate)) {
-    return l10n_util::GetNSStringF(IDS_OMNIBOX_EMPTY_HINT_WITH_DSE_NAME,
-                                   self.defaultSearchEngineName.cr_UTF16String);
-  } else {
-    return l10n_util::GetNSString(IDS_OMNIBOX_EMPTY_HINT);
-  }
+  return l10n_util::GetNSStringF(IDS_OMNIBOX_EMPTY_HINT_WITH_DSE_NAME,
+                                 self.defaultSearchEngineName.cr_UTF16String);
 }
 
 @end
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_mediator.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_mediator.mm
index f6db77d..7dd8ef78 100644
--- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_mediator.mm
+++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_mediator.mm
@@ -435,10 +435,7 @@
   _backgroundCustomizationService = nullptr;
   _imageFetcherService = nullptr;
   _backgroundImageCacheService = nullptr;
-  if (base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdate) ||
-      base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdateV2)) {
-    self.placeholderService = nullptr;
-  }
+  self.placeholderService = nullptr;
   base::UmaHistogramBoolean("IOS.NTP.LandscapeMode", _wasNTPInLandscape);
 }
 
@@ -454,9 +451,6 @@
 }
 
 - (void)setPlaceholderService:(PlaceholderService*)placeholderService {
-  CHECK(base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdate) ||
-        base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdateV2));
-
   _placeholderService = placeholderService;
 
   if (!placeholderService) {
@@ -526,10 +520,6 @@
 #pragma mark - PlaceholderServiceObserving
 
 - (void)placeholderImageUpdated {
-  if (!base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdateV2)) {
-    return;
-  }
-
   // Show Default Search Engine favicon.
   // Remember what is the Default Search Engine provider that the icon is
   // for, in case the user changes Default Search Engine while this is being
diff --git a/ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.mm b/ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.mm
index ba53ed5..b846752 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.mm
@@ -451,6 +451,13 @@
     return;
   }
 
+  if (_omniboxPresentationContext == OmniboxPresentationContext::kLensOverlay) {
+    if (_omniboxClient->GetPageClassification(/*is_prefetch=*/false) ==
+        metrics::OmniboxEventProto::SEARCH_SIDE_PANEL_SEARCHBOX) {
+      return;
+    }
+  }
+
   // Early exit if a query is already in progress or the popup is already open
   // This is what allows this method to be called multiple times in multiple
   // code locations without harm. Exept if the event is user triggered by
diff --git a/ios/chrome/browser/omnibox/model/placeholder_service/placeholder_service.mm b/ios/chrome/browser/omnibox/model/placeholder_service/placeholder_service.mm
index 6b807a93..849e452 100644
--- a/ios/chrome/browser/omnibox/model/placeholder_service/placeholder_service.mm
+++ b/ios/chrome/browser/omnibox/model/placeholder_service/placeholder_service.mm
@@ -113,10 +113,6 @@
 }
 
 NSString* PlaceholderService::GetCurrentPlaceholderText() {
-  if (!base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdate)) {
-    return l10n_util::GetNSString(IDS_OMNIBOX_EMPTY_HINT);
-  }
-
   CHECK(template_url_service_);
 
   std::u16string provider_name = u"";
diff --git a/ios/chrome/browser/orchestrator/ui_bundled/omnibox_focus_orchestrator.h b/ios/chrome/browser/orchestrator/ui_bundled/omnibox_focus_orchestrator.h
index 779a54b..66a5d1ef 100644
--- a/ios/chrome/browser/orchestrator/ui_bundled/omnibox_focus_orchestrator.h
+++ b/ios/chrome/browser/orchestrator/ui_bundled/omnibox_focus_orchestrator.h
@@ -18,7 +18,7 @@
   kOther,
   kPinnedFakebox,
   kUnpinnedFakebox,
-  kNTPOmnibox,  // Note: only used when kOmniboxMobileParityUpdateV2 is enabled.
+  kNTPOmnibox,
 };
 
 // Orchestrator for the animation occurring when the omnibox is
diff --git a/ios/chrome/browser/providers/cobalt/chromium_cobalt.mm b/ios/chrome/browser/providers/cobalt/chromium_cobalt.mm
index a1646dd..363166d 100644
--- a/ios/chrome/browser/providers/cobalt/chromium_cobalt.mm
+++ b/ios/chrome/browser/providers/cobalt/chromium_cobalt.mm
@@ -35,6 +35,10 @@
   return nullptr;
 }
 
+web::CobaltController* GetCobaltController(ProfileIOS* profile) {
+  return nullptr;
+}
+
 ChromeCoordinator* CreateCobaltAlertCoordinator(
     UIViewController* base_view_controller,
     Browser* browser,
@@ -52,4 +56,8 @@
   return nil;
 }
 
+ObservingSceneAgent* CreateCobaltSceneAgent() {
+  return nil;
+}
+
 }  // namespace ios::provider
diff --git a/ios/chrome/browser/scene/ui/scene_view_controller.mm b/ios/chrome/browser/scene/ui/scene_view_controller.mm
index a73f5a7..45a1f87 100644
--- a/ios/chrome/browser/scene/ui/scene_view_controller.mm
+++ b/ios/chrome/browser/scene/ui/scene_view_controller.mm
@@ -485,7 +485,6 @@
   }
 
   CGRect contentFrame = UIEdgeInsetsInsetRect(frame, insets);
-  _appContentView.frame = contentFrame;
   _appContentContainerView.frame = contentFrame;
 }
 
@@ -548,7 +547,7 @@
   }
 
   CGFloat width = panelWidth + _assistantLeadingConstraint.constant;
-  return MAX(0, width + (_assistantVisible ? kAssistantContainerMargin : 0.0));
+  return MAX(0, width + kAssistantContainerMargin);
 }
 
 // Helper method for dismissal block when attempting to show the Gemini floaty
diff --git a/ios/chrome/browser/toolbar/coordinator/main_toolbar_coordinator.mm b/ios/chrome/browser/toolbar/coordinator/main_toolbar_coordinator.mm
index 7921b719..6b9e192 100644
--- a/ios/chrome/browser/toolbar/coordinator/main_toolbar_coordinator.mm
+++ b/ios/chrome/browser/toolbar/coordinator/main_toolbar_coordinator.mm
@@ -273,12 +273,7 @@
   [self.secondaryToolbarCoordinator start];
 
   if (!IsChromeNextIaEnabled()) {
-    if (base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdateV2)) {
-      self.orchestrator = [[OmniboxFocusOrchestratorParity alloc] init];
-    } else {
-      self.orchestrator = [[OmniboxFocusOrchestrator alloc] init];
-    }
-
+    self.orchestrator = [[OmniboxFocusOrchestratorParity alloc] init];
     [self updateOrchestratorAnimatee];
   }
 
@@ -1079,42 +1074,24 @@
 /// an incognito browser, the NTP is displayed, and whether the fakebox was
 /// pinned if it was selected.
 - (OmniboxFocusTrigger)omniboxFocusTrigger {
-  if (base::FeatureList::IsEnabled(omnibox::kOmniboxMobileParityUpdateV2)) {
-    web::WebState* webState =
-        self.browser->GetWebStateList()->GetActiveWebState();
-    if (!webState) {
-      return OmniboxFocusTrigger::kOther;
-    }
-    if (!IsVisibleURLNewTabPage(webState)) {
-      return OmniboxFocusTrigger::kOther;
-    }
-
-    // (De)focusing on NTP.
-
-    if (self.isOffTheRecord || !IsSplitToolbarMode(self.traitEnvironment)) {
-      return _focusedFromFakebox ? OmniboxFocusTrigger::kUnpinnedFakebox
-                                 : OmniboxFocusTrigger::kNTPOmnibox;
-    }
-
-    return _fakeboxPinned ? OmniboxFocusTrigger::kPinnedFakebox
-                          : OmniboxFocusTrigger::kUnpinnedFakebox;
-
-  } else {
-    if (self.isOffTheRecord || !IsSplitToolbarMode(self.traitEnvironment)) {
-      return _focusedFromFakebox ? OmniboxFocusTrigger::kUnpinnedFakebox
-                                 : OmniboxFocusTrigger::kOther;
-    }
-    web::WebState* webState =
-        self.browser->GetWebStateList()->GetActiveWebState();
-    if (!webState) {
-      return OmniboxFocusTrigger::kOther;
-    }
-    if (!IsVisibleURLNewTabPage(webState)) {
-      return OmniboxFocusTrigger::kOther;
-    }
-    return _fakeboxPinned ? OmniboxFocusTrigger::kPinnedFakebox
-                          : OmniboxFocusTrigger::kUnpinnedFakebox;
+  web::WebState* webState =
+      self.browser->GetWebStateList()->GetActiveWebState();
+  if (!webState) {
+    return OmniboxFocusTrigger::kOther;
   }
+  if (!IsVisibleURLNewTabPage(webState)) {
+    return OmniboxFocusTrigger::kOther;
+  }
+
+  // (De)focusing on NTP.
+
+  if (self.isOffTheRecord || !IsSplitToolbarMode(self.traitEnvironment)) {
+    return _focusedFromFakebox ? OmniboxFocusTrigger::kUnpinnedFakebox
+                               : OmniboxFocusTrigger::kNTPOmnibox;
+  }
+
+  return _fakeboxPinned ? OmniboxFocusTrigger::kPinnedFakebox
+                        : OmniboxFocusTrigger::kUnpinnedFakebox;
 }
 
 - (void)updateOrchestratorAnimatee {
diff --git a/ios/chrome/browser/toolbar/legacy/ui_bundled/adaptive_toolbar_view_controller.mm b/ios/chrome/browser/toolbar/legacy/ui_bundled/adaptive_toolbar_view_controller.mm
index 5d9f9a3c47..9336ade 100644
--- a/ios/chrome/browser/toolbar/legacy/ui_bundled/adaptive_toolbar_view_controller.mm
+++ b/ios/chrome/browser/toolbar/legacy/ui_bundled/adaptive_toolbar_view_controller.mm
@@ -375,6 +375,12 @@
   // No-op, should be handled by the primary toolbar.
 }
 
+#pragma mark - FullscreenBrowserAgentObserving
+
+- (void)fullscreenDidUpdateObscuredInsetRange:(FullscreenBrowserAgent*)agent {
+  [self updateAllButtonsVisibility];
+}
+
 #pragma mark - FullscreenUIElement
 
 - (void)updateForFullscreenProgress:(CGFloat)progress {
diff --git a/ios/chrome/browser/toolbar/ui/buttons/toolbar_button_factory.mm b/ios/chrome/browser/toolbar/ui/buttons/toolbar_button_factory.mm
index 45a4da9..9052d95c 100644
--- a/ios/chrome/browser/toolbar/ui/buttons/toolbar_button_factory.mm
+++ b/ios/chrome/browser/toolbar/ui/buttons/toolbar_button_factory.mm
@@ -14,7 +14,7 @@
 
 namespace {
 // Default point size for toolbar buttons.
-constexpr CGFloat kDefaultSymbolPointSize = 22;
+constexpr CGFloat kDefaultSymbolPointSize = 19;
 }  // namespace
 
 @implementation ToolbarButtonFactory
diff --git a/ios/chrome/browser/web/model/chrome_web_client.h b/ios/chrome/browser/web/model/chrome_web_client.h
index e0df6cc..abf6e89 100644
--- a/ios/chrome/browser/web/model/chrome_web_client.h
+++ b/ios/chrome/browser/web/model/chrome_web_client.h
@@ -81,6 +81,8 @@
       const override API_AVAILABLE(ios(18.4));
   web::JSErrorReportLoggingLevel GetJSErrorReportLoggingLevel(
       web::BrowserState* browser_state) const override;
+  web::CobaltController* GetCobaltController(
+      web::BrowserState* browser_state) const override;
   bool IsSmoothScrollingSupported() const override;
 
  private:
diff --git a/ios/chrome/browser/web/model/chrome_web_client.mm b/ios/chrome/browser/web/model/chrome_web_client.mm
index c90ecc0..3a9175c 100644
--- a/ios/chrome/browser/web/model/chrome_web_client.mm
+++ b/ios/chrome/browser/web/model/chrome_web_client.mm
@@ -681,6 +681,15 @@
   return web::JSErrorReportLoggingLevel::REPORT_WITHOUT_URL;
 }
 
+web::CobaltController* ChromeWebClient::GetCobaltController(
+    web::BrowserState* browser_state) const {
+  ProfileIOS* profile = ProfileIOS::FromBrowserState(browser_state);
+  return ios::provider::GetCobaltController(profile);
+}
+
 bool ChromeWebClient::IsSmoothScrollingSupported() const {
-  return ios::provider::IsFullscreenSmoothScrollingSupported();
+  // For the purpose of ChromeWebClient, FullscreenRefactoring can be
+  // considered the same as FullscreenSmoothScrolling.
+  return IsFullscreenRefactoringEnabled() ||
+         ios::provider::IsFullscreenSmoothScrollingSupported();
 }
diff --git a/ios/chrome/browser/webui/ui_bundled/policy/policy_ui_handler.mm b/ios/chrome/browser/webui/ui_bundled/policy/policy_ui_handler.mm
index 337c52f..4caa1b7 100644
--- a/ios/chrome/browser/webui/ui_bundled/policy/policy_ui_handler.mm
+++ b/ios/chrome/browser/webui/ui_bundled/policy/policy_ui_handler.mm
@@ -317,6 +317,10 @@
 }
 
 void PolicyUIHandler::RestartBrowser(const std::string& policies) {
+  if (!PolicyUI::ShouldLoadTestPage(&*profile_)) {
+    return;
+  }
+
   // Set policies to preference
   PrefService* prefs = GetApplicationContext()->GetLocalState();
   prefs->SetString(policy::policy_prefs::kLocalTestPoliciesForNextStartup,
diff --git a/ios/chrome/test/providers/cobalt/test_cobalt.mm b/ios/chrome/test/providers/cobalt/test_cobalt.mm
index a1646dd..363166d 100644
--- a/ios/chrome/test/providers/cobalt/test_cobalt.mm
+++ b/ios/chrome/test/providers/cobalt/test_cobalt.mm
@@ -35,6 +35,10 @@
   return nullptr;
 }
 
+web::CobaltController* GetCobaltController(ProfileIOS* profile) {
+  return nullptr;
+}
+
 ChromeCoordinator* CreateCobaltAlertCoordinator(
     UIViewController* base_view_controller,
     Browser* browser,
@@ -52,4 +56,8 @@
   return nil;
 }
 
+ObservingSceneAgent* CreateCobaltSceneAgent() {
+  return nil;
+}
+
 }  // namespace ios::provider
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
index 1d71eeb..0681c9d 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-3c415a111fe7523702fe403f7d5d5398849b8d87
\ No newline at end of file
+63ce5a9d06f2d50b4d51f2c0c3233c1707611016
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
index 6c520305..35a7121 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-09268c0d200cdc65ae59e29132731120e403b298
\ No newline at end of file
+12b72025991fa4f0f379ad6a917e7a237a90c01f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
index 5dd634248..158f6373 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-0dd2ee7efa29dfd619ca4c1512f394dbd9991331
\ No newline at end of file
+f7d84ec7c209fc190aeae6682b67f6ba5cd07b37
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
index 7ac396a5..b9c595a 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-eb56240f1e275061cd53db7a9b6d88a9055c8065
\ No newline at end of file
+625c4e364648f4e448d81f589fd0d752cf95ab35
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
index 8a34c63..38c8ccb0 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-5f6ab37cff1687ca5a1c36df0d9a2d2861f8baf0
\ No newline at end of file
+a754473406f6923c1e3e04c117dd7424a37f766d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
index 2a6f768..0b169c1 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-b9c1a2c457f6bddf7d5abc322dca2c77add8cce6
\ No newline at end of file
+f528077ea30fc72ff2a9bbba3d700c18338a8fbe
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
index 4faeaf76..768dea3 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-97708b3c44196d7ab67a09a27b44e5a65961ded3
\ No newline at end of file
+40d6a36889fb4382333791607e270831f3f23bc2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
index 7ec9dad..20c40fbe 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-35d03537137205c75fe4653767fc172f130f01e4
\ No newline at end of file
+aba0ea986afb5526088d68174ce314052fab1f90
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
index ec0f520..1f903ba5 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-6b99b437424c1395a09ef29d226460d93e6cab66
\ No newline at end of file
+14fe41eec82c48c13bdb4a6ae0034240a5f57510
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 4517c57..38fe612 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-80086af07ee1bddab88e3f57846e8583420d82ec
\ No newline at end of file
+593e56caef8a4cf3b835baaeaf46fd1c3d389609
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 43754bd..e6bb1ce 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-54b259e945d3e386a48a16858173735aab669aa1
\ No newline at end of file
+77e39d0fc0abe3286f4c077c5a258f3d5ff9aa35
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
index a2357c65..8c60fad 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-3f871fa782f41d22679c950e66f6ca1c813e1955
\ No newline at end of file
+ff75ee36ac2db8479acc54fe694a2e617507bcde
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index d19772b..0235f353 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-118c548f8b00d44b677fd7aa4ce204a05c6b0504
\ No newline at end of file
+b5e95a3ef7648d89a8dce253c2556d7dbd44f2bc
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 3c2f6c49..ad04a107 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-3d320ce1579bbfff63831256fe26f5efed8befae
\ No newline at end of file
+d9420ce568c22b26e03398277a42d686893bdc97
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index bb857eb..2cf7914 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-d0803a5f7b3bf4b5edc6e31e4165bd537ddf9498
\ No newline at end of file
+d590ee8c15237eeffe4a698919274225bc50c8f2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
index 518c41b..70445f59 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-ac27dd728855a18a80af2232e6513d5a1bc58d60
\ No newline at end of file
+2b8dba4bcdba15c32ca8f2ea58adecc3c021d127
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index f3270ee3..945aaae 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-92349e9127fb99d967219adec3f6f097cced59a6
\ No newline at end of file
+31b5ab442f01034d63a07b618bf41a8ec38c799f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
index dc1cba9..6c9e5c15 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-583de613e08f690b6ec6bf5c1ce0ae34f3d01f8f
\ No newline at end of file
+3a37f313b8128aeb72844a4ac92a65221b6e8e6b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
index fcaee23..ee8ddf0 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-ee8f6da4e5c2707aaeb110b6e236a16a7373ed5b
\ No newline at end of file
+d1e92f0e4b06b35bc30870f0b00a47d0d6975af4
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
index aff7140a..e2fa561 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-44a49e8c042ea30b2a42b86675f890a5779d93f2
\ No newline at end of file
+3fedb0e12829ab7f2604337ceaa27ee68ae3d63e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 42773afa..6966a76 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-4ec8badf44f926b164d0366c30b24603dcd1675c
\ No newline at end of file
+ac076f300151616de0b8fe09f8d9dc1f620130a0
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
index 146331b3..bb7d17f 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-8f5b7daa940972311abc77fc43a761037b992958
\ No newline at end of file
+f5fcdfd9ded3921a42f229d343a9e8e71a5051a4
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 46835e2..520eee32 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-a838c24f3224ea2eb9d2a0839862493548e7b5af
\ No newline at end of file
+79d12ba00d89f174e0e0954ec4d9ad85ffbcd32c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 978529f9..6a578073 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-8589a49a2cd550c7c25af1de6510b4c1ffb2c2b5
\ No newline at end of file
+e5ad12e79886cc98c00fcb2ba8185078afea2e42
\ No newline at end of file
diff --git a/ios/public/provider/chrome/browser/cobalt/cobalt_api.h b/ios/public/provider/chrome/browser/cobalt/cobalt_api.h
index 2147bb5..e9d8aac 100644
--- a/ios/public/provider/chrome/browser/cobalt/cobalt_api.h
+++ b/ios/public/provider/chrome/browser/cobalt/cobalt_api.h
@@ -15,9 +15,11 @@
 class ProfileIOS;
 class TabHelperAttacher;
 @class UIViewController;
+@class ObservingSceneAgent;
 
 namespace web {
 class JavaScriptFeature;
+class CobaltController;
 }  // namespace web
 
 // Completion block for Cobalt alerts, called with `granted` set to true if the
@@ -59,6 +61,9 @@
 web::JavaScriptFeature* GetCobaltJavascriptFeatureForProfile(
     ProfileIOS* profile);
 
+// Returns the Cobalt controller for `profile`.
+web::CobaltController* GetCobaltController(ProfileIOS* profile);
+
 // Returns the coordinator for Cobalt alerts.
 ChromeCoordinator* CreateCobaltAlertCoordinator(
     UIViewController* base_view_controller,
@@ -74,6 +79,9 @@
     UIViewController* popup_view_controller,
     CobaltPopupCompletion completion);
 
+// Returns the Cobalt scene agent.
+ObservingSceneAgent* CreateCobaltSceneAgent();
+
 }  // namespace ios::provider
 
 #endif  // IOS_PUBLIC_PROVIDER_CHROME_BROWSER_COBALT_COBALT_API_H_
diff --git a/ios/public/provider/web/cobalt_api.h b/ios/public/provider/web/cobalt_api.h
index e9b5e0a..3836eda 100644
--- a/ios/public/provider/web/cobalt_api.h
+++ b/ios/public/provider/web/cobalt_api.h
@@ -7,17 +7,19 @@
 
 #import <Foundation/Foundation.h>
 
-namespace web {
-class BrowserState;
-}
 @class WKWebViewConfiguration;
 
+namespace web {
+class CobaltController;
+}
+
 namespace web::provider {
 
-// Initializes Cobalt in `configuration` for the given `browser_state`.
+// Initializes Cobalt in `configuration` using the given `cobalt_controller`.
 void InitializeCobaltInWKWebViewConfiguration(
     WKWebViewConfiguration* configuration,
-    BrowserState* browser_state);
+    bool is_off_the_record,
+    web::CobaltController* cobalt_controller);
 
 // Returns the list of origins that are allowed to use Cobalt.
 NSArray<NSString*>* GetCobaltOriginList();
diff --git a/ios/web/providers/web_cobalt.mm b/ios/web/providers/web_cobalt.mm
index e5f16e4..75df11b 100644
--- a/ios/web/providers/web_cobalt.mm
+++ b/ios/web/providers/web_cobalt.mm
@@ -8,7 +8,8 @@
 
 void InitializeCobaltInWKWebViewConfiguration(
     WKWebViewConfiguration* configuration,
-    BrowserState* browser_state) {
+    bool is_off_the_record,
+    web::CobaltController* cobalt_controller) {
   // Nothing to do.
 }
 
diff --git a/ios/web/public/web_client.h b/ios/web/public/web_client.h
index d0dda14c..677782f 100644
--- a/ios/web/public/web_client.h
+++ b/ios/web/public/web_client.h
@@ -43,6 +43,7 @@
 
 class BrowserState;
 class BrowserURLRewriter;
+class CobaltController;
 class JavaScriptFeature;
 class WebClient;
 class WebMainParts;
@@ -235,6 +236,10 @@
   virtual JSErrorReportLoggingLevel GetJSErrorReportLoggingLevel(
       BrowserState* browser_state) const;
 
+  // Returns the Cobalt controller for the given `browser_state`.
+  virtual CobaltController* GetCobaltController(
+      BrowserState* browser_state) const;
+
   // Returns whether smooth scrolling is supported.
   virtual bool IsSmoothScrollingSupported() const;
 };
diff --git a/ios/web/web_client.mm b/ios/web/web_client.mm
index e417db7..2083182 100644
--- a/ios/web/web_client.mm
+++ b/ios/web/web_client.mm
@@ -152,6 +152,11 @@
   return JSErrorReportLoggingLevel::NONE;
 }
 
+CobaltController* WebClient::GetCobaltController(
+    BrowserState* browser_state) const {
+  return nullptr;
+}
+
 bool WebClient::IsSmoothScrollingSupported() const {
   return false;
 }
diff --git a/ios/web/web_state/ui/wk_web_view_configuration_provider.mm b/ios/web/web_state/ui/wk_web_view_configuration_provider.mm
index 2c028363..6ca7c0c 100644
--- a/ios/web/web_state/ui/wk_web_view_configuration_provider.mm
+++ b/ios/web/web_state/ui/wk_web_view_configuration_provider.mm
@@ -202,8 +202,10 @@
   UpdateScripts();
 
   if (web::features::IsCobaltEnabled()) {
-    web::provider::InitializeCobaltInWKWebViewConfiguration(configuration_,
-                                                            browser_state_);
+    web::CobaltController* controller =
+        GetWebClient()->GetCobaltController(browser_state_);
+    web::provider::InitializeCobaltInWKWebViewConfiguration(
+        configuration_, browser_state_->IsOffTheRecord(), controller);
   }
 
   if (!scheme_handler_) {
diff --git a/ios_internal b/ios_internal
index 83596e0..7d651e3 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 83596e01ff4bbb77fee10fee3562d12542e0483f
+Subproject commit 7d651e31bd37875eaad1c2aa117a962033c7e163
diff --git a/media/capture/video/android/video_capture_device_android.cc b/media/capture/video/android/video_capture_device_android.cc
index b3b9f48..5fa8692 100644
--- a/media/capture/video/android/video_capture_device_android.cc
+++ b/media/capture/video/android/video_capture_device_android.cc
@@ -347,21 +347,26 @@
   const int y_plane_length = width * height;
   const int uv_plane_length = y_plane_length / 4;
   const int buffer_length = y_plane_length + uv_plane_length * 2;
-  auto buffer = base::HeapArray<uint8_t>::Uninit(
-      base::checked_cast<size_t>(buffer_length));
+  const size_t unsigned_buffer_length =
+      base::checked_cast<size_t>(buffer_length);
 
-  auto dst_y_span = buffer.subspan(0, y_plane_length);
-  auto dst_u_span = buffer.subspan(y_plane_length, uv_plane_length);
+  if (i420_buffer_.size() < unsigned_buffer_length) {
+    i420_buffer_ = base::HeapArray<uint8_t>::Uninit(unsigned_buffer_length);
+  }
+
+  auto dst_y_span = i420_buffer_.subspan(0, y_plane_length);
+  auto dst_u_span = i420_buffer_.subspan(y_plane_length, uv_plane_length);
   auto dst_v_span =
-      buffer.subspan(y_plane_length + uv_plane_length, uv_plane_length);
+      i420_buffer_.subspan(y_plane_length + uv_plane_length, uv_plane_length);
 
   libyuv::Android420ToI420(y_src, y_stride, u_src, uv_row_stride, v_src,
                            uv_row_stride, uv_pixel_stride, dst_y_span.data(),
                            width, dst_u_span.data(), width / 2,
                            dst_v_span.data(), width / 2, width, height);
 
-  SendIncomingDataToClient(buffer.data(), buffer_length, rotation, current_time,
-                           capture_time, ColorSpaceFromADataSpace(data_space));
+  SendIncomingDataToClient(i420_buffer_.data(), buffer_length, rotation,
+                           current_time, capture_time,
+                           ColorSpaceFromADataSpace(data_space));
 }
 
 void VideoCaptureDeviceAndroid::OnHardwareBufferAvailableOnMainThread(
diff --git a/media/capture/video/android/video_capture_device_android.h b/media/capture/video/android/video_capture_device_android.h
index e8e40dc5..e95cc896 100644
--- a/media/capture/video/android/video_capture_device_android.h
+++ b/media/capture/video/android/video_capture_device_android.h
@@ -12,6 +12,7 @@
 
 #include "base/android/scoped_java_ref.h"
 #include "base/containers/flat_map.h"
+#include "base/containers/heap_array.h"
 #include "base/memory/weak_ptr.h"
 #include "base/synchronization/lock.h"
 #include "base/threading/thread.h"
@@ -223,6 +224,9 @@
   const VideoCaptureDeviceDescriptor device_descriptor_;
   VideoCaptureFormat capture_format_;
 
+  // Buffer used for copying I420 frame data, to avoid allocations on every frame.
+  base::HeapArray<uint8_t> i420_buffer_;
+
   // Java VideoCaptureAndroid instance.
   base::android::ScopedJavaLocalRef<jobject> j_capture_;
 
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
index 5b679db..d6580a7 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
@@ -965,6 +965,19 @@
                                        frame->storage_type())})});
       return;
     }
+    constexpr VideoPixelFormat kExpectedFormats[] = {
+        PIXEL_FORMAT_I420,
+        PIXEL_FORMAT_NV12,
+    };
+    const bool is_expected_format =
+        std::ranges::contains(kExpectedFormats, frame->format());
+    if (!is_expected_format) {
+      SetErrorState(
+          {EncoderStatus::Codes::kInvalidInputFrame,
+           base::StrCat({"Unexpected format: ",
+                         VideoPixelFormatToString(frame->format())})});
+      return;
+    }
 
     if (!ReconfigureFormatIfNeeded(*frame)) {
       SetErrorState({EncoderStatus::Codes::kUnsupportedFrameFormat,
diff --git a/media/gpu/vaapi/vp8_vaapi_video_encoder_delegate.cc b/media/gpu/vaapi/vp8_vaapi_video_encoder_delegate.cc
index 0c7a5697..91c21cd 100644
--- a/media/gpu/vaapi/vp8_vaapi_video_encoder_delegate.cc
+++ b/media/gpu/vaapi/vp8_vaapi_video_encoder_delegate.cc
@@ -161,8 +161,8 @@
     Vp8FrameHeader& frame_hdr,
     Vp8Metadata& metadata,
     std::array<bool, kNumVp8ReferenceBuffers>& ref_frames_used) {
-  DCHECK_GE(num_layers, kMinSupportedVP8TemporalLayers);
-  DCHECK_LE(num_layers, kMaxSupportedVP8TemporalLayers);
+  CHECK_GE(num_layers, kMinSupportedVP8TemporalLayers);
+  CHECK_LE(num_layers, kMaxSupportedVP8TemporalLayers);
   enum BufferFlags : uint8_t {
     kNone = 0,
     kReference = 1,
@@ -461,6 +461,13 @@
   if (VP8TLEncodingIsEnabled()) {
     const size_t new_num_temporal_layers =
         GetActiveTemporalLayers(bitrate_allocation);
+    if (new_num_temporal_layers > kMaxSupportedVP8TemporalLayers ||
+        new_num_temporal_layers < kMinSupportedVP8TemporalLayers) {
+      VLOGF(1) << "Unsupported number of temporal layers: "
+               << new_num_temporal_layers;
+      return false;
+    }
+
     if (new_num_temporal_layers != num_temporal_layers_) {
       VLOGF(2) << "The number of temporal layers is changed, from "
                << base::strict_cast<int>(num_temporal_layers_) << " to "
diff --git a/net/base/features.cc b/net/base/features.cc
index e6d7d6d6..a145622 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -104,6 +104,9 @@
 
 BASE_FEATURE(kNetworkQualityEstimator, base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kNetworkQualityEstimatorIsPrivateHostCache,
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 const base::FeatureParam<int> kRecentHTTPThresholdInSeconds{
     &kNetworkQualityEstimator, "RecentHTTPThresholdInSeconds", -1};
 const base::FeatureParam<int> kRecentTransportThresholdInSeconds{
diff --git a/net/base/features.h b/net/base/features.h
index 627ed8c..4c976a96 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -145,6 +145,9 @@
 // quality estimator (NQE).
 NET_EXPORT BASE_DECLARE_FEATURE(kNetworkQualityEstimator);
 
+// Enables caching of IsPrivateHost() results in NetworkQualityEstimator.
+NET_EXPORT BASE_DECLARE_FEATURE(kNetworkQualityEstimatorIsPrivateHostCache);
+
 // The maximum age in seconds of observations to be used for calculating the
 // HTTP RTT from the historical data.
 // Negative value means infinite. i.e. all data are used.
diff --git a/net/dns/public/doh_provider_entry.cc b/net/dns/public/doh_provider_entry.cc
index 386c76b..f48d2604 100644
--- a/net/dns/public/doh_provider_entry.cc
+++ b/net/dns/public/doh_provider_entry.cc
@@ -54,10 +54,14 @@
 
 }  // namespace
 
-#define MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(feature_name, feature_state) \
-  ([]() {                                                                  \
-    static BASE_FEATURE(k##feature_name, #feature_name, feature_state);    \
-    return &k##feature_name;                                               \
+// Note: use BASE_FEATURE as a suffix for this macro name because some tooling
+// identifies feature definitions by searching for that string, so if our macro
+// matches the standard one we have a better chance of our feature flags being
+// detected by those tools.
+#define MAKE_STATIC_STORAGE_BASE_FEATURE(feature_name, feature_state) \
+  ([]() {                                                             \
+    static BASE_FEATURE(feature_name, feature_state);                 \
+    return &feature_name;                                             \
   })()
 
 // static
@@ -73,8 +77,8 @@
   static const auto entries(base::NoDestructor(std::to_array<DohProviderEntry>(
       {{
            "CleanBrowsingAdult",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderCleanBrowsingAdult, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderCleanBrowsingAdult,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"185.228.168.10", "185.228.169.11", "2a0d:2a00:1::1",
             "2a0d:2a00:2::1"},
            /*dns_over_tls_hostnames=*/{"adult-filter-dns.cleanbrowsing.org"},
@@ -86,9 +90,8 @@
        },
        {
            "CleanBrowsingFamily",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderCleanBrowsingFamily,
-               base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderCleanBrowsingFamily,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"185.228.168.168", "185.228.169.168",
             "2a0d:2a00:1::", "2a0d:2a00:2::"},
            /*dns_over_tls_hostnames=*/{"family-filter-dns.cleanbrowsing.org"},
@@ -100,9 +103,8 @@
        },
        {
            "CleanBrowsingSecure",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderCleanBrowsingSecure,
-               base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderCleanBrowsingSecure,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"185.228.168.9", "185.228.169.9", "2a0d:2a00:1::2",
             "2a0d:2a00:2::2"},
            /*dns_over_tls_hostnames=*/{"security-filter-dns.cleanbrowsing.org"},
@@ -114,8 +116,8 @@
        },
        {
            "Cloudflare",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderCloudflare, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderCloudflare,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"1.1.1.1", "1.0.0.1", "2606:4700:4700::1111",
             "2606:4700:4700::1001"},
            /*dns_over_tls_hostnames=*/
@@ -129,8 +131,8 @@
        },
        {
            "CloudflareFamily",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderCloudflareFamily, base::FEATURE_DISABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderCloudflareFamily,
+                                            base::FEATURE_DISABLED_BY_DEFAULT),
            {"1.1.1.3", "1.0.0.3", "2606:4700:4700::1113",
             "2606:4700:4700::1003"},
            {"family.cloudflare-dns.com"},
@@ -142,9 +144,8 @@
        },
        {
            "CloudflareSecurity",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderCloudflareSecurity,
-               base::FEATURE_DISABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderCloudflareSecurity,
+                                            base::FEATURE_DISABLED_BY_DEFAULT),
            {"1.1.1.2", "1.0.0.2", "2606:4700:4700::1112",
             "2606:4700:4700::1002"},
            {"security.cloudflare-dns.com"},
@@ -156,8 +157,8 @@
        },
        {
            "Comcast",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderComcast, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderComcast,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"75.75.75.75", "75.75.76.76", "2001:558:feed::1",
             "2001:558:feed::2"},
            /*dns_over_tls_hostnames=*/{"dot.xfinity.com"},
@@ -169,8 +170,8 @@
        },
        {
            "Cox",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderCox, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderCox,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"68.105.28.11", "68.105.28.12", "2001:578:3f::30"},
            /*dns_over_tls_hostnames=*/{"dot.cox.net"},
            "https://doh.cox.net/dns-query",
@@ -181,8 +182,8 @@
        },
        {
            "Cznic",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderCznic, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderCznic,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"185.43.135.1", "193.17.47.1", "2001:148f:fffe::1",
             "2001:148f:ffff::1"},
            /*dns_over_tls_hostnames=*/{"odvr.nic.cz"},
@@ -193,8 +194,8 @@
            /*display_countries=*/{"CZ"},
        },
        {"DNS4EU",
-        MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(DohProviderDns4eu,
-                                              base::FEATURE_ENABLED_BY_DEFAULT),
+        MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderDns4eu,
+                                         base::FEATURE_ENABLED_BY_DEFAULT),
         /*dns_over_53_server_ip_strs=*/{},
         /*dns_over_tls_hostnames=*/{},
         "https://protective.joindns4.eu/dns-query{?dns}",
@@ -212,8 +213,8 @@
          "2a13:1001::86:54:11:201"}},
        {
            "Dnssb",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderDnssb, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderDnssb,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"185.222.222.222", "45.11.45.11", "2a09::", "2a11::"},
            /*dns_over_tls_hostnames=*/{"dns.sb"},
            "https://doh.dns.sb/dns-query{?dns}",
@@ -224,8 +225,8 @@
        },
        {
            "Google",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderGoogle, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderGoogle,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"8.8.8.8", "8.8.4.4", "2001:4860:4860::8888",
             "2001:4860:4860::8844"},
            /*dns_over_tls_hostnames=*/
@@ -239,8 +240,8 @@
        },
        {
            "GoogleDns64",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderGoogleDns64, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderGoogleDns64,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"2001:4860:4860::64", "2001:4860:4860::6464"},
            /*dns_over_tls_hostnames=*/{"dns64.dns.google"},
            "https://dns64.dns.google/dns-query{?dns}",
@@ -251,8 +252,8 @@
        },
        {
            "Iij",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderIij, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderIij,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            /*dns_over_53_server_ip_strs=*/{},
            /*dns_over_tls_hostnames=*/{},
            "https://public.dns.iij.jp/dns-query",
@@ -262,8 +263,8 @@
            /*display_countries=*/{"JP"},
        },
        {"Levonet",
-        MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(DohProviderLevonet,
-                                              base::FEATURE_ENABLED_BY_DEFAULT),
+        MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderLevonet,
+                                         base::FEATURE_ENABLED_BY_DEFAULT),
         {"109.236.119.2", "109.236.120.2", "2a02:6ca3:0:1::2",
          "2a02:6ca3:0:2::2"},
         /*dns_over_tls_hostnames=*/{},
@@ -276,8 +277,8 @@
          "2a02:6ca3:0:2::2"}},
        {
            "NextDns",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderNextDns, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderNextDns,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            /*dns_over_53_server_ip_strs=*/{},
            /*dns_over_tls_hostnames=*/{},
            "https://chromium.dns.nextdns.io",
@@ -288,8 +289,8 @@
        },
        {
            "OpenDNS",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderOpenDNS, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderOpenDNS,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"208.67.222.222", "208.67.220.220", "2620:119:35::35",
             "2620:119:53::53"},
            /*dns_over_tls_hostnames=*/{},
@@ -302,8 +303,8 @@
        },
        {
            "OpenDNSFamily",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderOpenDNSFamily, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderOpenDNSFamily,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"208.67.222.123", "208.67.220.123", "2620:119:35::123",
             "2620:119:53::123"},
            /*dns_over_tls_hostnames=*/{},
@@ -315,8 +316,8 @@
        },
        {
            "Quad9Cdn",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderQuad9Cdn, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderQuad9Cdn,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"9.9.9.11", "149.112.112.11", "2620:fe::11", "2620:fe::fe:11"},
            /*dns_over_tls_hostnames=*/{"dns11.quad9.net"},
            "https://dns11.quad9.net/dns-query",
@@ -327,8 +328,8 @@
        },
        {
            "Quad9Insecure",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderQuad9Insecure, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderQuad9Insecure,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"9.9.9.10", "149.112.112.10", "2620:fe::10", "2620:fe::fe:10"},
            /*dns_over_tls_hostnames=*/{"dns10.quad9.net"},
            "https://dns10.quad9.net/dns-query",
@@ -339,8 +340,8 @@
        },
        {
            "Quad9Secure",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderQuad9Secure, base::FEATURE_DISABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderQuad9Secure,
+                                            base::FEATURE_DISABLED_BY_DEFAULT),
            {"9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9"},
            /*dns_over_tls_hostnames=*/{"dns.quad9.net", "dns9.quad9.net"},
            "https://dns.quad9.net/dns-query",
@@ -351,8 +352,8 @@
        },
        {
            "Quickline",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderQuickline, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderQuickline,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"212.60.61.246", "212.60.63.246", "2001:1a88:10:ffff::1",
             "2001:1a88:10:ffff::2"},
            /*dns_over_tls_hostnames=*/{"dot.quickline.ch"},
@@ -364,8 +365,8 @@
        },
        {
            "Spectrum1",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderSpectrum1, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderSpectrum1,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"209.18.47.61", "209.18.47.62", "2001:1998:0f00:0001::1",
             "2001:1998:0f00:0002::1"},
            /*dns_over_tls_hostnames=*/{},
@@ -377,8 +378,8 @@
        },
        {
            "Spectrum2",
-           MAKE_BASE_FEATURE_WITH_STATIC_STORAGE(
-               DohProviderSpectrum2, base::FEATURE_ENABLED_BY_DEFAULT),
+           MAKE_STATIC_STORAGE_BASE_FEATURE(kDohProviderSpectrum2,
+                                            base::FEATURE_ENABLED_BY_DEFAULT),
            {"209.18.47.61", "209.18.47.62", "2001:1998:0f00:0001::1",
             "2001:1998:0f00:0002::1"},
            /*dns_over_tls_hostnames=*/{},
@@ -397,7 +398,7 @@
   return *providers;
 }
 
-#undef MAKE_BASE_FEATURE_WITH_STATIC_STORAGE
+#undef MAKE_STATIC_STORAGE_BASE_FEATURE
 
 // static
 DohProviderEntry DohProviderEntry::ConstructForTesting(
diff --git a/net/nqe/network_quality_estimator.cc b/net/nqe/network_quality_estimator.cc
index 45fa0189..10b27bcd 100644
--- a/net/nqe/network_quality_estimator.cc
+++ b/net/nqe/network_quality_estimator.cc
@@ -635,8 +635,7 @@
     const URLRequest& request) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  bool private_network_request =
-      nqe::internal::IsRequestForPrivateHost(request, net_log_);
+  bool private_network_request = IsPrivateHost(request);
 
   return (use_localhost_requests_ || !private_network_request) &&
          // Verify that response headers are received, so it can be ensured that
@@ -664,6 +663,7 @@
                                network_quality_, effective_connection_type_));
 
   // Clear the local state.
+  is_private_host_cache_.Clear();
   last_connection_change_ = tick_clock_->NowTicks();
   http_downstream_throughput_kbps_observations_.Clear();
   for (auto& rtt_ms_observation : rtt_ms_observations_)
@@ -1668,4 +1668,29 @@
   return p2p_connections_count_;
 }
 
+bool NetworkQualityEstimator::IsPrivateHost(const URLRequest& request) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  SCOPED_UMA_HISTOGRAM_TIMER("NQE.IsPrivateHost.Duration");
+  std::string host(request.url().host());
+
+  if (base::FeatureList::IsEnabled(
+          features::kNetworkQualityEstimatorIsPrivateHostCache)) {
+    auto it = is_private_host_cache_.Get(host);
+    if (it != is_private_host_cache_.end()) {
+      base::UmaHistogramBoolean("NQE.IsPrivateHost.CacheHit", true);
+      return it->second;
+    }
+    base::UmaHistogramBoolean("NQE.IsPrivateHost.CacheHit", false);
+  }
+
+  bool is_private = nqe::internal::IsRequestForPrivateHost(request, net_log_);
+
+  if (base::FeatureList::IsEnabled(
+          features::kNetworkQualityEstimatorIsPrivateHostCache)) {
+    is_private_host_cache_.Put(host, is_private);
+  }
+
+  return is_private;
+}
+
 }  // namespace net
diff --git a/net/nqe/network_quality_estimator.h b/net/nqe/network_quality_estimator.h
index 4cd82019..fcc2feb 100644
--- a/net/nqe/network_quality_estimator.h
+++ b/net/nqe/network_quality_estimator.h
@@ -15,6 +15,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/containers/lru_cache.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
@@ -196,6 +197,9 @@
   // Notifies NetworkQualityEstimator that `request` will be destroyed.
   void NotifyURLRequestDestroyed(const URLRequest& request);
 
+  // Returns true if the request is for a private host.
+  bool IsPrivateHost(const URLRequest& request) const;
+
   // Adds `rtt_observer` to the list of round trip time observers. Must be
   // called on the IO thread.
   void AddRTTObserver(RTTObserver* rtt_observer);
@@ -683,6 +687,9 @@
   std::unordered_map<raw_ptr<const URLRequest>, base::TimeTicks>
       waiting_async_notify_headers_received_;
 
+  // Cache for IsPrivateHost results.
+  mutable base::LRUCache<std::string, bool> is_private_host_cache_{64};
+
   base::WeakPtrFactory<NetworkQualityEstimator> weak_ptr_factory_{this};
 };
 
diff --git a/net/nqe/network_quality_estimator_unittest.cc b/net/nqe/network_quality_estimator_unittest.cc
index 8c4a628..9b4e3d1 100644
--- a/net/nqe/network_quality_estimator_unittest.cc
+++ b/net/nqe/network_quality_estimator_unittest.cc
@@ -26,6 +26,7 @@
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/run_until.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
@@ -33,6 +34,7 @@
 #include "net/base/load_flags.h"
 #include "net/base/network_activity_monitor.h"
 #include "net/base/network_change_notifier.h"
+#include "net/dns/mock_host_resolver.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
 #include "net/http/http_status_code.h"
@@ -2900,4 +2902,113 @@
   estimator.RemoveThroughputObserver(&throughput_observer);
 }
 
+// Helper class to count the number of DNS resolution requests. Used to verify
+// that the IsPrivateHost cache effectively suppresses redundant lookups.
+class CountingHostResolver : public MockHostResolver {
+ public:
+  CountingHostResolver() = default;
+  ~CountingHostResolver() override = default;
+
+  int num_resolve_calls() const { return num_resolve_calls_; }
+
+  std::unique_ptr<ResolveHostRequest> CreateRequest(
+      url::SchemeHostPort host,
+      NetworkAnonymizationKey network_anonymization_key,
+      NetLogWithSource net_log,
+      std::optional<ResolveHostParameters> optional_parameters) override {
+    num_resolve_calls_++;
+    return MockHostResolver::CreateRequest(
+        std::move(host), std::move(network_anonymization_key),
+        std::move(net_log), std::move(optional_parameters));
+  }
+
+ private:
+  int num_resolve_calls_ = 0;
+};
+
+class NetworkQualityEstimatorIsPrivateHostCacheTest
+    : public NetworkQualityEstimatorTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  NetworkQualityEstimatorIsPrivateHostCacheTest() {
+    if (IsCacheEnabled()) {
+      feature_list_.InitAndEnableFeature(
+          features::kNetworkQualityEstimatorIsPrivateHostCache);
+    } else {
+      feature_list_.InitAndDisableFeature(
+          features::kNetworkQualityEstimatorIsPrivateHostCache);
+    }
+  }
+
+  bool IsCacheEnabled() const { return GetParam(); }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+TEST_P(NetworkQualityEstimatorIsPrivateHostCacheTest, IsPrivateHostCaching) {
+  base::HistogramTester histogram_tester;
+
+  TestNetworkQualityEstimator estimator;
+  TestDelegate test_delegate;
+  auto host_resolver = std::make_unique<CountingHostResolver>();
+  CountingHostResolver* host_resolver_ptr = host_resolver.get();
+
+  // Set up the context with our counting host resolver.
+  auto context_builder = CreateTestURLRequestContextBuilder();
+  context_builder->set_network_quality_estimator(&estimator);
+  context_builder->set_host_resolver(std::move(host_resolver));
+  auto context = context_builder->Build();
+
+  GURL url("http://www.google.com");
+  std::unique_ptr<URLRequest> request(context->CreateRequest(
+      url, DEFAULT_PRIORITY, &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
+
+  // 1. First call: Should always trigger a physical resolution.
+  estimator.IsPrivateHost(*request);
+  EXPECT_EQ(1, host_resolver_ptr->num_resolve_calls());
+  if (IsCacheEnabled()) {
+    histogram_tester.ExpectBucketCount("NQE.IsPrivateHost.CacheHit", false, 1);
+    histogram_tester.ExpectBucketCount("NQE.IsPrivateHost.CacheHit", true, 0);
+  }
+  histogram_tester.ExpectTotalCount("NQE.IsPrivateHost.Duration", 1);
+
+  // 2. Second call for the same host: Should be cached if the feature is ON.
+  estimator.IsPrivateHost(*request);
+  if (IsCacheEnabled()) {
+    histogram_tester.ExpectBucketCount("NQE.IsPrivateHost.CacheHit", false, 1);
+    histogram_tester.ExpectBucketCount("NQE.IsPrivateHost.CacheHit", true, 1);
+    EXPECT_EQ(1, host_resolver_ptr->num_resolve_calls());
+  } else {
+    EXPECT_EQ(2, host_resolver_ptr->num_resolve_calls());
+  }
+
+  // 3. Network change: Should clear the cache if it exists.
+  estimator.SimulateNetworkChange(
+      NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "wifi");
+
+  // 4. Third call after network change: Should resolve again because host
+  // "private-ness" might have changed on the new network.
+  estimator.IsPrivateHost(*request);
+  if (IsCacheEnabled()) {
+    // Resolved once more (Total 2)
+    EXPECT_EQ(2, host_resolver_ptr->num_resolve_calls());
+  } else {
+    // Just continues to resolve (Total 3)
+    EXPECT_EQ(3, host_resolver_ptr->num_resolve_calls());
+  }
+
+  // 5. Fourth call: Should be cached again if the feature is ON.
+  estimator.IsPrivateHost(*request);
+  if (IsCacheEnabled()) {
+    EXPECT_EQ(2, host_resolver_ptr->num_resolve_calls());
+  } else {
+    EXPECT_EQ(4, host_resolver_ptr->num_resolve_calls());
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         NetworkQualityEstimatorIsPrivateHostCacheTest,
+                         testing::Bool());
+
 }  // namespace net
diff --git a/net/nqe/throughput_analyzer.cc b/net/nqe/throughput_analyzer.cc
index dd4d9e70..d4b7b46 100644
--- a/net/nqe/throughput_analyzer.cc
+++ b/net/nqe/throughput_analyzer.cc
@@ -400,7 +400,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   bool private_network_request =
-      nqe::internal::IsRequestForPrivateHost(request, net_log_);
+      network_quality_estimator_->IsPrivateHost(request);
 
   return !(use_localhost_requests_for_tests_ || !private_network_request) ||
          request.creation_time() < last_connection_change_;
diff --git a/services/network/p2p/socket_tcp.cc b/services/network/p2p/socket_tcp.cc
index be8dd90..a9ae16a 100644
--- a/services/network/p2p/socket_tcp.cc
+++ b/services/network/p2p/socket_tcp.cc
@@ -25,7 +25,6 @@
 #include "services/network/proxy_resolving_client_socket.h"
 #include "services/network/proxy_resolving_client_socket_factory.h"
 #include "services/network/public/cpp/p2p_param_traits.h"
-#include "third_party/webrtc/media/base/rtp_utils.h"
 #include "third_party/webrtc/rtc_base/time_utils.h"
 #include "url/gurl.h"
 
@@ -472,11 +471,6 @@
     CHECK_EQ(writer.remaining(), 0u);
   }
 
-  base::span<uint8_t> send_buffer_without_header =
-      send_buffer.buffer->span().subspan(kPacketHeaderSize);
-  webrtc::ApplyPacketOptions(send_buffer_without_header,
-                             options.packet_time_params, webrtc::TimeMicros());
-
   return WriteOrQueue(send_buffer);
 }
 
@@ -547,9 +541,6 @@
           base::MakeRefCounted<net::VectorIOBuffer>(std::move(buffer)),
           buffer_size));
 
-  webrtc::ApplyPacketOptions(send_buffer.buffer->first(data.size()),
-                             options.packet_time_params, webrtc::TimeMicros());
-
   // WriteOrQueue may free the memory, so dump it first.
   delegate_->DumpPacket(send_buffer.buffer->span(), false);
 
diff --git a/services/network/p2p/socket_udp.cc b/services/network/p2p/socket_udp.cc
index cf470a0..790ef41a 100644
--- a/services/network/p2p/socket_udp.cc
+++ b/services/network/p2p/socket_udp.cc
@@ -26,7 +26,6 @@
 #include "services/network/throttling/throttling_network_interceptor.h"
 #include "services/network/throttling/throttling_p2p_network_interceptor.h"
 #include "third_party/perfetto/include/perfetto/tracing/track.h"
-#include "third_party/webrtc/media/base/rtp_utils.h"
 #include "third_party/webrtc/rtc_base/time_utils.h"
 
 namespace {
@@ -457,9 +456,6 @@
       static_cast<net::DiffServCodePoint>(packet.packet_options.dscp),
       packet.packet_options.ect_1 ? net::ECN_ECT1 : net::ECN_NOT_ECT);
 
-  webrtc::ApplyPacketOptions(packet.data->first(packet.size),
-                             packet.packet_options.packet_time_params,
-                             send_time_us);
   bool success = DoSendToSocket(packet, send_time_ms);
   if (success) {
     delegate_->DumpPacket(packet.data->first(packet.size), /*incoming=*/false);
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 5d9f13c..29d7118 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -23481,6 +23481,27 @@
             ]
         }
     ],
+    "V8CompactionOnRegularGCs": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "chromeos",
+                "fuchsia",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Disabled",
+                    "disable_features": [
+                        "V8Flag_compaction_on_regular_gcs"
+                    ]
+                }
+            ]
+        }
+    ],
     "V8DisableEagerAllocationFailures": [
         {
             "platforms": [
diff --git a/third_party/angle b/third_party/angle
index 942c07d..0b3709b9 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 942c07d0c8a6ab15368faa6c972385536189244c
+Subproject commit 0b3709b9cf4a6b8c75576b0b8c77bcda80fe38f0
diff --git a/third_party/blink/public/devtools_protocol/domains/Audits.pdl b/third_party/blink/public/devtools_protocol/domains/Audits.pdl
index 536399a..b8a2bec 100644
--- a/third_party/blink/public/devtools_protocol/domains/Audits.pdl
+++ b/third_party/blink/public/devtools_protocol/domains/Audits.pdl
@@ -429,6 +429,8 @@
       AutofillPolicyControlledFeatureInfo
       ManualTextPolicyControlledFeatureInfo
       FormModelContextParameterMissingTitleAndDescription
+      FormModelContextMissingToolName
+      FormModelContextMissingToolDescription
 
   # Depending on the concrete errorType, different properties are set.
   type GenericIssueDetails extends object
diff --git a/third_party/blink/public/mojom/devtools/inspector_issue.mojom b/third_party/blink/public/mojom/devtools/inspector_issue.mojom
index 15cb2a8..c810462 100644
--- a/third_party/blink/public/mojom/devtools/inspector_issue.mojom
+++ b/third_party/blink/public/mojom/devtools/inspector_issue.mojom
@@ -324,6 +324,8 @@
   kAutofillPolicyControlledFeatureInfo,
   kManualTextPolicyControlledFeatureInfo,
   kFormModelContextParameterMissingTitleAndDescription,
+  kFormModelContextMissingToolName,
+  kFormModelContextMissingToolDescription,
 };
 
 struct GenericIssueDetails {
diff --git a/third_party/blink/renderer/core/css/style_rule.cc b/third_party/blink/renderer/core/css/style_rule.cc
index cec08b7..aa1e8fc3 100644
--- a/third_party/blink/renderer/core/css/style_rule.cc
+++ b/third_party/blink/renderer/core/css/style_rule.cc
@@ -605,6 +605,19 @@
                               mixin_parameter_bindings));
 }
 
+HeapVector<CSSSelector> CloneSelectorListWithDummyFallback(
+    StyleRule* new_parent) {
+  if (new_parent) {
+    return CSSSelectorList::Copy(new_parent->FirstSelector());
+  }
+  // A StyleRule cannot have an empty selector; create a dummy.
+  HeapVector<CSSSelector> selectors;
+  selectors.emplace_back(/*parent_rule=*/nullptr, /*is_implicit=*/true);
+  selectors.back().SetLastInSelectorList(true);
+  selectors.back().SetLastInComplexSelector(true);
+  return selectors;
+}
+
 // Make sure that the FakeParentRuleForDeclarations, if any,
 // gets our parent as parent. In particular, we'd like any
 // StyleRuleNestedDeclarations in there to get our selector
@@ -615,14 +628,19 @@
     StyleRule* old_inner_rule,
     StyleRule* new_parent,
     const MixinParameterBindings* mixin_parameter_bindings) {
+  if (!old_inner_rule) {
+    return nullptr;
+  }
   HeapVector<CSSSelector> selectors =
-      CSSSelectorList::Copy(new_parent->FirstSelector());
+      CloneSelectorListWithDummyFallback(new_parent);
   auto* new_rule = StyleRule::Create(
       selectors, old_inner_rule->Properties().ImmutableCopyIfNeeded(),
       mixin_parameter_bindings);
-  for (StyleRuleBase* child_rule : *old_inner_rule->ChildRules()) {
-    new_rule->AddChildRule(
-        child_rule->Clone(new_rule, mixin_parameter_bindings));
+  if (old_inner_rule->ChildRules()) {
+    for (StyleRuleBase* child_rule : *old_inner_rule->ChildRules()) {
+      new_rule->AddChildRule(
+          child_rule->Clone(new_rule, mixin_parameter_bindings));
+    }
   }
   return new_rule;
 }
@@ -701,9 +719,6 @@
     case kApplyMixin: {
       auto* apply_rule = To<StyleRuleApplyMixin>(this);
       StyleRule* old_inner_rule = apply_rule->FakeParentRuleForDeclarations();
-      if (!old_inner_rule || !old_inner_rule->ChildRules()) {
-        return this;
-      }
       return MakeGarbageCollected<StyleRuleApplyMixin>(
           apply_rule->GetName(), apply_rule->GetArguments(),
           CloneFakeParentRule(old_inner_rule, new_parent,
@@ -712,15 +727,14 @@
     case kContents: {
       auto* contents_rule = To<StyleRuleContentsStatement>(this);
       StyleRule* old_inner_rule = contents_rule->FakeParentRuleForFallback();
-      if (!old_inner_rule || !old_inner_rule->ChildRules()) {
-        return this;
-      }
       return MakeGarbageCollected<StyleRuleContentsStatement>(
           CloneFakeParentRule(old_inner_rule, new_parent,
                               mixin_parameter_bindings));
     }
     case kNestedDeclarations: {
       auto* nested_declarations_rule = To<StyleRuleNestedDeclarations>(this);
+      HeapVector<CSSSelector> selectors;
+      StyleRule* old_inner_rule = nested_declarations_rule->InnerStyleRule();
       // Nested declaration rules are different from regular nested style rules,
       // since they don't refer to their parent rule with any '&' selector.
       // Instead the outer selector list is *copied* parse-time. Now that we're
@@ -731,11 +745,10 @@
       // by @scope rules, however, since they always just behave like
       // :where(:scope).
       if (nested_declarations_rule->NestingType() == CSSNestingType::kScope) {
-        return this;
+        selectors = CSSSelectorList::Copy(old_inner_rule->FirstSelector());
+      } else {
+        selectors = CloneSelectorListWithDummyFallback(new_parent);
       }
-      StyleRule* old_inner_rule = nested_declarations_rule->InnerStyleRule();
-      HeapVector<CSSSelector> selectors =
-          CSSSelectorList::Copy(new_parent->FirstSelector());
       auto* new_inner_rule = StyleRule::Create(
           selectors, old_inner_rule->Properties().ImmutableCopyIfNeeded(),
           mixin_parameter_bindings);
diff --git a/third_party/blink/renderer/core/css/style_rule.h b/third_party/blink/renderer/core/css/style_rule.h
index 4beff03..5b3fe99 100644
--- a/third_party/blink/renderer/core/css/style_rule.h
+++ b/third_party/blink/renderer/core/css/style_rule.h
@@ -864,7 +864,7 @@
            rule.IsContainerRule() || rule.IsLayerBlockRule() ||
            rule.IsScopeRule() || rule.IsStartingStyleRule() ||
            rule.IsFunctionRule() || rule.IsPageRule() ||
-           rule.IsNavigationRule();
+           rule.IsNavigationRule() || rule.IsMixinRule() || rule.IsResultRule();
   }
 };
 
diff --git a/third_party/blink/renderer/core/css/style_rule_test.cc b/third_party/blink/renderer/core/css/style_rule_test.cc
index bdbd934..88f1cc0 100644
--- a/third_party/blink/renderer/core/css/style_rule_test.cc
+++ b/third_party/blink/renderer/core/css/style_rule_test.cc
@@ -5,14 +5,18 @@
 #include "third_party/blink/renderer/core/css/style_rule.h"
 
 #include "base/functional/function_ref.h"
+#include "third_party/blink/renderer/core/css/css_property_value_set.h"
 #include "third_party/blink/renderer/core/css/css_rule_list.h"
 #include "third_party/blink/renderer/core/css/css_scope_rule.h"
 #include "third_party/blink/renderer/core/css/css_style_rule.h"
 #include "third_party/blink/renderer/core/css/css_style_sheet.h"
 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
 #include "third_party/blink/renderer/core/css/navigation_query.h"
+#include "third_party/blink/renderer/core/css/style_rule_nested_declarations.h"
+#include "third_party/blink/renderer/core/css/style_sheet_contents.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
@@ -581,4 +585,104 @@
   EXPECT_FALSE(navigation_test);
 }
 
+struct CloneTestParam {
+  const char* name;
+  const char* css;
+};
+
+class StyleRuleCloneTest : public StyleRuleTest,
+                           public testing::WithParamInterface<CloneTestParam> {
+ protected:
+  void VerifyDifferent(const StyleRuleBase* rule1, const StyleRuleBase* rule2) {
+    ASSERT_TRUE(rule1);
+    ASSERT_TRUE(rule2);
+    EXPECT_NE(rule1, rule2);
+    ASSERT_EQ(rule1->GetType(), rule2->GetType());
+
+    if (auto* group1 = DynamicTo<StyleRuleGroup>(rule1)) {
+      auto* group2 = To<StyleRuleGroup>(rule2);
+      const HeapVector<Member<StyleRuleBase>>& c1 = group1->ChildRules();
+      const HeapVector<Member<StyleRuleBase>>& c2 = group2->ChildRules();
+      ASSERT_EQ(c1.size(), c2.size());
+      for (wtf_size_t i = 0; i < c1.size(); ++i) {
+        VerifyDifferent(c1[i].Get(), c2[i].Get());
+      }
+    } else if (auto* style1 = DynamicTo<StyleRule>(rule1)) {
+      auto* style2 = To<StyleRule>(rule2);
+      const GCedHeapVector<Member<StyleRuleBase>>* c1 = style1->ChildRules();
+      const GCedHeapVector<Member<StyleRuleBase>>* c2 = style2->ChildRules();
+      if (c1 && c2) {
+        ASSERT_EQ(c1->size(), c2->size());
+        for (wtf_size_t i = 0; i < c1->size(); ++i) {
+          VerifyDifferent((*c1)[i].Get(), (*c2)[i].Get());
+        }
+      } else {
+        EXPECT_FALSE(c1);
+        EXPECT_FALSE(c2);
+      }
+    }
+  }
+};
+
+TEST_P(StyleRuleCloneTest, CloneRulesAreDifferent) {
+  auto param = GetParam();
+  CSSStyleSheet* sheet = css_test_helpers::CreateStyleSheet(GetDocument());
+  sheet->SetText(param.css, CSSImportRules::kIgnoreWithWarning);
+  StyleSheetContents* contents1 = sheet->Contents();
+  StyleSheetContents* contents2 = contents1->Copy();
+
+  ASSERT_EQ(contents1->ChildRules().size(), contents2->ChildRules().size());
+  EXPECT_GT(contents1->ChildRules().size(), 0);
+
+  for (wtf_size_t i = 0; i < contents1->ChildRules().size(); ++i) {
+    VerifyDifferent(contents1->ChildRules()[i].Get(),
+                    contents2->ChildRules()[i].Get());
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    StyleRuleTest,
+    StyleRuleCloneTest,
+    testing::Values(
+        CloneTestParam{"NestedScopeDeclarations",
+                       "@scope (div) { color: green; } "},
+        CloneTestParam{"NestedDeclarations",
+                       "div { @media (width > 100px) { color: green; } }"},
+        CloneTestParam{"MixinContentsStatement",
+                       "@mixin --m() { @result { @contents; } }"},
+        CloneTestParam{"MixinContentsEmptyBlock",
+                       "@mixin --m() { @result { @contents {} } }"},
+        CloneTestParam{"MixinContentsNonEmptyBlock",
+                       "@mixin --m() { @result { @contents { div {} } } }"},
+        CloneTestParam{"ApplyStatement", "div { @apply --m(); }"},
+        CloneTestParam{"ApplyEmptyBlock", "div { @apply --m() { } }"},
+        CloneTestParam{"ApplyNonEmptyBlock",
+                       "div { @apply --m() { div {} } }"}),
+    [](const testing::TestParamInfo<StyleRuleCloneTest::ParamType>& info) {
+      return info.param.name;
+    });
+
+TEST_F(StyleRuleTest, CloneNestedDeclarationsNoParent) {
+  HeapVector<CSSSelector> selectors;
+  selectors.emplace_back(/*parent_rule=*/nullptr, /*is_implicit=*/true);
+  selectors.back().SetLastInSelectorList(true);
+  selectors.back().SetLastInComplexSelector(true);
+
+  auto* declarations =
+      MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode);
+
+  auto* inner_rule = StyleRule::Create(selectors, declarations,
+                                       /*mixin_parameter_bindings=*/nullptr);
+
+  StyleRuleBase* nested_declarations1 =
+      MakeGarbageCollected<StyleRuleNestedDeclarations>(
+          CSSNestingType::kNesting, inner_rule);
+
+  StyleRuleBase* nested_declarations2 = nested_declarations1->Clone(
+      /*new_parent=*/nullptr,
+      /*mixin_parameter_bindings=*/nullptr);  // Don't crash.
+
+  EXPECT_NE(nested_declarations1, nested_declarations2);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 9ad7a238..ff203e4 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -259,13 +259,17 @@
   bool transfer_to_gpu_texture_was_invoked_ = false;
 };
 
-// Adapter for wrapping a CanvasResourceReleaseCallback into a
-// viz::ReleaseCallback
-void ReleaseCanvasResource(CanvasResource::ReleaseCallback callback,
-                           scoped_refptr<CanvasResource> canvas_resource,
+// viz::ReleaseCallback for CanvasResource
+void ReleaseCanvasResource(scoped_refptr<CanvasResource> canvas_resource,
                            const gpu::SyncToken& sync_token,
                            bool is_lost) {
-  std::move(callback).Run(std::move(canvas_resource), sync_token, is_lost);
+  CHECK(canvas_resource);
+  canvas_resource->WaitSyncToken(sync_token);
+  if (is_lost) {
+    canvas_resource->NotifyResourceLost();
+  }
+
+  CanvasResource::DropRefOnOwningThread(std::move(canvas_resource));
 }
 
 void UmaHistogramCompressionRatio(
@@ -374,15 +378,13 @@
     return false;
   }
 
-  CanvasResource::ReleaseCallback release_callback;
-  if (!frame->PrepareTransferableResource(out_resource, &release_callback,
+  if (!frame->PrepareTransferableResource(out_resource,
                                           /*needs_verified_synctoken=*/false) ||
       *out_resource == cc_layer_->current_transferable_resource()) {
     // If the resource did not change, the release will be handled correctly
-    // when the callback from the previous frame is dispatched. But run the
-    // |release_callback| to release the ref acquired above.
-    std::move(release_callback)
-        .Run(std::move(frame), gpu::SyncToken(), false /* is_lost */);
+    // when the callback from the previous frame is dispatched. But we need to
+    // drop ref to the current resource.
+    CanvasResource::DropRefOnOwningThread(std::move(frame));
     return false;
   }
   // TODO(https://crbug.com/1475955): HDR metadata should be propagated to
@@ -391,8 +393,8 @@
   // here.
   out_resource->hdr_metadata = hdr_metadata_;
   // Note: frame is kept alive via a reference kept in out_release_callback.
-  *out_release_callback = blink::BindOnce(
-      ReleaseCanvasResource, std::move(release_callback), std::move(frame));
+  *out_release_callback =
+      blink::BindOnce(ReleaseCanvasResource, std::move(frame));
 
   return true;
 }
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.cc b/third_party/blink/renderer/core/html/forms/html_form_element.cc
index 88cd679..43c83bc 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.cc
@@ -31,6 +31,7 @@
 #include "base/auto_reset.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/security_context/insecure_request_policy.h"
+#include "third_party/blink/public/mojom/devtools/inspector_issue.mojom-blink.h"
 #include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink.h"
 #include "third_party/blink/public/web/web_form_related_change_type.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
@@ -40,6 +41,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_union_element_radionodelist.h"
 #include "third_party/blink/renderer/core/dom/attribute.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/dom_node_ids.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/events/scoped_event_queue.h"
@@ -73,6 +75,7 @@
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/input_type_names.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
+#include "third_party/blink/renderer/core/inspector/inspector_audits_issue.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/loader/form_submission.h"
 #include "third_party/blink/renderer/core/loader/mixed_content_checker.h"
@@ -320,6 +323,26 @@
   }
 }
 
+void HTMLFormElement::ReportInvalidMCPFormIssueIfNeeded(
+    const String& name,
+    const String& description) {
+  if (!isConnected()) {
+    return;
+  }
+  if (name.empty()) {
+    AuditsIssue::ReportGenericIssue(
+        GetDocument().GetFrame(),
+        mojom::blink::GenericIssueErrorType::kFormModelContextMissingToolName,
+        DOMNodeIds::IdForNode(this));
+    return;
+  }
+  CHECK(description.empty());
+  AuditsIssue::ReportGenericIssue(GetDocument().GetFrame(),
+                                  mojom::blink::GenericIssueErrorType::
+                                      kFormModelContextMissingToolDescription,
+                                  DOMNodeIds::IdForNode(this));
+}
+
 // This gets called when a <form> is added or removed from the document, or
 // when `toolname` or `tooldescription` attributes are added, removed, or
 // changed.
@@ -333,6 +356,12 @@
   String name = FastGetAttribute(html_names::kToolnameAttr);
   String description = FastGetAttribute(html_names::kTooldescriptionAttr);
   bool is_valid_mcp_form = isConnected() && name && description;
+  // Only report issues if it is not a valid mcp form and
+  // at least one of name or description is present.
+  // If no name or description are present, ignore.
+  if (!is_valid_mcp_form && (name || description)) {
+    ReportInvalidMCPFormIssueIfNeeded(name, description);
+  }
   bool name_or_description_changed =
       is_valid_mcp_form && active_webmcp_tool_ &&
       (active_webmcp_tool_->ToolName() != name ||
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.h b/third_party/blink/renderer/core/html/forms/html_form_element.h
index fb23dd3..f6934aa 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.h
@@ -236,6 +236,8 @@
 
   bool IsValidWebMCPForm() const;
   void UpdateMcpDefinitionsIfNeeded();
+  void ReportInvalidMCPFormIssueIfNeeded(const String& name,
+                                         const String& description);
 
   using PastNamesMap = GCedHeapHashMap<AtomicString, Member<Element>>;
 
diff --git a/third_party/blink/renderer/core/html/forms/html_form_mcp_tool_test.cc b/third_party/blink/renderer/core/html/forms/html_form_mcp_tool_test.cc
index 76069aed..5ca39e2 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_mcp_tool_test.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_mcp_tool_test.cc
@@ -3324,4 +3324,111 @@
   EXPECT_EQ(expected_json->ToJSONString(), actual);
 }
 
+TEST_F(HTMLFormMcpToolTest, GenericIssue_MissingName) {
+  InspectorIssueStorage& storage =
+      GetDocument().GetPage()->GetInspectorIssueStorage();
+  storage.Clear();
+  EXPECT_EQ(0u, storage.size());
+
+  SetBodyInnerHTML(
+      R"HTML(
+    <form id="form" tooldescription="perform task">
+      <input name="text1" type="text">
+    </form>
+  )HTML");
+
+  EXPECT_EQ(1u, storage.size());
+  protocol::Audits::InspectorIssue* issue = storage.at(0);
+  EXPECT_EQ(protocol::Audits::InspectorIssueCodeEnum::GenericIssue,
+            issue->getCode());
+  EXPECT_TRUE(issue->getDetails()->hasGenericIssueDetails());
+  EXPECT_EQ(protocol::Audits::GenericIssueErrorTypeEnum::
+                FormModelContextMissingToolName,
+            issue->getDetails()->getGenericIssueDetails()->getErrorType());
+}
+
+TEST_F(HTMLFormMcpToolTest, GenericIssue_MissingDescription) {
+  InspectorIssueStorage& storage =
+      GetDocument().GetPage()->GetInspectorIssueStorage();
+  storage.Clear();
+  EXPECT_EQ(0u, storage.size());
+
+  SetBodyInnerHTML(
+      R"HTML(
+    <form id="form" toolname="mytool">
+      <input name="text1" type="text">
+    </form>
+  )HTML");
+
+  EXPECT_EQ(1u, storage.size());
+  protocol::Audits::InspectorIssue* issue = storage.at(0);
+  EXPECT_EQ(protocol::Audits::InspectorIssueCodeEnum::GenericIssue,
+            issue->getCode());
+  EXPECT_TRUE(issue->getDetails()->hasGenericIssueDetails());
+  EXPECT_EQ(protocol::Audits::GenericIssueErrorTypeEnum::
+                FormModelContextMissingToolDescription,
+            issue->getDetails()->getGenericIssueDetails()->getErrorType());
+}
+
+TEST_F(HTMLFormMcpToolTest, GenericIssue_MissingDescriptionTwice) {
+  InspectorIssueStorage& storage =
+      GetDocument().GetPage()->GetInspectorIssueStorage();
+  storage.Clear();
+  EXPECT_EQ(0u, storage.size());
+
+  SetBodyInnerHTML(
+      R"HTML(
+    <form id="form" toolname="mytool">
+      <input name="text1" type="text">
+    </form>
+  )HTML");
+
+  EXPECT_EQ(1u, storage.size());
+  SetBodyInnerHTML(
+      R"HTML(
+    <form id="form" toolname="mytool">
+      <input name="text1" type="text">
+    </form>
+  )HTML");
+  EXPECT_EQ(2u, storage.size());
+  EXPECT_EQ(
+      storage.at(0)->getDetails()->getGenericIssueDetails()->getErrorType(),
+      storage.at(1)->getDetails()->getGenericIssueDetails()->getErrorType());
+}
+
+TEST_F(HTMLFormMcpToolTest, GenericIssue_RegularFormNoIssues) {
+  InspectorIssueStorage& storage =
+      GetDocument().GetPage()->GetInspectorIssueStorage();
+  storage.Clear();
+  EXPECT_EQ(0u, storage.size());
+
+  SetBodyInnerHTML(
+      R"HTML(
+    <form id="form">
+      <input name="text1" type="text">
+    </form>
+  )HTML");
+
+  EXPECT_EQ(0u, storage.size());
+}
+
+TEST_F(HTMLFormMcpToolTest, GenericIssue_NotConnectedNoIssues) {
+  UpdateAllLifecyclePhasesForTest();
+
+  InspectorIssueStorage& storage =
+      GetDocument().GetPage()->GetInspectorIssueStorage();
+  storage.Clear();
+  EXPECT_EQ(0u, storage.size());
+
+  HTMLFormElement* form_element =
+      MakeGarbageCollected<HTMLFormElement>(GetDocument());
+  form_element->setAttribute(html_names::kToolnameAttr, AtomicString("mytool"));
+  form_element->setAttribute(html_names::kTooldescriptionAttr,
+                             AtomicString("description"));
+  EXPECT_FALSE(IsValidWebMCPForm(*form_element));  // Not connected.
+
+  // Should not report issues because it's not connected.
+  EXPECT_EQ(0u, storage.size());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc b/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc
index 05a3c40..7977b831 100644
--- a/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_audits_issue.cc
@@ -179,6 +179,13 @@
         kFormModelContextParameterMissingTitleAndDescription:
       return protocol::Audits::GenericIssueErrorTypeEnum::
           FormModelContextParameterMissingTitleAndDescription;
+    case mojom::blink::GenericIssueErrorType::kFormModelContextMissingToolName:
+      return protocol::Audits::GenericIssueErrorTypeEnum::
+          FormModelContextMissingToolName;
+    case mojom::blink::GenericIssueErrorType::
+        kFormModelContextMissingToolDescription:
+      return protocol::Audits::GenericIssueErrorTypeEnum::
+          FormModelContextMissingToolDescription;
   }
 }
 
diff --git a/third_party/blink/renderer/core/loader/frame_load_request.h b/third_party/blink/renderer/core/loader/frame_load_request.h
index 7d636eb..efdb616 100644
--- a/third_party/blink/renderer/core/loader/frame_load_request.h
+++ b/third_party/blink/renderer/core/loader/frame_load_request.h
@@ -246,6 +246,8 @@
   std::optional<base::UnguessableToken> script_tool_invocation_id_;
   mojom::blink::TriggeringEventInfo triggering_event_info_ =
       mojom::blink::TriggeringEventInfo::kNotFromEvent;
+  // The element that triggered the navigation. This may be cross-origin to the
+  // navigation's destination, and should be checked before use.
   Element* source_element_ = nullptr;
   ShouldSendReferrer should_send_referrer_;
   const DOMWrapperWorld* world_ = nullptr;
diff --git a/third_party/blink/renderer/core/navigation_api/navigate_event_dispatch_params.h b/third_party/blink/renderer/core/navigation_api/navigate_event_dispatch_params.h
index b9d22cd..4dd7efe 100644
--- a/third_party/blink/renderer/core/navigation_api/navigate_event_dispatch_params.h
+++ b/third_party/blink/renderer/core/navigation_api/navigate_event_dispatch_params.h
@@ -39,6 +39,8 @@
   const NavigateEventType event_type;
   const WebFrameLoadType frame_load_type;
   UserNavigationInvolvement involvement = UserNavigationInvolvement::kNone;
+  // The element that triggered the navigation. This may be cross-origin to the
+  // navigation's destination, and should be checked before use.
   Member<Element> source_element;
   scoped_refptr<SerializedScriptValue> state_object;
   Member<HistoryItem> destination_item;
diff --git a/third_party/blink/renderer/core/navigation_api/navigation_api.cc b/third_party/blink/renderer/core/navigation_api/navigation_api.cc
index 002912d..f678e024 100644
--- a/third_party/blink/renderer/core/navigation_api/navigation_api.cc
+++ b/third_party/blink/renderer/core/navigation_api/navigation_api.cc
@@ -12,6 +12,7 @@
 #include "base/time/time.h"
 #include "third_party/blink/public/mojom/frame/frame.mojom-blink.h"
 #include "third_party/blink/public/web/web_frame_load_type.h"
+#include "third_party/blink/renderer/bindings/core/v8/binding_security.h"
 #include "third_party/blink/renderer/bindings/core/v8/capture_source_location.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
@@ -755,6 +756,11 @@
     return DispatchResult::kContinue;
   }
 
+  if (params->source_element && !BindingSecurity::ShouldAllowAccessTo(
+                                    window_, params->source_element.Get())) {
+    params->source_element = nullptr;
+  }
+
   LocalFrame* frame = window_->GetFrame();
   auto* script_state = ToScriptStateForMainWorld(frame);
   ScriptState::Scope scope(script_state);
diff --git a/third_party/blink/renderer/core/paint/text_decoration_info.cc b/third_party/blink/renderer/core/paint/text_decoration_info.cc
index 52f3f8c..dbf4042b 100644
--- a/third_party/blink/renderer/core/paint/text_decoration_info.cc
+++ b/third_party/blink/renderer/core/paint/text_decoration_info.cc
@@ -198,8 +198,8 @@
 const ResolvedDecoration TextDecorationInfo::UpdateForDecorationIndex() {
   DCHECK_LT(decoration_index_, AppliedDecorationCount());
   ResolvedDecoration decoration;
-  applied_text_decoration_ = &AppliedDecoration(decoration_index_);
-  decoration.lines = applied_text_decoration_->Lines();
+  decoration.applied_text_decoration = &AppliedDecoration(decoration_index_);
+  decoration.lines = decoration.applied_text_decoration->Lines();
   decoration.has_underline =
       EnumHasFlags(decoration.lines, TextDecorationLine::kUnderline);
   decoration.has_overline =
@@ -325,8 +325,8 @@
         MakeSpellingGrammarWave(decorating_box_style_->EffectiveZoom());
 #endif
   } else {
-    DCHECK(applied_text_decoration_);
-    style = TextDecorationStyleToStrokeStyle(applied_text_decoration_->Style());
+    style = TextDecorationStyleToStrokeStyle(
+        decoration.applied_text_decoration->Style());
   }
 
   const gfx::PointF start_point =
@@ -363,7 +363,7 @@
   if (decoration.is_flipped_underline_and_overline) [[unlikely]] {
     line_offset = Length();
   } else {
-    line_offset = applied_text_decoration_->UnderlineOffset();
+    line_offset = decoration.applied_text_decoration->UnderlineOffset();
   }
   float paint_underline_offset = decoration_offset.ComputeUnderlineOffset(
       decoration.underline_position, decoration.computed_font_size,
@@ -384,7 +384,7 @@
   Length line_offset;
   FontVerticalPositionType position;
   if (decoration.is_flipped_underline_and_overline) [[unlikely]] {
-    line_offset = applied_text_decoration_->UnderlineOffset();
+    line_offset = decoration.applied_text_decoration->UnderlineOffset();
     position = FontVerticalPositionType::TopOfEmHeight;
   } else {
     line_offset = Length();
@@ -417,7 +417,6 @@
   DCHECK(!decoration.HasUnderline());
   DCHECK(!decoration.HasOverline());
   DCHECK(!decoration.HasLineThrough());
-  DCHECK(applied_text_decoration_);
   const int paint_underline_offset = decoration_offset.ComputeUnderlineOffset(
       decoration.underline_position, TargetStyle().ComputedFontSize(),
       decoration.font_data, Length(), decoration.resolved_thickness);
@@ -442,17 +441,15 @@
 
   // Find the matched normal and selection |AppliedTextDecoration|
   // and use the text-decoration-color from selection when it is.
-  DCHECK(applied_text_decoration_);
   if (decoration.lines == selection_decoration_line_) {
     return selection_decoration_color_;
   }
 
-  return applied_text_decoration_->GetColor();
+  return decoration.applied_text_decoration->GetColor();
 }
 
 float TextDecorationInfo::ComputeThickness(
     const ResolvedDecoration& decoration) const {
-  DCHECK(applied_text_decoration_);
   if (decoration.HasSpellingOrGrammarError()) {
     // Spelling and grammar error thickness doesn't depend on the font size.
 #if BUILDFLAG(IS_ANDROID)
@@ -472,8 +469,8 @@
 #endif
   }
   const float thickness = ComputeDecorationThickness(
-      applied_text_decoration_->Thickness(), decoration.computed_font_size,
-      decoration.font_data);
+      decoration.applied_text_decoration->Thickness(),
+      decoration.computed_font_size, decoration.font_data);
   return std::max(is_svg_text_ ? 0.0f : 1.0f, thickness);
 }
 
diff --git a/third_party/blink/renderer/core/paint/text_decoration_info.h b/third_party/blink/renderer/core/paint/text_decoration_info.h
index 6998fb7..b573b47a 100644
--- a/third_party/blink/renderer/core/paint/text_decoration_info.h
+++ b/third_party/blink/renderer/core/paint/text_decoration_info.h
@@ -49,6 +49,9 @@
   STACK_ALLOCATED();
 
  public:
+  // ResolveDecorationAt() must fill `applied_text_decoration`, so it never be
+  // nullptr.
+  const AppliedTextDecoration* applied_text_decoration = nullptr;
   const SimpleFontData* font_data = nullptr;
   TextDecorationLine lines = TextDecorationLine::kNone;
   float ascent = 0.f;
@@ -167,7 +170,7 @@
   // Decorating box properties for the current |decoration_index_|.
   const InlinePaintContext* const inline_context_ = nullptr;
   const DecoratingBox* decorating_box_ = nullptr;
-  const AppliedTextDecoration* applied_text_decoration_ = nullptr;
+
   const TextDecorationLine selection_decoration_line_ =
       TextDecorationLine::kNone;
   const Color selection_decoration_color_;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 0f27717..29405a3d 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -3551,7 +3551,12 @@
 
       // Update (create or remove) validation child of root, if it is needed, so
       // that the tree can be frozen in the correct state.
-      ValidationMessageObjectIfInvalid();
+      if (ValidationMessageObjectIfInvalid()) {
+        // A validation message exists and must be a child of root. Ensure root
+        // rebuilds its children to include it, since Init(Root()) only sets the
+        // has_dirty_descendants_ flag but not children_dirty_.
+        InvalidateChildren(Root());
+      }
 
       // If MarkDocumentDirty() was called, do it now, so that the entire tree
       // is invalidated before updating it.
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_test.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_test.cc
index 2b7981944..82253a4 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_test.cc
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "base/test/metrics/histogram_tester.h"
+#include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
@@ -15,6 +16,7 @@
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/pseudo_element.h"
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
+#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_select_element.h"
 #include "third_party/blink/renderer/core/testing/mock_function_scope.h"
 #include "third_party/blink/renderer/core/view_transition/dom_view_transition.h"
@@ -30,6 +32,10 @@
 #include "ui/accessibility/ax_action_data.h"
 #include "ui/gfx/geometry/rect.h"
 
+#if BUILDFLAG(IS_WIN)
+#include "third_party/blink/public/web/win/web_font_rendering.h"
+#endif
+
 namespace blink {
 
 // TODO(nektar): Break test up into multiple tests.
@@ -581,4 +587,25 @@
   )HTML");
 }
 
+TEST_F(AccessibilityTest, ValidationMessageIncludedInRootChildren) {
+#if BUILDFLAG(IS_WIN)
+  blink::WebFontRendering::SetMenuFontMetrics(
+      blink::WebString::FromAscii("Arial"), 12);
+#endif
+  SetBodyInnerHTML(R"HTML(<input id="input">)HTML");
+
+  AXObject* root = GetAXRootObject();
+  ASSERT_TRUE(root);
+
+  auto* input = To<HTMLInputElement>(GetElementById("input"));
+  ASSERT_TRUE(input);
+  input->setCustomValidity("Error");
+  input->reportValidity();
+  GetDocument().View()->UpdateAllLifecyclePhasesForTest();
+
+  AXObject* message = GetAXObjectCache().ValidationMessageObjectIfInvalid();
+  ASSERT_TRUE(message);
+  EXPECT_TRUE(root->CachedChildrenIncludingIgnored().Contains(message));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/html_user_media_element_media_stream.cc b/third_party/blink/renderer/modules/mediastream/html_user_media_element_media_stream.cc
index dca7b0a9..0d270bc 100644
--- a/third_party/blink/renderer/modules/mediastream/html_user_media_element_media_stream.cc
+++ b/third_party/blink/renderer/modules/mediastream/html_user_media_element_media_stream.cc
@@ -33,9 +33,15 @@
 }
 
 // static
-const V8UnionDOMExceptionOrOverconstrainedError*
-HTMLUserMediaElementMediaStream::error(HTMLUserMediaElement& element) {
-  return From(element).error_.Get();
+ScriptValue HTMLUserMediaElementMediaStream::error(
+    ScriptState* script_state,
+    HTMLUserMediaElement& element) {
+  const auto& error_ref = From(element).error_;
+  if (error_ref.IsEmpty()) {
+    return ScriptValue::CreateNull(script_state->GetIsolate());
+  }
+  return ScriptValue(script_state->GetIsolate(),
+                     error_ref.GetAcrossWorld(script_state));
 }
 
 HTMLUserMediaElementMediaStream::HTMLUserMediaElementMediaStream(
diff --git a/third_party/blink/renderer/modules/mediastream/html_user_media_element_media_stream.h b/third_party/blink/renderer/modules/mediastream/html_user_media_element_media_stream.h
index 5435adc..d38bb905c 100644
--- a/third_party/blink/renderer/modules/mediastream/html_user_media_element_media_stream.h
+++ b/third_party/blink/renderer/modules/mediastream/html_user_media_element_media_stream.h
@@ -8,6 +8,8 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_union_domexception_overconstrainederror.h"
 #include "third_party/blink/renderer/core/html/html_user_media_element.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
+#include "third_party/blink/renderer/bindings/core/v8/world_safe_v8_reference.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
 
 namespace blink {
@@ -22,23 +24,20 @@
 
   static HTMLUserMediaElementMediaStream& From(HTMLUserMediaElement&);
   static MediaStream* stream(HTMLUserMediaElement&);
-  static const V8UnionDOMExceptionOrOverconstrainedError* error(
-      HTMLUserMediaElement& element);
+  static ScriptValue error(ScriptState*, HTMLUserMediaElement& element);
 
   explicit HTMLUserMediaElementMediaStream(HTMLUserMediaElement&);
 
   MediaStream* GetMediaStream() const { return media_stream_.Get(); }
   void SetMediaStream(MediaStream* stream) { media_stream_ = stream; }
 
-  void SetError(const V8UnionDOMExceptionOrOverconstrainedError* error) {
-    error_ = error;
-  }
+  void SetError(WorldSafeV8Reference<v8::Value> error) { error_ = std::move(error); }
 
   void Trace(Visitor*) const override;
 
  private:
   Member<MediaStream> media_stream_;
-  Member<const V8UnionDOMExceptionOrOverconstrainedError> error_;
+  WorldSafeV8Reference<v8::Value> error_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/html_user_media_element_media_stream.idl b/third_party/blink/renderer/modules/mediastream/html_user_media_element_media_stream.idl
index 9ffe7d46..9f4e66b 100644
--- a/third_party/blink/renderer/modules/mediastream/html_user_media_element_media_stream.idl
+++ b/third_party/blink/renderer/modules/mediastream/html_user_media_element_media_stream.idl
@@ -5,5 +5,5 @@
 [RuntimeEnabled=UserMediaElement, ImplementedAs=HTMLUserMediaElementMediaStream]
 partial interface HTMLUserMediaElement {
     readonly attribute MediaStream? stream;
-    readonly attribute (DOMException or OverconstrainedError)? error;
+    [CallWith=ScriptState] readonly attribute any error;
 };
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.cc b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.cc
index da15d83d..881f2cd 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.cc
@@ -717,7 +717,8 @@
     BooleanConstraint voice_isolation_constraint =
         constraint_set.voice_isolation;
 
-    if (voice_isolation_constraint.HasIdeal()) {
+    if (voice_isolation_constraint.HasIdeal() &&
+        allowed_values_.Contains(voice_isolation_constraint.Ideal())) {
       VoiceIsolationType voice_isolation_type_ideal =
           voice_isolation_constraint.Ideal()
               ? VoiceIsolationType::kVoiceIsolationEnabled
@@ -750,22 +751,24 @@
   // APM always supports mono output;
   result.push_back(1);
   const int channels = device_params.channels();
-  if (channels > 1)
+  if (channels > 1) {
     result.push_back(channels);
+  }
   return result;
 }
 
 // This container represents the supported audio settings for a given type of
-// audio source. In practice, there are three types of sources: processed using
-// APM, processed without APM, and unprocessed. Processing using APM has two
-// flavors: one for the systems where audio processing is done in the renderer,
-// another for the systems where audio processing is done in the audio service.
+// audio source. In practice, there are three types of sources: processed
+// using APM, processed without APM, and unprocessed. Processing using APM has
+// two flavors: one for the systems where audio processing is done in the
+// renderer, another for the systems where audio processing is done in the
+// audio service.
 class ProcessingBasedContainer {
  public:
   // Creates an instance of ProcessingBasedContainer for the WebRTC processed
   // source type. The source type allows (a) any type of echo cancellation,
-  // though the system echo cancellation type depends on the availability of the
-  // related |parameters.effects()|, and (b) any combination of processing
+  // though the system echo cancellation type depends on the availability of
+  // the related |parameters.effects()|, and (b) any combination of processing
   // properties settings.
   static ProcessingBasedContainer CreateApmProcessedContainer(
       std::optional<SourceInfo> source_info,
@@ -785,11 +788,15 @@
       }
     }
     echo_cancellation_modes.push_back(EchoCancellationMode::kDisabled);
+    BoolSet voice_isolation_set;
+#if !BUILDFLAG(IS_CHROMEOS)
+    // Voice Isolation is only supported on ChromeOS.
+    voice_isolation_set = BoolSet({false});
+#endif
     return ProcessingBasedContainer(
         ProcessingType::kApmProcessed, std::move(echo_cancellation_modes),
         /*auto_gain_control_set=*/BoolSet(),
-        /*noise_suppression_set=*/BoolSet(),
-        /*voice_isolation_set=*/BoolSet(),
+        /*noise_suppression_set=*/BoolSet(), voice_isolation_set,
         /*sample_size_range=*/IntRangeSet::FromValue(GetSampleSize()),
         /*channels_set=*/GetApmSupportedChannels(device_parameters),
         /*sample_rate_range=*/
@@ -806,11 +813,15 @@
       AudioCaptureApi api,
       const media::AudioParameters& device_parameters,
       bool is_reconfiguration_allowed) {
+    BoolSet voice_isolation_set;
+#if !BUILDFLAG(IS_CHROMEOS)
+    // Voice Isolation is only supported on ChromeOS.
+    voice_isolation_set = BoolSet({false});
+#endif
     return ProcessingBasedContainer(
         ProcessingType::kNoApmProcessed, {EchoCancellationMode::kDisabled},
         /*auto_gain_control_set=*/BoolSet({false}),
-        /*noise_suppression_set=*/BoolSet({false}),
-        /*voice_isolation_set=*/BoolSet(),
+        /*noise_suppression_set=*/BoolSet({false}), voice_isolation_set,
         /*sample_size_range=*/IntRangeSet::FromValue(GetSampleSize()),
         /*channels_set=*/{device_parameters.channels()},
         /*sample_rate_range=*/
@@ -818,10 +829,10 @@
         api, device_parameters, is_reconfiguration_allowed);
   }
 
-  // Creates an instance of ProcessingBasedContainer for the unprocessed source
-  // type. The source type allows (a) either system echo cancellation, if
-  // allowed by the |parameters.effects()|, or none, while (c) all processing
-  // properties settings cannot be enabled.
+  // Creates an instance of ProcessingBasedContainer for the unprocessed
+  // source type. The source type allows (a) either system echo cancellation,
+  // if allowed by the |parameters.effects()|, or none, while (c) all
+  // processing properties settings cannot be enabled.
   static ProcessingBasedContainer CreateUnprocessedContainer(
       std::optional<SourceInfo> source_info,
       AudioCaptureApi api,
@@ -844,13 +855,15 @@
 
     failed_constraint_name =
         echo_cancellation_container_.ApplyConstraintSet(constraint_set);
-    if (failed_constraint_name)
+    if (failed_constraint_name) {
       return failed_constraint_name;
+    }
 
     failed_constraint_name =
         auto_gain_control_container_.ApplyConstraintSet(constraint_set);
-    if (failed_constraint_name)
+    if (failed_constraint_name) {
       return failed_constraint_name;
+    }
 
     failed_constraint_name =
         voice_isolation_container_.ApplyConstraintSet(constraint_set);
@@ -860,23 +873,27 @@
 
     failed_constraint_name =
         sample_size_container_.ApplyConstraintSet(constraint_set.sample_size);
-    if (failed_constraint_name)
+    if (failed_constraint_name) {
       return failed_constraint_name;
+    }
 
     failed_constraint_name =
         channels_container_.ApplyConstraintSet(constraint_set.channel_count);
-    if (failed_constraint_name)
+    if (failed_constraint_name) {
       return failed_constraint_name;
+    }
 
     failed_constraint_name =
         sample_rate_container_.ApplyConstraintSet(constraint_set.sample_rate);
-    if (failed_constraint_name)
+    if (failed_constraint_name) {
       return failed_constraint_name;
+    }
 
     failed_constraint_name =
         latency_container_.ApplyConstraintSet(constraint_set.latency);
-    if (failed_constraint_name)
+    if (failed_constraint_name) {
       return failed_constraint_name;
+    }
 
     failed_constraint_name = noise_suppression_container_.ApplyConstraintSet(
         constraint_set.noise_suppression);
@@ -1036,8 +1053,9 @@
     }
 
     // If the device is already opened, restrict supported values for
-    // non-reconfigurable settings to what is already configured. The rationale
-    // for this is that opening multiple instances of the APM is costly.
+    // non-reconfigurable settings to what is already configured. The
+    // rationale for this is that opening multiple instances of the APM is
+    // costly.
     // TODO(crbug.com/1147928): Consider removing this restriction.
     auto_gain_control_container_ = AutoGainControlContainer(
         BoolSet({source_info->properties().auto_gain_control}));
@@ -1126,14 +1144,14 @@
 
     // If the device is in use, a source will be provided and all containers
     // must be initialized such that their only supported values correspond to
-    // the source settings. Otherwise, the containers are initialized to contain
-    // all possible values.
+    // the source settings. Otherwise, the containers are initialized to
+    // contain all possible values.
     std::optional<SourceInfo> source_info =
         SourceInfo::FromSource(capability.source());
 
     // Three variations of the processing-based container. Each variant is
-    // associated to a different type of audio processing configuration, namely
-    // unprocessed, processed by WebRTC, or processed by other means.
+    // associated to a different type of audio processing configuration,
+    // namely unprocessed, processed by WebRTC, or processed by other means.
     processing_based_containers_.push_back(
         ProcessingBasedContainer::CreateUnprocessedContainer(
             source_info, api, device_parameters_, is_reconfiguration_allowed));
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio_test.cc
index 449af0e7..d85d0b34 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio_test.cc
@@ -1433,6 +1433,8 @@
 #endif
 }
 
+#if BUILDFLAG(IS_CHROMEOS)
+// Voice Isolation is only supported on ChromeOS.
 TEST_P(MediaStreamConstraintsUtilAudioTest, VoiceIsolationControl) {
   if (!IsDeviceCapture()) {
     return;
@@ -1459,6 +1461,44 @@
       settings.audio_processing_properties().voice_isolation,
       AudioProcessingProperties::VoiceIsolationType::kVoiceIsolationDefault);
 }
+#else
+TEST_P(MediaStreamConstraintsUtilAudioTest, VoiceIsolationControl) {
+  if (!IsDeviceCapture()) {
+    return;
+  }
+  constraint_factory_.Reset();
+  constraint_factory_.basic().voice_isolation.SetExact(true);
+  AudioCaptureSettings settings = SelectSettings(true, capabilities_);
+  EXPECT_FALSE(settings.HasValue());
+  constraint_factory_.Reset();
+  constraint_factory_.basic().voice_isolation.SetExact(false);
+  settings = SelectSettings(true, capabilities_);
+  EXPECT_TRUE(settings.HasValue());
+  EXPECT_EQ(
+      settings.audio_processing_properties().voice_isolation,
+      AudioProcessingProperties::VoiceIsolationType::kVoiceIsolationDisabled);
+  constraint_factory_.Reset();
+  constraint_factory_.basic().voice_isolation.SetIdeal(true);
+  settings = SelectSettings(true, capabilities_);
+  EXPECT_TRUE(settings.HasValue());
+  EXPECT_EQ(
+      settings.audio_processing_properties().voice_isolation,
+      AudioProcessingProperties::VoiceIsolationType::kVoiceIsolationDisabled);
+  constraint_factory_.Reset();
+  constraint_factory_.basic().voice_isolation.SetIdeal(false);
+  settings = SelectSettings(true, capabilities_);
+  EXPECT_TRUE(settings.HasValue());
+  EXPECT_EQ(
+      settings.audio_processing_properties().voice_isolation,
+      AudioProcessingProperties::VoiceIsolationType::kVoiceIsolationDisabled);
+  constraint_factory_.Reset();
+  settings = SelectSettings(true, capabilities_);
+  EXPECT_TRUE(settings.HasValue());
+  EXPECT_EQ(
+      settings.audio_processing_properties().voice_isolation,
+      AudioProcessingProperties::VoiceIsolationType::kVoiceIsolationDisabled);
+}
+#endif
 
 // Test advanced constraints sets that can be satisfied.
 TEST_P(MediaStreamConstraintsUtilAudioTest, AdvancedCompatibleConstraints) {
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_request_provider_impl.cc b/third_party/blink/renderer/modules/mediastream/user_media_request_provider_impl.cc
index a0cf959..a0421d44 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_request_provider_impl.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_request_provider_impl.cc
@@ -4,7 +4,9 @@
 
 #include "third_party/blink/renderer/modules/mediastream/user_media_request_provider_impl.h"
 
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_boolean_mediatrackconstraints.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_union_domexception_overconstrainederror.h"
+#include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/space_split_string.h"
 #include "third_party/blink/renderer/core/event_type_names.h"
@@ -13,11 +15,27 @@
 #include "third_party/blink/renderer/modules/mediastream/html_user_media_element_media_stream.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream.h"
 #include "third_party/blink/renderer/modules/mediastream/user_media_client.h"
+#include "third_party/blink/renderer/modules/mediastream/user_media_element_constraints.h"
 #include "third_party/blink/renderer/modules/mediastream/user_media_request.h"
+#include "third_party/blink/renderer/bindings/core/v8/world_safe_v8_reference.h"
+#include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
 #include "third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h"
 
 namespace blink {
 
+namespace {
+bool IsConstraintEnabled(
+    const V8UnionBooleanOrMediaTrackConstraints* constraint) {
+  if (!constraint) {
+    return false;
+  }
+  if (constraint->IsBoolean()) {
+    return constraint->GetAsBoolean();
+  }
+  return constraint->IsMediaTrackConstraints();
+}
+}  // namespace
+
 UserMediaRequestProviderCallbacks::UserMediaRequestProviderCallbacks(
     HTMLUserMediaElement* element)
     : element_(element) {}
@@ -40,8 +58,15 @@
     const V8MediaStreamError* error,
     CaptureController* capture_controller,
     UserMediaRequestResult result) {
-  if (element_) {
-    HTMLUserMediaElementMediaStream::From(*element_).SetError(error);
+  if (element_ && element_->GetExecutionContext()) {
+    ScriptState* script_state =
+        ToScriptStateForMainWorld(element_->GetDocument().GetFrame());
+    if (script_state) {
+      ScriptState::Scope scope(script_state);
+      HTMLUserMediaElementMediaStream::From(*element_).SetError(
+          WorldSafeV8Reference<v8::Value>(script_state->GetIsolate(),
+                                          error->ToV8(script_state)));
+    }
     element_->DispatchEvent(*Event::Create(event_type_names::kStream));
   }
 }
@@ -67,11 +92,21 @@
     HTMLUserMediaElement* element,
     const Vector<mojom::blink::PermissionDescriptorPtr>&
         permission_descriptors) {
+  if (permission_descriptors.empty()) {
+    return;
+  }
+
   LocalDOMWindow* window = element->GetDocument().domWindow();
   if (!window) {
     return;
   }
 
+  ScriptState* script_state = ToScriptStateForMainWorld(window->GetFrame());
+  if (!script_state) {
+    return;
+  }
+  ScriptState::Scope scope(script_state);
+
   UserMediaClient* client = UserMediaClient::From(window);
   if (!client) {
     return;
@@ -83,26 +118,78 @@
     return;
   }
 
-  // TODO: b/494481412: Parse constraints from HTMLUserMediaElement
-  MediaConstraints audio_constraints;
-  MediaConstraints video_constraints;
+  // Constraints that are set on the HTMLUserMediaElement.
+  const MediaStreamConstraints* constraints =
+      UserMediaElementConstraints::From(*element).Constraints();
 
-  for (const auto& descriptor : permission_descriptors) {
-    if (descriptor->name == mojom::blink::PermissionName::AUDIO_CAPTURE) {
-      audio_constraints.Initialize();
-    } else if (descriptor->name ==
-               mojom::blink::PermissionName::VIDEO_CAPTURE) {
-      video_constraints.Initialize();
+  // Constraints that will be used for the UserMediaRequest.
+  MediaStreamConstraints* request_constraints = nullptr;
+
+  if (permission_descriptors.size() == 2) {
+    // Camera and Microphone element.
+    if (!IsConstraintEnabled(constraints->audio()) &&
+        !IsConstraintEnabled(constraints->video())) {
+      HTMLUserMediaElementMediaStream::From(*element).SetError(
+          WorldSafeV8Reference<v8::Value>(
+              window->GetIsolate(),
+              V8ThrowException::CreateTypeError(window->GetIsolate(),
+                                                "No constraints set")));
+      element->DispatchEvent(*Event::Create(event_type_names::kStream));
+      return;
     }
+    request_constraints = MediaStreamConstraints::Create();
+    request_constraints->setAudio(constraints->audio());
+    request_constraints->setVideo(constraints->video());
+  } else if (permission_descriptors[0]->name ==
+             mojom::blink::PermissionName::AUDIO_CAPTURE) {
+    // Audio only element.
+    if (!IsConstraintEnabled(constraints->audio())) {
+      HTMLUserMediaElementMediaStream::From(*element).SetError(
+          WorldSafeV8Reference<v8::Value>(
+              window->GetIsolate(),
+              V8ThrowException::CreateTypeError(window->GetIsolate(),
+                                                "No audio constraints set")));
+      element->DispatchEvent(*Event::Create(event_type_names::kStream));
+      return;
+    }
+    request_constraints = MediaStreamConstraints::Create();
+    request_constraints->setAudio(constraints->audio());
+  } else {
+    // Video only element.
+    CHECK_EQ(permission_descriptors[0]->name,
+             mojom::blink::PermissionName::VIDEO_CAPTURE);
+    if (!IsConstraintEnabled(constraints->video())) {
+      HTMLUserMediaElementMediaStream::From(*element).SetError(
+          WorldSafeV8Reference<v8::Value>(
+              window->GetIsolate(),
+              V8ThrowException::CreateTypeError(window->GetIsolate(),
+                                                "No video constraints set")));
+      element->DispatchEvent(*Event::Create(event_type_names::kStream));
+      return;
+    }
+    request_constraints = MediaStreamConstraints::Create();
+    request_constraints->setVideo(constraints->video());
   }
 
-  UserMediaRequest* request = MakeGarbageCollected<UserMediaRequest>(
-      window, client, UserMediaRequestType::kUserMedia, audio_constraints,
-      video_constraints,
-      /*should_prefer_current_tab=*/false,
-      /*capture_controller=*/nullptr,
-      MakeGarbageCollected<UserMediaRequestProviderCallbacks>(element));
-  request->Start();
+  ExceptionState exception_state(window->GetIsolate());
+  UserMediaRequest* request = UserMediaRequest::Create(
+      window, client, UserMediaRequestType::kUserMedia, request_constraints,
+      MakeGarbageCollected<UserMediaRequestProviderCallbacks>(element),
+      exception_state);
+
+  if (exception_state.HadException()) {
+    HTMLUserMediaElementMediaStream::From(*element).SetError(
+        WorldSafeV8Reference<v8::Value>(
+            window->GetIsolate(),
+            V8ThrowException::CreateTypeError(
+                window->GetIsolate(), "Stream creation failed")));
+    element->DispatchEvent(*Event::Create(event_type_names::kStream));
+    return;
+  }
+
+  if (request) {
+    request->Start();
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_request_provider_impl_test.cc b/third_party/blink/renderer/modules/mediastream/user_media_request_provider_impl_test.cc
index 3bf1b0a..d2da8b8 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_request_provider_impl_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_request_provider_impl_test.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/mediastream/user_media_request_provider_impl.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_typedefs.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_union_domexception_overconstrainederror.h"
 #include "third_party/blink/renderer/core/dom/document.h"
@@ -18,6 +19,8 @@
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/modules/mediastream/html_user_media_element_media_stream.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream.h"
+#include "third_party/blink/renderer/modules/mediastream/user_media_element_constraints.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_union_boolean_mediatrackconstraints.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
 
@@ -34,10 +37,17 @@
 // Verifies that the provider doesn't crash when attempting to process a request
 // without a valid UserMediaClient.
 TEST_F(UserMediaRequestProviderImplTest, StartRequestEarlyExitNoClient) {
+  V8TestingScope scope;
   auto* provider = UserMediaRequestProvider::From(*GetDocument().domWindow());
 
   auto* element = MakeGarbageCollected<HTMLUserMediaElement>(GetDocument());
   element->setAttribute(html_names::kTypeAttr, AtomicString("camera"));
+
+  MediaStreamConstraints* constraints = MediaStreamConstraints::Create();
+  constraints->setVideo(
+      MakeGarbageCollected<V8UnionBooleanOrMediaTrackConstraints>(true));
+  UserMediaElementConstraints::setConstraints(*element, constraints);
+
   provider->StartRequest(element, element->GetPermissionDescriptors());
   // Test passes if it doesn't crash.
 }
@@ -45,11 +55,17 @@
 // Verifies that StartRequest gracefully exits and makes no changes when an
 // active stream is already present.
 TEST_F(UserMediaRequestProviderImplTest, StartRequestActiveStreamExists) {
+  V8TestingScope scope;
   auto* provider = UserMediaRequestProvider::From(*GetDocument().domWindow());
 
   auto* element = MakeGarbageCollected<HTMLUserMediaElement>(GetDocument());
   element->setAttribute(html_names::kTypeAttr, AtomicString("camera"));
 
+  MediaStreamConstraints* constraints = MediaStreamConstraints::Create();
+  constraints->setVideo(
+      MakeGarbageCollected<V8UnionBooleanOrMediaTrackConstraints>(true));
+  UserMediaElementConstraints::setConstraints(*element, constraints);
+
   auto* stream = MediaStream::Create(GetDocument().GetExecutionContext());
   HTMLUserMediaElementMediaStream::From(*element).SetMediaStream(stream);
 
@@ -86,6 +102,7 @@
 };
 
 TEST_F(UserMediaRequestProviderImplTest, CallbacksOnError) {
+  V8TestingScope scope;
   auto* element = MakeGarbageCollected<HTMLUserMediaElement>(GetDocument());
   auto* callbacks =
       MakeGarbageCollected<UserMediaRequestProviderCallbacks>(element);
@@ -94,7 +111,7 @@
   auto* listener = MakeGarbageCollected<TestEventListener>();
   element->addEventListener(event_type_names::kStream, listener);
 
-  EXPECT_EQ(HTMLUserMediaElementMediaStream::error(*element), nullptr);
+  EXPECT_TRUE(HTMLUserMediaElementMediaStream::error(scope.GetScriptState(), *element).IsNull());
 
   DOMException* dom_exception =
       DOMException::Create("Some error message", "NotFoundError");
@@ -105,13 +122,53 @@
 
   // Check that the event was fired and the error was set
   EXPECT_TRUE(listener->fired());
-  EXPECT_NE(HTMLUserMediaElementMediaStream::error(*element), nullptr);
-  EXPECT_TRUE(
-      HTMLUserMediaElementMediaStream::error(*element)->IsDOMException());
-  EXPECT_EQ(HTMLUserMediaElementMediaStream::error(*element)
-                ->GetAsDOMException()
-                ->name(),
+  ScriptValue stored_error = HTMLUserMediaElementMediaStream::error(scope.GetScriptState(), *element);
+  EXPECT_FALSE(stored_error.IsEmpty());
+  EXPECT_TRUE(stored_error.V8Value()->IsObject());
+  EXPECT_EQ(ToCoreString(scope.GetIsolate(), stored_error.V8Value()
+                                                 .As<v8::Object>()
+                                                 ->Get(scope.GetContext(),
+                                                       V8String(scope.GetIsolate(),
+                                                                "name"))
+                                                 .ToLocalChecked()
+                                                 .As<v8::String>()),
             "NotFoundError");
 }
 
+TEST_F(UserMediaRequestProviderImplTest, StartRequestNoConstraintsError) {
+  V8TestingScope scope;
+  auto* provider = UserMediaRequestProvider::From(*GetDocument().domWindow());
+
+  auto* element = MakeGarbageCollected<HTMLUserMediaElement>(GetDocument());
+  element->setAttribute(html_names::kTypeAttr, AtomicString("camera microphone"));
+
+  MediaStreamConstraints* constraints = MediaStreamConstraints::Create();
+  UserMediaElementConstraints::setConstraints(*element, constraints);
+
+  // Set up event listener
+  auto* listener = MakeGarbageCollected<TestEventListener>();
+  element->addEventListener(event_type_names::kStream, listener);
+
+  provider->StartRequest(element, element->GetPermissionDescriptors());
+
+  EXPECT_TRUE(listener->fired());
+  ScriptValue stored_error = HTMLUserMediaElementMediaStream::error(scope.GetScriptState(), *element);
+  EXPECT_FALSE(stored_error.IsEmpty());
+  EXPECT_TRUE(stored_error.V8Value()->IsObject());
+
+  v8::Local<v8::Object> error_obj = stored_error.V8Value().As<v8::Object>();
+  EXPECT_EQ(ToCoreString(scope.GetIsolate(), error_obj->Get(scope.GetContext(),
+                                                       V8String(scope.GetIsolate(),
+                                                                "name"))
+                                                 .ToLocalChecked()
+                                                 .As<v8::String>()),
+            "TypeError");
+  EXPECT_EQ(ToCoreString(scope.GetIsolate(), error_obj->Get(scope.GetContext(),
+                                                       V8String(scope.GetIsolate(),
+                                                                "message"))
+                                                 .ToLocalChecked()
+                                                 .As<v8::String>()),
+            "No constraints set");
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/smart_card/OWNERS b/third_party/blink/renderer/modules/smart_card/OWNERS
index 1f114915..5c88425 100644
--- a/third_party/blink/renderer/modules/smart_card/OWNERS
+++ b/third_party/blink/renderer/modules/smart_card/OWNERS
@@ -3,6 +3,7 @@
 # For backup
 greengrape@google.com
 simonha@google.com
+paulinagacek@google.com
 
 # Changes to Mojo interfaces require a security review to avoid
 # introducing new sandbox escapes.
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
index 84780f7..32f25557 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -98,25 +98,19 @@
   return ContextProviderWrapper()->ContextProvider().WebGPUInterface();
 }
 
-static void ReleaseFrameResources(
+// static
+void CanvasResource::ReleaseFrameResources(
     scoped_refptr<CanvasResource>&& resource,
     const gpu::SyncToken& sync_token,
     bool lost_resource) {
   CHECK(resource);
 
   resource->WaitSyncToken(sync_token);
-
-  // TODO(khushalsagar): If multiple readers had access to this resource, losing
-  // it once should make sure subsequent releases don't try to recycle this
-  // resource.
   if (lost_resource) {
     resource->NotifyResourceLost();
-  } else {
-    // Allow the resource to determine whether it wants to preserve itself for
-    // reuse.
-    auto* raw_resource = resource.get();
-    raw_resource->OnRefReturned(std::move(resource));
   }
+
+  CanvasResource::DropRefOnOwningThread(std::move(resource));
 }
 
 // static
@@ -126,30 +120,32 @@
     return;
   }
 
-  auto& owning_thread_task_runner = resource->owning_thread_task_runner_;
-  owning_thread_task_runner->PostTask(
-      FROM_HERE, base::BindOnce(&OnPlaceholderReleasedResourceOnOwningThread,
-                                std::move(resource)));
+  if (resource->is_cross_thread()) {
+    auto& owning_thread_task_runner = resource->owning_thread_task_runner_;
+    owning_thread_task_runner->PostTask(
+        FROM_HERE, base::BindOnce(&DropRefOnOwningThread, std::move(resource)));
+  } else {
+    DropRefOnOwningThread(std::move(resource));
+  }
 }
 
 // static
-void CanvasResource::OnPlaceholderReleasedResourceOnOwningThread(
+void CanvasResource::DropRefOnOwningThread(
     scoped_refptr<CanvasResource> resource) {
+  CHECK(resource);
   DCHECK(!resource->is_cross_thread());
 
-  ReleaseFrameResources(std::move(resource), gpu::SyncToken(),
-                        /*is_lost=*/false);
+  // Allow the resource to determine whether it wants to preserve itself for
+  // reuse.
+  auto* raw_resource = resource.get();
+  raw_resource->OnRefReturned(std::move(resource));
 }
 
 bool CanvasResource::PrepareTransferableResource(
     viz::TransferableResource* out_resource,
-    CanvasResource::ReleaseCallback* out_callback,
     bool needs_verified_synctoken) {
   DCHECK(IsValid());
 
-  DCHECK(out_callback);
-  *out_callback = blink::BindOnce(&ReleaseFrameResources);
-
   if (!out_resource)
     return true;
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.h b/third_party/blink/renderer/platform/graphics/canvas_resource.h
index 0bac4fe2..32006efc 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.h
@@ -70,6 +70,8 @@
 
   virtual void OnRefReturned(scoped_refptr<CanvasResource>&& resource) {}
 
+  static void DropRefOnOwningThread(scoped_refptr<CanvasResource> resource);
+
   // Returns true if the resource is still usable. It maybe not be valid in the
   // case of a context loss or if we fail to initialize the memory backing for
   // the resource.
@@ -91,7 +93,6 @@
   // Provides a TransferableResource representation of this resource to share it
   // with the compositor.
   bool PrepareTransferableResource(viz::TransferableResource*,
-                                   ReleaseCallback*,
                                    bool needs_verified_synctoken);
 
   // Issues a wait for this sync token on the context used by this resource for
@@ -123,6 +124,10 @@
  protected:
   explicit CanvasResource(scoped_refptr<gpu::ClientSharedImage> shared_image);
 
+  static void ReleaseFrameResources(scoped_refptr<CanvasResource>&& resource,
+                                    const gpu::SyncToken& sync_token,
+                                    bool lost_resource);
+
   virtual gfx::HDRMetadata GetHDRMetadata() const { return gfx::HDRMetadata(); }
   virtual viz::TransferableResource::ResourceSource
   GetTransferableResourceSource() const {
@@ -143,9 +148,6 @@
   friend class CanvasResourceProviderTest;
   friend class WebGPUMailboxTexture;
 
-  static void OnPlaceholderReleasedResourceOnOwningThread(
-      scoped_refptr<CanvasResource> resource);
-
   // Returns true if the resource is rastered via the GPU.
   virtual bool UsesAcceleratedRaster() const = 0;
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
index 076d4bf3..343b7d0 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
@@ -63,7 +63,7 @@
  private:
   void ReleaseResource(const gpu::SyncToken& sync_token, bool is_lost) {
     auto resource = std::move(resource_);
-    if (release_callback_) {
+    if (resource) {
       std::move(release_callback_)
           .Run(std::move(resource), sync_token, is_lost);
     }
@@ -275,9 +275,19 @@
   // value will have no effect.
   const bool nearest_neighbor = false;
 
-  CanvasResource::ReleaseCallback release_callback;
+  CanvasResource::ReleaseCallback release_callback =
+      base::BindOnce([](scoped_refptr<CanvasResource>&& resource,
+                        const gpu::SyncToken& sync_token, bool lost_resource) {
+        CHECK(resource);
+        resource->WaitSyncToken(sync_token);
+        if (lost_resource) {
+          resource->NotifyResourceLost();
+        }
+
+        CanvasResource::DropRefOnOwningThread(std::move(resource));
+      });
   canvas_resource->PrepareTransferableResource(
-      &resource, &release_callback,
+      &resource,
       /*needs_verified_synctoken=*/true);
 
   const viz::ResourceId resource_id = next_resource_id;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
index 537a8de..e2b374b 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
@@ -128,12 +128,11 @@
   void EnsureResourceRecycled(CanvasResourceProvider* provider,
                               scoped_refptr<CanvasResource>&& resource) {
     viz::TransferableResource transferable_resource;
-    CanvasResource::ReleaseCallback release_callback;
     CHECK(resource->PrepareTransferableResource(
-        &transferable_resource, &release_callback,
+        &transferable_resource,
         /*needs_verified_synctoken=*/false));
-    std::move(release_callback)
-        .Run(std::move(resource), resource->sync_token(), false);
+
+    CanvasResource::DropRefOnOwningThread(std::move(resource));
   }
 
   test::TaskEnvironment task_environment_{
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc
index 30d9165..7fcb625 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_test.cc
@@ -49,15 +49,13 @@
       /*context_provider_wrapper=*/nullptr, shared_image_interface_provider);
   EXPECT_TRUE(!!canvas_resource);
   viz::TransferableResource resource;
-  CanvasResource::ReleaseCallback release_callback;
   bool success = canvas_resource->PrepareTransferableResource(
-      &resource, &release_callback, /*needs_verified_synctoken=*/false);
+      &resource, /*needs_verified_synctoken=*/false);
 
   EXPECT_TRUE(success);
   EXPECT_TRUE(resource.GetIsSoftware());
 
-  std::move(release_callback)
-      .Run(std::move(canvas_resource), gpu::SyncToken(), false);
+  CanvasResource::DropRefOnOwningThread(std::move(canvas_resource));
 }
 
 TEST(CanvasResourceTest, PrepareTransferableResource_PreservesAlphaType) {
@@ -68,7 +66,6 @@
   InitializeSharedGpuContext(test_context_provider.get());
 
   viz::TransferableResource resource;
-  CanvasResource::ReleaseCallback release_callback;
 
   gpu::ImageInfo image_info(
       gfx::Size(10, 10), viz::SinglePlaneFormat::kRGBA_8888,
@@ -86,7 +83,7 @@
       /*is_accelerated=*/false);
 
   ASSERT_TRUE(premul_canvas_resource->PrepareTransferableResource(
-      &resource, &release_callback, /*needs_verified_synctoken=*/false));
+      &resource, /*needs_verified_synctoken=*/false));
   EXPECT_EQ(resource.GetAlphaType(), kPremul_SkAlphaType);
 
   image_info.alpha_type = kUnpremul_SkAlphaType;
@@ -100,7 +97,7 @@
       /*is_accelerated=*/false);
 
   ASSERT_TRUE(unpremul_canvas_resource->PrepareTransferableResource(
-      &resource, &release_callback, /*needs_verified_synctoken=*/false));
+      &resource, /*needs_verified_synctoken=*/false));
   EXPECT_EQ(resource.GetAlphaType(), kUnpremul_SkAlphaType);
 
   // InitializeSharedGpuContext() requires SharedGpuContext::Reset()
diff --git a/third_party/blink/renderer/platform/text/character_property_data.h b/third_party/blink/renderer/platform/text/character_property_data.h
index 1b71afac..ffbd96f 100644
--- a/third_party/blink/renderer/platform/text/character_property_data.h
+++ b/third_party/blink/renderer/platform/text/character_property_data.h
@@ -12,6 +12,8 @@
 namespace blink {
 
 // clang-format off
+// Do not add entries to this array that are already covered by
+// the logic in character_property_data_generator.cc for emoji sequences.
 static constexpr auto kIsCJKIdeographOrSymbolArray = std::to_array<UChar32>({
     // 0x2C7 Caron, Mandarin Chinese 3rd Tone
     0x2C7,
@@ -23,23 +25,19 @@
     0x2D9, 0x2020, 0x2021, 0x2030, 0x203B, 0x203C, 0x2042, 0x2047, 0x2048,
     0x2049, 0x2051, 0x20DD, 0x20DE, 0x2100, 0x2103, 0x2105, 0x2109, 0x210A,
     0x2113, 0x2116, 0x2121, 0x212B, 0x213B, 0x2150, 0x2151, 0x2152, 0x217F,
-    0x2189, 0x2307, 0x23F0, 0x23F3, 0x2312, 0x23CE, 0x2423, 0x25A0, 0x25A1,
-    0x25A2, 0x25AA, 0x25AB, 0x25B1, 0x25B2, 0x25B3, 0x25B6, 0x25B7, 0x25BC,
-    0x25BD, 0x25C0, 0x25C1, 0x25C6, 0x25C7, 0x25C9, 0x25CB, 0x25CC, 0x25EF,
-    0x2605, 0x2606, 0x260E, 0x2616, 0x2617, 0x261D, 0x2640, 0x2642, 0x267F,
-    0x2693, 0x26A0, 0x26A1, 0x26BD, 0x26BE, 0x26CE, 0x26D4,
-    // AIRPLANE added for PILOT emoji sequences.
-    0x26EA, 0x26F5, 0x26F9, 0x26FA, 0x26FD, 0x2705, 0x2708, 0x2713, 0x271A,
-    0x2728, 0x273F, 0x2740, 0x274C, 0x274E, 0x27B0, 0x27BF, 0x2B1A, 0x2B1B,
-    0x2B1C, 0x2B50, 0x2B55, 0xFE10, 0xFE11, 0xFE12, 0xFE19, 0xFF1D,
+    0x2189, 0x2307, 0x2312, 0x23CE, 0x2423, 0x25A0, 0x25A1, 0x25A2, 0x25AA,
+    0x25AB, 0x25B1, 0x25B2, 0x25B3, 0x25B6, 0x25B7, 0x25BC, 0x25BD, 0x25C0,
+    0x25C1, 0x25C6, 0x25C7, 0x25C9, 0x25CB, 0x25CC, 0x25EF, 0x2605, 0x2606,
+    0x260E, 0x2616, 0x2617, 0x26A0, 0x2713, 0x271A, 0x273F, 0x2740, 0x2756,
+    0x2763, 0x2B1A, 0xFE10, 0xFE11, 0xFE12, 0xFE19, 0xFF1D,
     // Emoji.
-    0x1F100, 0x1F004, 0x1F0CF, 0x1F18E
+    0x1F100, 0x1F200, 0x1F237, 0x1F32C, 0x1F336, 0x1F37D, 0x1F43F, 0x1F54F,
+    0x1F93B, 0x1F946
 });
 
+// Do not add entries to this array that are already covered by
+// the logic in character_property_data_generator.cc for emoji sequences.
 static constexpr auto kIsCJKIdeographOrSymbolRanges = std::to_array<UChar32>({
-    // STAFF OF AESCULAPIUS..SCALES for emoji sequences for doctor and judge
-    // professions.
-    0x2695, 0x2696,
     // cjkIdeographRanges
     // CJK Radicals Supplement and Kangxi Radicals.
     0x2E80, 0x2FDF,
@@ -57,19 +55,14 @@
     0x20000, 0x2FFFF,
 
     // cjkSymbolRanges
-    0x2156, 0x215A, 0x2160, 0x216B, 0x2170, 0x217B, 0x231A, 0x231B, 0x23E9,
-    0x23EC, 0x23BE, 0x23CC, 0x2460, 0x2492, 0x249C, 0x24FF, 0x25CE, 0x25D3,
-    0x25E2, 0x25E6, 0x25FD, 0x25FE, 0x2600, 0x2603, 0x2660, 0x266F,
+    0x2156, 0x215A, 0x2160, 0x216B, 0x2170, 0x217B, 0x23BE, 0x23CC,
+    0x2460, 0x2492, 0x249C, 0x24FF, 0x25CE, 0x25D3, 0x25E2, 0x25E6,
+    0x2600, 0x2603, 0x2660, 0x266F,
     // Emoji HEAVY HEART EXCLAMATION MARK ORNAMENT..HEAVY BLACK HEART
     // Needed in order not to break Emoji heart-kiss sequences in
     // CachingWordShapeIterator.
     // cmp. http://www.unicode.org/emoji/charts/emoji-zwj-sequences.html
-    0x2614, 0x2615, 0x2648, 0x2653, 0x26AA, 0x26AB, 0x26C4, 0x26C5, 0x26F2,
-    0x26F3, 0x2753, 0x2757, 0x2763, 0x2764, 0x2672, 0x267D, 0x2776, 0x277F,
-    0x2795, 0x2797,
-    // Hand signs needed in order
-    // not to break Emoji modifier sequences.
-    0x270A, 0x270D,
+    0x2672, 0x267D, 0x2776, 0x277F,
     // Ideographic Description Characters, with CJK Symbols and Punctuation,
     // excluding 0x3030.
     // Exclude Hangul Tone Marks (0x302E .. 0x302F) because Hangul is not Han
@@ -106,45 +99,14 @@
     0x1B170, 0x1B2FF,
     // Emoji.
     0x1F110, 0x1F129, 0x1F130, 0x1F149, 0x1F150, 0x1F169, 0x1F170, 0x1F189,
-    0x1F191, 0x1F19A, 0x1F1E6, 0x1F1FF, 0x1F200, 0x1F6FF,
-    // Modifiers
-    0x1F3FB, 0x1F3FF,
-
-    // Transport
-    0x1F6DC, 0x1F6DF,
-
-    // Colored circles and squares for use with emoji.
-    0x1F7E0, 0x1F7EB,
-
-    // Math
-    0x1F7F0, 0x1F7F0,
-
-    0x1F900, 0x1F90F,
-    // ZIPPER-MOUTH FACE...SIGN OF THE HORNS
-    0x1F910, 0x1F918, 0x1F919, 0x1F97F, 0x1F980, 0x1F9BF, 0x1F9C0, 0x1F9FF,
-    // Clothing
-    // Colored heart symbols
-    // Medical symbols
-    0x1FA70, 0x1FA7C,
-    // Toys and sport symbols
-    // Musical instruments
-    0x1FA80, 0x1FA8A,
-    // Miscellaneous objects
-    // Religious symbol (KHANDA)
-    // Animals and nature
-    // Body parts
-    // People
-    // Miscellaneous (FINGERPRINT)
-    0x1FA8E, 0x1FAC6,
-    // Animals and nature (including reserved)
-    // Food and drink
-    0x1FAC8, 0x1FADC,
-    // Miscellaneous (SPLATTER)
-    // Faces
-    0x1FADF, 0x1FAEA,
-    // Emotion (FIGHT CLOUD)
-    // Hand symbols
-    0x1FAEF, 0x1FAF8,
+    0x1F202, 0x1F219, 0x1F21B, 0x1F22E, 0x1F230, 0x1F231, 0x1F23B, 0x1F24F,
+    0x1F252, 0x1F2FF, 0x1F321, 0x1F32A, 0x1F394, 0x1F39F, 0x1F3CD, 0x1F3CE,
+    0x1F3D4, 0x1F3DF, 0x1F3F1, 0x1F3F2, 0x1F3F5, 0x1F3F7, 0x1F4FD, 0x1F4FE,
+    0x1F53E, 0x1F54A, 0x1F568, 0x1F573, 0x1F576, 0x1F579, 0x1F57B, 0x1F58F,
+    0x1F591, 0x1F594, 0x1F597, 0x1F5A3, 0x1F5A5, 0x1F5E7, 0x1F5E9, 0x1F5FA,
+    0x1F650, 0x1F67F, 0x1F6C6, 0x1F6CB, 0x1F6CD, 0x1F6CF, 0x1F6D3, 0x1F6D4,
+    0x1F6D9, 0x1F6DB, 0x1F6E0, 0x1F6EA, 0x1F6ED, 0x1F6F3, 0x1F6FD, 0x1F6FF,
+    0x1F900, 0x1F90B, 0x1FAC9, 0x1FACC,
 });
 
 // https://html.spec.whatwg.org/C/#prod-potentialcustomelementname
diff --git a/third_party/blink/renderer/platform/text/character_test.cc b/third_party/blink/renderer/platform/text/character_test.cc
index 29b1518b..b08cda5 100644
--- a/third_party/blink/renderer/platform/text/character_test.cc
+++ b/third_party/blink/renderer/platform/text/character_test.cc
@@ -14,6 +14,7 @@
 #include <algorithm>
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/text/character_property_data.h"
 #include "third_party/blink/renderer/platform/text/emoji_segmentation_category.h"
 #include "third_party/blink/renderer/platform/text/emoji_segmentation_category_inline_header.h"
 #include "third_party/blink/renderer/platform/text/justification_opportunity.h"
@@ -96,6 +97,64 @@
   }
 }
 
+TEST(CharacterTest, CJKIdeographOrSymbolCollisions) {
+  icu::UnicodeSet emoji_set;
+  UErrorCode error = U_ZERO_ERROR;
+  emoji_set.addAll(
+      icu::UnicodeSet(icu::UnicodeString("[:Emoji_Presentation:]"), error));
+  ASSERT_EQ(error, U_ZERO_ERROR);
+
+  const char* const kRgiEmojiSequences =
+      "[[:RGI_Emoji_ZWJ_Sequence:][:RGI_Emoji_Modifier_Sequence:]]";
+  icu::UnicodeSet set(icu::UnicodeString(kRgiEmojiSequences), error);
+  ASSERT_EQ(error, U_ZERO_ERROR);
+  for (auto s : set.strings()) {
+    icu::UnicodeString us(s.data(), static_cast<int32_t>(s.length()));
+    for (auto cp : icu::header::unsafeUTFStringCodePoints<UChar32>(us)) {
+      if (Character::IsExtendedPictographic(cp.codePoint())) {
+        emoji_set.add(cp.codePoint());
+      }
+    }
+  }
+
+  for (UChar32 cp : kIsCJKIdeographOrSymbolArray) {
+    if (emoji_set.contains(cp)) {
+      ADD_FAILURE() << "Codepoint 0x" << std::hex << cp
+                    << " in kIsCJKIdeographOrSymbolArray is already covered by "
+                       "SetIsCJKIdeographOrSymbolForEmoji.";
+    }
+  }
+
+  for (size_t i = 0; i < kIsCJKIdeographOrSymbolRanges.size(); i += 2) {
+    UChar32 start = kIsCJKIdeographOrSymbolRanges[i];
+    UChar32 end = kIsCJKIdeographOrSymbolRanges[i + 1];
+    for (UChar32 cp = start; cp <= end; ++cp) {
+      if (emoji_set.contains(cp)) {
+        ADD_FAILURE()
+            << "Codepoint 0x" << std::hex << cp
+            << " in kIsCJKIdeographOrSymbolRanges (range 0x" << start << " - 0x"
+            << end
+            << ") is already covered by SetIsCJKIdeographOrSymbolForEmoji.";
+      }
+    }
+  }
+}
+
+TEST(CharacterTest, CJKIdeographOrSymbolArrayOrRangeCollisions) {
+  for (UChar32 cp : kIsCJKIdeographOrSymbolArray) {
+    for (size_t i = 0; i < kIsCJKIdeographOrSymbolRanges.size(); i += 2) {
+      UChar32 start = kIsCJKIdeographOrSymbolRanges[i];
+      UChar32 end = kIsCJKIdeographOrSymbolRanges[i + 1];
+      if (cp >= start && cp <= end) {
+        ADD_FAILURE() << "Codepoint 0x" << std::hex << cp
+                      << " in kIsCJKIdeographOrSymbolArray is already covered "
+                         "by kIsCJKIdeographOrSymbolRanges (range 0x"
+                      << start << " - 0x" << end << ").";
+      }
+    }
+  }
+}
+
 static void TestSpecificUChar32RangeIdeograph(UChar32 range_start,
                                               UChar32 range_end,
                                               bool before = true,
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/usermedia/set-constraints-combinations.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/usermedia/set-constraints-combinations.tentative.html
new file mode 100644
index 0000000..f71e62f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/permission-element/usermedia/set-constraints-combinations.tentative.html
@@ -0,0 +1,112 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="timeout" content="long">
+<title>HTMLUserMediaElement setConstraints() behavior combinations</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<body>
+  <script>
+    const testCases = [
+      // type="camera"
+      { type: 'camera', video: undefined, audio: undefined, expVideo: 0, expAudio: 0 },
+      { type: 'camera', video: true, audio: undefined, expVideo: 1, expAudio: 0 },
+      { type: 'camera', video: true, audio: false, expVideo: 1, expAudio: 0 },
+      { type: 'camera', video: true, audio: true, expVideo: 1, expAudio: 0 },
+      { type: 'camera', video: false, audio: undefined, expVideo: 0, expAudio: 0 },
+      { type: 'camera', video: false, audio: false, expVideo: 0, expAudio: 0 },
+      { type: 'camera', video: false, audio: true, expVideo: 0, expAudio: 0 },
+      { type: 'camera', video: undefined, audio: false, expVideo: 0, expAudio: 0 },
+      { type: 'camera', video: undefined, audio: true, expVideo: 0, expAudio: 0 },
+
+      // type="microphone"
+      { type: 'microphone', video: undefined, audio: undefined, expVideo: 0, expAudio: 0 },
+      { type: 'microphone', video: undefined, audio: true, expVideo: 0, expAudio: 1 },
+      { type: 'microphone', video: false, audio: true, expVideo: 0, expAudio: 1 },
+      { type: 'microphone', video: true, audio: true, expVideo: 0, expAudio: 1 },
+      { type: 'microphone', video: undefined, audio: false, expVideo: 0, expAudio: 0 },
+      { type: 'microphone', video: false, audio: false, expVideo: 0, expAudio: 0 },
+      { type: 'microphone', video: true, audio: false, expVideo: 0, expAudio: 0 },
+      { type: 'microphone', video: false, audio: undefined, expVideo: 0, expAudio: 0 },
+      { type: 'microphone', video: true, audio: undefined, expVideo: 0, expAudio: 0 },
+
+      // type="camera microphone"
+      { type: "camera microphone", video: undefined, audio: undefined, expVideo: 0, expAudio: 0 },
+      { type: "camera microphone", video: true, audio: undefined, expVideo: 1, expAudio: 0 },
+      { type: "camera microphone", video: true, audio: false, expVideo: 1, expAudio: 0 },
+      { type: "camera microphone", video: true, audio: true, expVideo: 1, expAudio: 1 },
+      { type: "camera microphone", video: false, audio: true, expVideo: 0, expAudio: 1 },
+      { type: "camera microphone", video: false, audio: undefined, expVideo: 0, expAudio: 0 },
+      { type: "camera microphone", video: false, audio: false, expVideo: 0, expAudio: 0 },
+      { type: "camera microphone", video: undefined, audio: true, expVideo: 0, expAudio: 1 },
+      { type: "camera microphone", video: undefined, audio: false, expVideo: 0, expAudio: 0 },
+
+      // No type attribute (null)
+      { type: null, video: undefined, audio: undefined, expVideo: 0, expAudio: 0 },
+      { type: null, video: true, audio: undefined, expVideo: 1, expAudio: 0 },
+      { type: null, video: true, audio: false, expVideo: 1, expAudio: 0 },
+      { type: null, video: true, audio: true, expVideo: 1, expAudio: 1 },
+      { type: null, video: false, audio: true, expVideo: 0, expAudio: 1 },
+      { type: null, video: false, audio: undefined, expVideo: 0, expAudio: 0 },
+      { type: null, video: false, audio: false, expVideo: 0, expAudio: 0 },
+      { type: null, video: undefined, audio: true, expVideo: 0, expAudio: 1 },
+      { type: null, video: undefined, audio: false, expVideo: 0, expAudio: 0 },
+    ];
+
+    promise_test(async (t) => {
+      await test_driver.set_permission({ name: "camera" }, "granted");
+      await test_driver.set_permission({ name: "microphone" }, "granted");
+
+      for (let i = 0; i < testCases.length; i++) {
+        const testCase = testCases[i];
+        const { type, video, audio, expVideo, expAudio } = testCase;
+        const testName = `Case ${i}: type='${type}', video=${video}, audio=${audio}`;
+
+        const usermedia = document.createElement("usermedia");
+        if (type !== null) {
+          usermedia.setAttribute("type", type);
+        }
+        document.body.appendChild(usermedia);
+
+        const stream_promise = new Promise((resolve) => {
+          usermedia.onstream = resolve;
+        });
+
+        usermedia.setConstraints({ video: video, audio: audio });
+
+        // should_trigger is true if the element has at least one permission descriptor.
+        const should_trigger = (type !== null) || (video === true) || (audio === true);
+
+        // Wait for PEPC registration
+        await new Promise(r => t.step_timeout(r, 100));
+
+        if (should_trigger) {
+          await test_driver.bless("click", () => usermedia.click());
+          await stream_promise;
+        } else {
+          await test_driver.bless("click", () => usermedia.click());
+          // Wait a bit to ensure no stream event is fired
+          await new Promise(r => t.step_timeout(r, 200));
+        }
+
+        const stream = usermedia.stream;
+        if (expVideo || expAudio) {
+          assert_true(stream instanceof MediaStream, `${testName}: stream type check`);
+          assert_equals(stream.getVideoTracks().length, expVideo, `${testName}: video tracks mismatch`);
+          assert_equals(stream.getAudioTracks().length, expAudio, `${testName}: audio tracks mismatch`);
+        } else {
+          assert_equals(stream, null, `${testName}: stream should be null`);
+          if (should_trigger) {
+            assert_true(usermedia.error !== null && usermedia.error !== undefined, `${testName}: error attribute should be set`);
+          }
+        }
+
+        if (stream) {
+          stream.getTracks().forEach(track => track.stop());
+        }
+        document.body.removeChild(usermedia);
+      }
+    }, "HTMLUserMediaElement setConstraints() combinations consolidated");
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/resources/echo-sourceElement.html b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/resources/echo-sourceElement.html
new file mode 100644
index 0000000..121827ba
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/resources/echo-sourceElement.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<script>
+  navigation.onnavigate = e => {
+    if (e.destination.url.includes("#test")) {
+      window.parent.postMessage({
+        type: "result",
+        sourceElementIsNull: e.sourceElement === null,
+        sourceElementName: e.sourceElement ? e.sourceElement.tagName : "null"
+      }, "*");
+    }
+  };
+  window.parent.postMessage({ type: "ready" }, "*");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/sourceElement-cross-origin.sub.html b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/sourceElement-cross-origin.sub.html
new file mode 100644
index 0000000..688b76a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/navigation-api/navigate-event/sourceElement-cross-origin.sub.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="theFrame" name="theFrame" src="http://{{domains[www1]}}:{{ports[http][0]}}/navigation-api/navigate-event/resources/echo-sourceElement.html"></iframe>
+<script>
+async_test(t => {
+  window.addEventListener("message", t.step_func(e => {
+    if (e.data.type === "ready") {
+      const a = document.createElement("a");
+      a.href = document.getElementById("theFrame").src + "#test";
+      a.target = "theFrame";
+      document.body.appendChild(a);
+      a.click();
+    } else if (e.data.type === "result") {
+      assert_true(e.data.sourceElementIsNull, "sourceElement must be null for cross-origin initiators (it was " + e.data.sourceElementName + ")");
+      t.done();
+    }
+  }));
+}, "NavigateEvent.sourceElement should be null for cross-origin same-document initiators");
+</script>
diff --git a/third_party/blink/web_tests/fast/mediastream/MediaStreamTrack-getConstraints.html b/third_party/blink/web_tests/fast/mediastream/MediaStreamTrack-getConstraints.html
index e203fac..0923e2d 100644
--- a/third_party/blink/web_tests/fast/mediastream/MediaStreamTrack-getConstraints.html
+++ b/third_party/blink/web_tests/fast/mediastream/MediaStreamTrack-getConstraints.html
@@ -9,7 +9,7 @@
 
 // If a constraint is specified, it should come back in getConstraints().
 promise_test(function() {
-  return navigator.mediaDevices.getUserMedia({audio: { echoCancellation: { exact: true}, autoGainControl: { exact: true }, noiseSuppression: { exact: true }, voiceIsolation: { exact: true }}})
+  return navigator.mediaDevices.getUserMedia({audio: { echoCancellation: { exact: true}, autoGainControl: { exact: true }, noiseSuppression: { exact: true }, voiceIsolation: { exact: false }}})
       .then(function(s) {
     constraints = s.getAudioTracks()[0].getConstraints();
     assert_equals(Object.keys(constraints).length, 4);
@@ -20,7 +20,7 @@
     assert_true(constraints.hasOwnProperty('noiseSuppression'));
     assert_true(constraints.noiseSuppression.exact);
     assert_true(constraints.hasOwnProperty('voiceIsolation'));
-    assert_true(constraints.voiceIsolation.exact);
+    assert_false(constraints.voiceIsolation.exact);
   });
 }, 'A set constraint is returned on getConstraints');
 
@@ -165,7 +165,7 @@
 constraintSyntaxTest('voiceIsolation with simple boolean value', { voiceIsolation: true });
 constraintSyntaxTest('voiceIsolation with ideal boolean value', { voiceIsolation: { ideal: true }});
 constraintSyntaxTestWithChange('voiceIsolation with exact unwrapped boolean value',
-    { voiceIsolation: { exact: true } }, { voiceIsolation: true });
+    { voiceIsolation: { exact: false } }, { voiceIsolation: false });
 
 </script>
 </body>
diff --git a/third_party/crabbyavif/README.chromium b/third_party/crabbyavif/README.chromium
index 1464506..2d3e41a8 100644
--- a/third_party/crabbyavif/README.chromium
+++ b/third_party/crabbyavif/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crabbyavif
 URL: https://github.com/webmproject/CrabbyAvif
 Version: N/A
-Revision: 593c3744025312e2859d2e93016dcdbd8033cb81
+Revision: 3bd20ee70c9a3505eb6846bef81c2876ada113fa
 Update Mechanism: Autoroll
 License: Apache-2.0
 License File: LICENSE
diff --git a/third_party/crabbyavif/src b/third_party/crabbyavif/src
index 593c374..3bd20ee7 160000
--- a/third_party/crabbyavif/src
+++ b/third_party/crabbyavif/src
@@ -1 +1 @@
-Subproject commit 593c3744025312e2859d2e93016dcdbd8033cb81
+Subproject commit 3bd20ee70c9a3505eb6846bef81c2876ada113fa
diff --git a/third_party/dawn b/third_party/dawn
index 7b04a3a..936cc16 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit 7b04a3a55a4f689fb66b8cc758e440d60b976155
+Subproject commit 936cc1681ce39fd2d758fe3d74f7878cbb5573ea
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index e510e65..dfec07e 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit e510e6588351303b677e5ac144db4755fef96f1c
+Subproject commit dfec07e090d057f295fe61e870922e4e6edcfc59
diff --git a/third_party/google-truth/BUILD.gn b/third_party/google-truth/BUILD.gn
index 49269b2de..8750628 100644
--- a/third_party/google-truth/BUILD.gn
+++ b/third_party/google-truth/BUILD.gn
@@ -13,8 +13,6 @@
   requires_android = true
 
   sources = [
-    "src/core/src/main/java/com/google/common/truth/AbstractArraySubject.java",
-
     # ActualValueInference is intentionally omitted because its dependencies are
     # not compatible with Android. Truth supports being built without this
     # class; see the implementation of
@@ -26,7 +24,6 @@
     "src/core/src/main/java/com/google/common/truth/BooleanSubject.java",
     "src/core/src/main/java/com/google/common/truth/ClassSubject.java",
     "src/core/src/main/java/com/google/common/truth/ComparableSubject.java",
-    "src/core/src/main/java/com/google/common/truth/ComparisonFailureWithFacts.java",
     "src/core/src/main/java/com/google/common/truth/ComparisonFailures.java",
     "src/core/src/main/java/com/google/common/truth/Correspondence.java",
     "src/core/src/main/java/com/google/common/truth/CustomSubjectBuilder.java",
@@ -41,17 +38,26 @@
     "src/core/src/main/java/com/google/common/truth/FloatSubject.java",
     "src/core/src/main/java/com/google/common/truth/GraphMatching.java",
     "src/core/src/main/java/com/google/common/truth/GuavaOptionalSubject.java",
+    "src/core/src/main/java/com/google/common/truth/IgnoreJRERequirement.java",
+    "src/core/src/main/java/com/google/common/truth/IntStreamSubject.java",
     "src/core/src/main/java/com/google/common/truth/IntegerSubject.java",
     "src/core/src/main/java/com/google/common/truth/IterableSubject.java",
     "src/core/src/main/java/com/google/common/truth/J2ktIncompatible.java",
     "src/core/src/main/java/com/google/common/truth/LazyMessage.java",
+    "src/core/src/main/java/com/google/common/truth/LongStreamSubject.java",
     "src/core/src/main/java/com/google/common/truth/LongSubject.java",
     "src/core/src/main/java/com/google/common/truth/MapSubject.java",
     "src/core/src/main/java/com/google/common/truth/MathUtil.java",
     "src/core/src/main/java/com/google/common/truth/MultimapSubject.java",
     "src/core/src/main/java/com/google/common/truth/MultisetSubject.java",
+    "src/core/src/main/java/com/google/common/truth/NullnessCasts.java",
     "src/core/src/main/java/com/google/common/truth/ObjectArraySubject.java",
+    "src/core/src/main/java/com/google/common/truth/OptionalDoubleSubject.java",
+    "src/core/src/main/java/com/google/common/truth/OptionalIntSubject.java",
+    "src/core/src/main/java/com/google/common/truth/OptionalLongSubject.java",
+    "src/core/src/main/java/com/google/common/truth/OptionalSubject.java",
     "src/core/src/main/java/com/google/common/truth/Ordered.java",
+    "src/core/src/main/java/com/google/common/truth/PathSubject.java",
     "src/core/src/main/java/com/google/common/truth/Platform.java",
     "src/core/src/main/java/com/google/common/truth/PrimitiveBooleanArraySubject.java",
     "src/core/src/main/java/com/google/common/truth/PrimitiveByteArraySubject.java",
@@ -62,14 +68,17 @@
     "src/core/src/main/java/com/google/common/truth/PrimitiveLongArraySubject.java",
     "src/core/src/main/java/com/google/common/truth/PrimitiveShortArraySubject.java",
     "src/core/src/main/java/com/google/common/truth/SimpleSubjectBuilder.java",
+    "src/core/src/main/java/com/google/common/truth/SneakyThrows.java",
     "src/core/src/main/java/com/google/common/truth/StackTraceCleaner.java",
     "src/core/src/main/java/com/google/common/truth/StandardSubjectBuilder.java",
+    "src/core/src/main/java/com/google/common/truth/StreamSubject.java",
     "src/core/src/main/java/com/google/common/truth/StringSubject.java",
     "src/core/src/main/java/com/google/common/truth/Subject.java",
     "src/core/src/main/java/com/google/common/truth/SubjectUtils.java",
     "src/core/src/main/java/com/google/common/truth/TableSubject.java",
     "src/core/src/main/java/com/google/common/truth/ThrowableSubject.java",
     "src/core/src/main/java/com/google/common/truth/Truth.java",
+    "src/core/src/main/java/com/google/common/truth/Truth8.java",
     "src/core/src/main/java/com/google/common/truth/TruthFailureSubject.java",
     "src/core/src/main/java/com/google/common/truth/TruthJUnit.java",
     "src/core/src/main/java/com/google/common/truth/UsedByReflection.java",
@@ -78,8 +87,9 @@
 
   deps = [
     "//third_party/android_deps:com_google_errorprone_error_prone_annotations_java",
+    "//third_party/android_deps:com_google_j2objc_j2objc_annotations_java",
     "//third_party/android_deps:guava_android_java",
-    "//third_party/android_deps:org_checkerframework_checker_qual_java",
+    "//third_party/android_deps:org_jspecify_jspecify_java",
     "//third_party/junit",
   ]
 }
diff --git a/third_party/google-truth/README.chromium b/third_party/google-truth/README.chromium
index 6e3893b4..e252dd0 100644
--- a/third_party/google-truth/README.chromium
+++ b/third_party/google-truth/README.chromium
@@ -1,7 +1,7 @@
 Name: Fluent assertions for Java
 Short Name: google-truth
 URL: https://github.com/google/truth
-Version: 33387149b465f82712a817e6744847fe136949b3
+Version: fc54b9e4436ed8ca8bcae7dd71e07a6857e13836
 Update Mechanism: Manual
 License: Apache-2.0
 License File: LICENSE
diff --git a/third_party/google-truth/src b/third_party/google-truth/src
index 3338714..fc54b9e 160000
--- a/third_party/google-truth/src
+++ b/third_party/google-truth/src
@@ -1 +1 @@
-Subproject commit 33387149b465f82712a817e6744847fe136949b3
+Subproject commit fc54b9e4436ed8ca8bcae7dd71e07a6857e13836
diff --git a/third_party/skia b/third_party/skia
index 2c49b3f..d8415c5 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 2c49b3f9c3c2dececf1b41a9334312a85ef8aa9f
+Subproject commit d8415c5d7b91dbfae97a4786a35e892935b07ede
diff --git a/third_party/webrtc b/third_party/webrtc
index 07a13d3..9dde36eb 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 07a13d347ff2cd44325259be766567c7f9e87360
+Subproject commit 9dde36ebf937da0ab92837932b253cc3d42c8dc2
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 5e2594b..87a80b6 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -11572,6 +11572,7 @@
   <int value="-832970990" label="mantis-feature-key:disabled"/>
   <int value="-832806243" label="QuickActionSearchWidgetAndroid:disabled"/>
   <int value="-832780530" label="SecurePaymentConfirmationDebug:enabled"/>
+  <int value="-832695519" label="AutofillAiNoFillingIconsExperiment:disabled"/>
   <int value="-832561975" label="enable-picture-in-picture"/>
   <int value="-832154680"
       label="AutofillEnforceDelaysInStrikeDatabase:enabled"/>
@@ -20231,6 +20232,7 @@
   <int value="2043141918" label="DnsOverHttps:enabled"/>
   <int value="2043184476"
       label="SecurePaymentConfirmationAvailabilityAPI:enabled"/>
+  <int value="2043217488" label="AutofillAiNoFillingIconsExperiment:enabled"/>
   <int value="2043321329" label="OfflinePagesPrefetchingUI:disabled"/>
   <int value="2043419490" label="DevToolsStartingStyleDebugging:disabled"/>
   <int value="2043794900" label="CommercePriceTracking:enabled"/>
diff --git a/tools/metrics/histograms/metadata/enterprise/histograms.xml b/tools/metrics/histograms/metadata/enterprise/histograms.xml
index 449f0d1..11d2d2e 100644
--- a/tools/metrics/histograms/metadata/enterprise/histograms.xml
+++ b/tools/metrics/histograms/metadata/enterprise/histograms.xml
@@ -3250,7 +3250,7 @@
 </histogram>
 
 <histogram name="Enterprise.ProfileSeparation.DasherPolicyFetch.HttpResponse"
-    enum="HttpResponseCode" expires_after="2025-07-01">
+    enum="HttpResponseCode" expires_after="2026-07-01">
   <owner>ydago@chromium.org</owner>
   <owner>zmin@chromium.org</owner>
   <summary>
@@ -3258,17 +3258,23 @@
     ManagedAccountsSigninRestriction policy value for individual dasher
     accounts. This is recorded after trying to the the policy value, when the
     status is available.
+
+    Warning: this histogram was expired from 2025-07-01 to 2026-04-15; data may
+    be missing.
   </summary>
 </histogram>
 
 <histogram name="Enterprise.ProfileSeparation.DasherPolicyFetch.NetworkError"
-    enum="NetErrorCodes" expires_after="2025-07-01">
+    enum="NetErrorCodes" expires_after="2026-07-01">
   <owner>ydago@chromium.org</owner>
   <owner>zmin@chromium.org</owner>
   <summary>
     The network errors that happen when trying to get the
     ManagedAccountsSigninRestriction policy value for individual dasher
     accounts. This is recorded every time after trying to the the policy value.
+
+    Warning: this histogram was expired from 2025-07-01 to 2026-04-15; data may
+    be missing.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index f8af432..e074c37d 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -5416,6 +5416,27 @@
   </token>
 </histogram>
 
+<histogram name="NQE.IsPrivateHost.CacheHit" enum="BooleanCacheHit"
+    expires_after="2027-04-01">
+  <owner>hayato@chromium.org</owner>
+  <owner>blink-network-stack@google.com</owner>
+  <summary>
+    Counts how many times the result of NetworkQualityEstimator::IsPrivateHost
+    is served from the LRU cache or not. Recorded every time the method is
+    called.
+  </summary>
+</histogram>
+
+<histogram name="NQE.IsPrivateHost.Duration" units="ms"
+    expires_after="2027-04-01">
+  <owner>hayato@chromium.org</owner>
+  <owner>blink-network-stack@google.com</owner>
+  <summary>
+    The duration of NetworkQualityEstimator::IsPrivateHost. Recorded every time
+    the method is called.
+  </summary>
+</histogram>
+
 <histogram name="NQE.RTT.Error.Absolute" units="ms" expires_after="2025-06-08">
   <owner>yyanagisawa@chromium.org</owner>
   <owner>chrome-loading@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/enums.xml b/tools/metrics/histograms/metadata/safe_browsing/enums.xml
index 2f14f4db..1804ddb 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/enums.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/enums.xml
@@ -378,7 +378,7 @@
   <int value="0" label="DuringHashRealTimeLookup"/>
   <int value="1" label="AsyncFetch"/>
   <int value="2" label="KeyRelatedHttpErrorCode"/>
-  <int value="3" label="KeyRotatedHeader"/>
+  <int value="3" label="(Obsolete) KeyRotatedHeader"/>
 </enum>
 
 <enum name="SafeBrowsingPageLoadTokenClearReason">
diff --git a/tools/metrics/histograms/metadata/signin/histograms.xml b/tools/metrics/histograms/metadata/signin/histograms.xml
index 575f810..9ec2552 100644
--- a/tools/metrics/histograms/metadata/signin/histograms.xml
+++ b/tools/metrics/histograms/metadata/signin/histograms.xml
@@ -1557,10 +1557,13 @@
 </histogram>
 
 <histogram name="Signin.Enterprise.WorkProfile.ProfileCreatedWithPolicySet"
-    enum="BooleanCreated" expires_after="2025-07-01">
+    enum="BooleanCreated" expires_after="2026-07-01">
   <owner>ydago@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <summary>
+    Warning: this histogram was expired from 2025-07-01 to 2026-04-15; data may
+    be missing.
+
     Whether the user chose to create a new work profile after a signin while
     profile separation is enforced by policy. This is recorded after the user
     accepts or refuses to create a new profile when shown the enterprise profile
@@ -1569,10 +1572,13 @@
 </histogram>
 
 <histogram name="Signin.Enterprise.WorkProfile.ProfileCreatedwithPolicyUnset"
-    enum="BooleanCreated" expires_after="2025-07-01">
+    enum="BooleanCreated" expires_after="2026-07-01">
   <owner>ydago@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <summary>
+    Warning: this histogram was expired from 2025-07-01 to 2026-04-15; data may
+    be missing.
+
     Whether the user chose to create a new work profile after a signin while
     profile separation is not enforced by policy. This is recorded after the
     user accepts or refuses to create a new profile when shown the enterprise
diff --git a/tools/perf/fetch_benchmark_deps.py b/tools/perf/fetch_benchmark_deps.py
index ceeccee..e1410e5 100755
--- a/tools/perf/fetch_benchmark_deps.py
+++ b/tools/perf/fetch_benchmark_deps.py
@@ -10,7 +10,6 @@
 import os
 import sys
 import logging
-from concurrent.futures import ThreadPoolExecutor
 from six.moves import input  # pylint: disable=redefined-builtin
 
 from chrome_telemetry_build import chromium_config
@@ -85,7 +84,7 @@
   return deps
 
 
-def FetchDepsForCrossbench(max_workers=None):
+def FetchDepsForCrossbench():
   # Note: Any new crossbench archives need to be added below
   cb_story_sets = [
       speedometer3_pages.Speedometer30CrossbenchStory(),
@@ -93,10 +92,8 @@
       crossbench_embedder.EmbedderCrossbenchStorySet(),
       crossbench_gma_embedder.GmaEmbedderCrossbenchStorySet(),
   ]
-  with ThreadPoolExecutor(max_workers=max_workers) as executor:
-    list(
-        executor.map(lambda s: s.wpr_archive_info.DownloadArchivesIfNeeded(),
-                     cb_story_sets))
+  for story_set in cb_story_sets:
+    story_set.wpr_archive_info.DownloadArchivesIfNeeded()
 
   platform = platform_module.GetHostPlatform()
   binary_manager.InitDependencyManager(None)
@@ -128,11 +125,6 @@
   parser.add_argument(
         '-v', '--verbose', action='count', dest='verbosity', default=0,
         help='Increase verbosity level (repeat as needed)')
-  parser.add_argument('-j',
-                      '--jobs',
-                      type=int,
-                      help='Number of concurrent jobs to run. '
-                      'Defaults to ThreadPoolExecutor default.')
 
   options = parser.parse_args(args)
 
@@ -158,23 +150,14 @@
     if not options.force:
       input('No benchmark name is specified. Fetching all benchmark deps. '
             'Press enter to continue...')
-    benchmarks_to_fetch = []
     for b in benchmark_finders.GetOfficialBenchmarks():
       supported_platforms = b.GetSupportedPlatformNames(b.SUPPORTED_PLATFORMS)
       if(not options.platform or
          options.platform in supported_platforms or
          'all' in supported_platforms):
-        benchmarks_to_fetch.append(b)
+        deps[b.Name()] = _FetchDepsForBenchmark(b)
 
-    def Fetch(b):
-      return b.Name(), _FetchDepsForBenchmark(b)
-
-    with ThreadPoolExecutor(max_workers=options.jobs) as executor:
-      results = executor.map(Fetch, benchmarks_to_fetch)
-      for name, benchmark_deps in results:
-        deps[name] = benchmark_deps
-
-  FetchDepsForCrossbench(max_workers=options.jobs)
+  FetchDepsForCrossbench()
 
   if options.output_deps:
     with open(options.output_deps, 'w') as outfile:
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index e521d49..4b6d1d8 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -232,7 +232,7 @@
  <item id="webrtc_peer_connection" added_in_milestone="66" content_hash_code="039bf82b" os_list="linux,windows,chromeos,android" file_path="third_party/blink/renderer/modules/peerconnection/peer_connection_dependency_factory.cc" />
  <item id="websocket_basic_stream" added_in_milestone="66" content_hash_code="040f7353" os_list="linux,windows,chromeos,android" file_path="net/websockets/websocket_basic_stream.cc" />
  <item id="websocket_stream" added_in_milestone="62" content_hash_code="0064beee" os_list="linux,windows,chromeos,android" file_path="content/browser/websockets/websocket_connector_impl.cc" />
- <item id="webstore_install_helper" added_in_milestone="62" content_hash_code="009bbc99" os_list="linux,windows,chromeos" file_path="chrome/browser/extensions/webstore_install_helper.cc" />
+ <item id="webstore_install_helper" added_in_milestone="62" content_hash_code="05401ec3" os_list="linux,windows,chromeos" file_path="extensions/browser/webstore_install_helper.cc" />
  <item id="webstore_installer" added_in_milestone="62" content_hash_code="05cbc758" os_list="linux,windows,chromeos" file_path="extensions/browser/webstore_installer.cc" />
  <item id="webui_content_scripts_download" added_in_milestone="62" content_hash_code="07257fcb" os_list="linux,windows,chromeos" file_path="extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.cc" />
  <item id="well_known_path_that_should_not_exist" added_in_milestone="86" content_hash_code="052bc423" os_list="windows,chromeos,android,linux" file_path="components/password_manager/core/browser/well_known_change_password/well_known_change_password_state.cc" />
diff --git a/tools/typescript/definitions/autofill_private.d.ts b/tools/typescript/definitions/autofill_private.d.ts
index 9ff16769..84f5c2d0 100644
--- a/tools/typescript/definitions/autofill_private.d.ts
+++ b/tools/typescript/definitions/autofill_private.d.ts
@@ -284,6 +284,7 @@
         nickname: string;
         shouldAuthenticateToView?: boolean;
         storedInWallet?: boolean;
+        isReadOnly?: boolean;
       }
 
       export interface EntityInstanceWithLabels {
@@ -292,7 +293,8 @@
         entityInstanceLabel: string;
         entityInstanceSubLabel: string;
         storedInWallet: boolean;
-        walletEntityUrl?: string
+        walletEntityUrl?: string;
+        isReadOnly?: boolean;
       }
 
       export interface PayOverTimeIssuerEntry {
diff --git a/v8 b/v8
index 97206e5..de3dee3 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 97206e58a92220c1b9b45ba6a22225903c96d9b1
+Subproject commit de3dee3a9fef6b33968ade6f6e870f7e635a5443