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