diff --git a/DEPS b/DEPS index 0281c6a..3da2fb8 100644 --- a/DEPS +++ b/DEPS
@@ -299,15 +299,15 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '12dbc34d742ee67f1b267dc276fe7d84631aa856', + 'skia_revision': '0feee17aeacab6b88ac8be3d8b35ae4c940eeea4', # 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': '577adbe0c15f8fa0643d5f91cc92a06c271c3456', + 'v8_revision': '1beea5ff6680e267f28e29054239ebcdd4ccb861', # 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': '214b7ce20bb6a308c8e929743cd8402f3f5204dd', + 'angle_revision': 'db33baf4eb0d7954f0110cddc30acb9cdc12e2d4', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -319,7 +319,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. - 'boringssl_revision': '45cab558a838e1a546391406e7cbe1eec9b6e643', + 'boringssl_revision': '136284f8548bc7fb43e99e7f69e03fab57168e8b', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Fuchsia sdk # and whatever else without interference from each other. @@ -371,7 +371,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '5255e1a11a4d0bf747f304816a2523f952be9228', + 'catapult_revision': '0c5acc073dd14893f2a75a51819ad81627098551', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling CrossBench # and whatever else without interference from each other. @@ -391,7 +391,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling devtools-frontend # and whatever else without interference from each other. - 'devtools_frontend_revision': '4cc0688b9538f497ae06e3a0f97431ffc86f4181', + 'devtools_frontend_revision': 'c049d058ae5973f47a21fb1e4eb7a224174ef032', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libprotobuf-mutator # and whatever else without interference from each other. @@ -415,7 +415,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': 'f4cae4afdd4bca082d36025c66c7c284ed16a67f', + 'dawn_revision': '606e03a22bfe5686be18fcc934555f55f987c1e9', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -519,7 +519,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling llvm-libc # and whatever else without interference from each other. - 'llvm_libc_revision': '61d7ed30110f97d6842304bc9035ee9cdae6b5e5', + 'llvm_libc_revision': '4a2940b40b394ca57312aa9bbc8af430fe9a5340', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling llvm-libc # and whatever else without interference from each other. @@ -1486,7 +1486,7 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - 'af42a48a53e7717ae26d7cd6e90195216fb9fe51', + '687392ba3de05f6fae3002d7b7435b231d048dce', 'condition': 'checkout_android and checkout_src_internal', }, @@ -1645,7 +1645,7 @@ 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': 'HYTISQQTMh5XcYAUlGBAU6HIXb_FD0kMzljZh0v03j0C', + 'version': '1jSxEkUFGSfsQTosCWjXFmS0h3Lk_BRPBBLwBNosOVYC', }, ], 'condition': 'checkout_android and non_git_source', @@ -1978,7 +1978,7 @@ 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'd1f9fa6c922b20d8034fe4c7f3a62f8a824c561b', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '977c37458fda472d8822a8b57e4a83a7bc747471', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), @@ -2526,7 +2526,7 @@ Var('pdfium_git') + '/pdfium.git' + '@' + Var('pdfium_revision'), 'src/third_party/perfetto': - Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '829564494ca9d7bc1c8b15998c5f1f3536cdacf8', + Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '6ffe623d283575c00047cf9abf90c3659505a592', 'src/base/tracing/test/data': { 'bucket': 'perfetto', @@ -4614,7 +4614,7 @@ 'src/components/optimization_guide/internal': { 'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' + - '6032e32fa8b82048023e47709f1e5a22dd277791', + '3633af3203b28054ee2d0aafd69b7a687dce4d1c', 'condition': 'checkout_src_internal', },
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnReceivedHttpErrorTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnReceivedHttpErrorTest.java index b482b10..71982b0 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnReceivedHttpErrorTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnReceivedHttpErrorTest.java
@@ -23,9 +23,11 @@ import org.chromium.android_webview.AwWebResourceRequest; import org.chromium.android_webview.test.util.AwTestTouchUtils; import org.chromium.android_webview.test.util.CommonResources; +import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.Feature; import org.chromium.components.embedder_support.util.WebResourceResponseInfo; import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer; +import org.chromium.content_public.browser.test.util.WebContentsUtils; import org.chromium.net.test.util.TestWebServer; import java.util.ArrayList; @@ -152,6 +154,8 @@ null); AwActivityTestRule.enableJavaScriptOnUiThread(mAwContents); mActivityTestRule.loadUrlAsync(mAwContents, pageWithLinkUrl); + ThreadUtils.runOnUiThreadBlocking( + () -> WebContentsUtils.simulateEndOfPaintHolding(mAwContents.getWebContents())); mActivityTestRule.waitForPixelColorAtCenterOfView( mAwContents, mTestContainerView, CommonResources.LINK_COLOR);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ContextMenuTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ContextMenuTest.java index 0dd432c..eed1505 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/ContextMenuTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/ContextMenuTest.java
@@ -47,6 +47,7 @@ import org.chromium.android_webview.contextmenu.AwContextMenuItemDelegate; import org.chromium.android_webview.contextmenu.AwContextMenuPopulator; import org.chromium.android_webview.test.AwActivityTestRule.TestDependencyFactory; +import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.Batch; import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.CriteriaHelper; @@ -56,6 +57,7 @@ import org.chromium.components.embedder_support.contextmenu.ContextMenuParams; import org.chromium.content_public.browser.test.util.DOMUtils; import org.chromium.content_public.browser.test.util.TestTouchUtils; +import org.chromium.content_public.browser.test.util.WebContentsUtils; import org.chromium.net.test.util.TestWebServer; import org.chromium.ui.modelutil.MVCListAdapter.ModelList; import org.chromium.ui.mojom.MenuSourceType; @@ -452,6 +454,8 @@ mRule.loadUrlSync( mTestContainerView.getAwContents(), mContentsClient.getOnPageFinishedHelper(), url); done.waitForCallback(callCount); + ThreadUtils.runOnUiThreadBlocking( + () -> WebContentsUtils.simulateEndOfPaintHolding(mAwContents.getWebContents())); } private void assertStringContains(String subString, String superString) {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java index 8a577e8..ce36fed 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java
@@ -40,6 +40,7 @@ import org.chromium.content_public.browser.SelectionPopupController; import org.chromium.content_public.browser.test.util.DOMUtils; import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer; +import org.chromium.content_public.browser.test.util.WebContentsUtils; import org.chromium.net.test.util.TestWebServer; import java.util.List; @@ -179,7 +180,6 @@ mParentContents, mParentContentsClient, "navigator.userAgent"); final String popupPath = "/popup.html"; - final String myUserAgentString = "myUserAgent"; final String parentPageHtml = CommonResources.makeHtmlPageFrom( "", @@ -248,7 +248,6 @@ "tryOpenWindow()"); PopupInfo popupInfo = mActivityTestRule.createPopupContents(mParentContents); - TestAwContentsClient popupContentsClient = popupInfo.popupContentsClient; final AwContents popupContents = popupInfo.popupContents; // Override the user agent string for the popup window. @@ -436,12 +435,12 @@ hasOpener ? "" : "rel=\"noopener noreferrer\""); final String mainHtml = CommonResources.makeHtmlPageFrom("", body); final String openerUrl = mWebServer.setResponse("/popupOpener.html", mainHtml, null); - final String popupUrl = - mWebServer.setResponse( - "/popup.html", - CommonResources.makeHtmlPageFrom( - "<title>" + POPUP_TITLE + "</title>", "This is a popup window"), - null); + + mWebServer.setResponse( + "/popup.html", + CommonResources.makeHtmlPageFrom( + "<title>" + POPUP_TITLE + "</title>", "This is a popup window"), + null); mParentContentsClient.getOnCreateWindowHelper().setReturnValue(true); mActivityTestRule.loadUrlSync( @@ -633,6 +632,10 @@ // https://crbug.com/1334843 mParentContentsClient.getOnPageCommitVisibleHelper().waitForOnly(); + // Force an end of paint-holding which is irrelevant here and can block input events. + ThreadUtils.runOnUiThreadBlocking( + () -> WebContentsUtils.simulateEndOfPaintHolding(mParentContents.getWebContents())); + // Step 4. Click iframe_link to give user gesture. DOMUtils.clickRect(mParentContents.getWebContents(), rect);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewModalDialogOverrideTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewModalDialogOverrideTest.java index 00c5e542..06cbfc1 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/WebViewModalDialogOverrideTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/WebViewModalDialogOverrideTest.java
@@ -24,6 +24,7 @@ import org.chromium.base.test.util.Feature; import org.chromium.content_public.browser.GestureListenerManager; import org.chromium.content_public.browser.GestureStateListener; +import org.chromium.content_public.browser.test.util.WebContentsUtils; import java.util.concurrent.atomic.AtomicBoolean; @@ -251,6 +252,9 @@ BEFORE_UNLOAD_URL, "text/html", false); + ThreadUtils.runOnUiThreadBlocking( + () -> WebContentsUtils.simulateEndOfPaintHolding(awContents.getWebContents())); + AwActivityTestRule.enableJavaScriptOnUiThread(awContents); // JavaScript onbeforeunload dialogs require a user gesture. tapViewAndWait(view);
diff --git a/chrome/VERSION b/chrome/VERSION index 72218cbc..86c1d06 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=138 MINOR=0 -BUILD=7172 +BUILD=7173 PATCH=0
diff --git a/chrome/android/features/tab_ui/java/res/drawable/chevron_right.xml b/chrome/android/features/tab_ui/java/res/drawable/chevron_right.xml index 1cba635d..ff032bf1 100644 --- a/chrome/android/features/tab_ui/java/res/drawable/chevron_right.xml +++ b/chrome/android/features/tab_ui/java/res/drawable/chevron_right.xml
@@ -5,13 +5,14 @@ found in the LICENSE file. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" -android:width="24dp" -android:height="24dp" -android:viewportWidth="24" -android:viewportHeight="24" -android:tint="@macro/default_icon_color"> -<path - android:fillColor="@android:color/white" - android:pathData="M7.59,18.59L9,20l8,-8 -8,-8 -1.41,1.41L14.17,12"/> +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="@macro/default_icon_color"> + <path + android:fillColor="@android:color/white" + android:pathData="M7.59,18.59L9,20l8,-8 -8,-8 -1.41,1.41L14.17,12"/> </vector>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java index 510fb48..41d0730 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
@@ -218,7 +218,6 @@ dataSharingTabManager, mComponentName, showColorPickerPopupRunnable, - actionConfirmationManager, modalDialogManager, desktopWindowStateManager, tabBookmarkerSupplier,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java index 86328b2..2617593f 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -48,7 +48,6 @@ import org.chromium.chrome.browser.tab.TabLaunchType; import org.chromium.chrome.browser.tab.TabSelectionType; import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory; -import org.chromium.chrome.browser.tab_ui.ActionConfirmationManager; import org.chromium.chrome.browser.tab_ui.RecyclerViewPosition; import org.chromium.chrome.browser.tab_ui.TabUiThemeUtils; import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter; @@ -81,6 +80,7 @@ import org.chromium.components.browser_ui.styles.SemanticColorUtils; import org.chromium.components.browser_ui.widget.gesture.BackPressHandler; import org.chromium.components.collaboration.CollaborationService; +import org.chromium.components.collaboration.CollaborationServiceLeaveOrDeleteEntryPoint; import org.chromium.components.collaboration.CollaborationServiceShareOrManageEntryPoint; import org.chromium.components.collaboration.messaging.CollaborationEvent; import org.chromium.components.collaboration.messaging.MessageUtils; @@ -226,8 +226,6 @@ private final DataSharingTabManager mDataSharingTabManager; private final String mComponentName; private final Runnable mShowColorPickerPopupRunnable; - private final ActionConfirmationManager mActionConfirmationManager; - private final ModalDialogManager mModalDialogManager; private final Profile mOriginalProfile; private final @Nullable TabGroupSyncService mTabGroupSyncService; private final @Nullable DataSharingService mDataSharingService; @@ -267,7 +265,6 @@ @NonNull DataSharingTabManager dataSharingTabManager, String componentName, Runnable showColorPickerPopupRunnable, - @Nullable ActionConfirmationManager actionConfirmationManager, @Nullable ModalDialogManager modalDialogManager, @Nullable DesktopWindowStateManager desktopWindowStateManager, ObservableSupplier<TabBookmarker> tabBookmarkerSupplier, @@ -286,8 +283,6 @@ mDataSharingTabManager = dataSharingTabManager; mComponentName = componentName; mShowColorPickerPopupRunnable = showColorPickerPopupRunnable; - mActionConfirmationManager = actionConfirmationManager; - mModalDialogManager = modalDialogManager; mOriginalProfile = mCurrentTabGroupModelFilterSupplier .get() @@ -1050,6 +1045,7 @@ mTransitiveSharedGroupObserver.getCollaborationIdSupplier().get()); int tabId = mCurrentTabId; + EitherGroupId eitherId = EitherGroupId.createLocalId(new LocalTabGroupId(tabGroupId)); if (tabId == Tab.INVALID_TAB_ID) return; if (menuId == R.id.ungroup_tab || menuId == R.id.select_tabs) { @@ -1070,8 +1066,7 @@ } else if (menuId == R.id.manage_sharing) { RecordUserAction.record("TabGridDialogMenu.ManageSharing"); mDataSharingTabManager.createOrManageFlow( - mActivity, - EitherGroupId.createLocalId(new LocalTabGroupId(tabGroupId)), + eitherId, CollaborationServiceShareOrManageEntryPoint.ANDROID_TAB_GRID_DIALOG_MANAGE, /* createGroupFinishedCallback= */ null); } else if (menuId == R.id.recent_activity) { @@ -1093,20 +1088,14 @@ /* didCloseCallback= */ null); } else if (menuId == R.id.delete_shared_group) { RecordUserAction.record("TabGridDialogMenu.DeleteShared"); - TabUiUtils.exitSharedTabGroupWithDialog( - mActivity, - mCurrentTabGroupModelFilterSupplier.get(), - mActionConfirmationManager, - mModalDialogManager, - tabId); + mDataSharingTabManager.leaveOrDeleteFlow( + eitherId, + CollaborationServiceLeaveOrDeleteEntryPoint.ANDROID_TAB_GRID_DIALOG_DELETE); } else if (menuId == R.id.leave_group) { RecordUserAction.record("TabGridDialogMenu.LeaveShared"); - TabUiUtils.exitSharedTabGroupWithDialog( - mActivity, - mCurrentTabGroupModelFilterSupplier.get(), - mActionConfirmationManager, - mModalDialogManager, - tabId); + mDataSharingTabManager.leaveOrDeleteFlow( + eitherId, + CollaborationServiceLeaveOrDeleteEntryPoint.ANDROID_TAB_GRID_DIALOG_LEAVE); } }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java index 45b46c1..736c5ff 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java
@@ -21,6 +21,7 @@ import org.chromium.chrome.browser.collaboration.CollaborationServiceFactory; import org.chromium.chrome.browser.collaboration.messaging.MessagingBackendServiceFactory; import org.chromium.chrome.browser.data_sharing.DataSharingServiceFactory; +import org.chromium.chrome.browser.data_sharing.DataSharingTabManager; import org.chromium.chrome.browser.hub.PaneManager; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.ProfileProvider; @@ -79,6 +80,7 @@ * @param modalDialogManager Used to show confirmation dialogs. * @param onIsScrolledChanged To be invoked whenever the scrolled state changes. * @param edgeToEdgeSupplier Supplier to the {@link EdgeToEdgeController} instance. + * @param dataSharingTabManager The {@link} DataSharingTabManager to start collaboration flows. */ public TabGroupListCoordinator( Context context, @@ -88,7 +90,8 @@ TabGroupUiActionHandler tabGroupUiActionHandler, ModalDialogManager modalDialogManager, Consumer<Boolean> onIsScrolledChanged, - @NonNull ObservableSupplier<EdgeToEdgeController> edgeToEdgeSupplier) { + @NonNull ObservableSupplier<EdgeToEdgeController> edgeToEdgeSupplier, + @NonNull DataSharingTabManager dataSharingTabManager) { ModelList modelList = new ModelList(); mSimpleRecyclerViewAdapter = new SimpleRecyclerViewAdapter(modelList) { @@ -178,8 +181,8 @@ tabGroupUiActionHandler, actionConfirmationManager, syncService, - modalDialogManager, - enableContainment()); + enableContainment(), + dataSharingTabManager); if (EdgeToEdgeUtils.isDrawKeyNativePageToEdgeEnabled()) { mEdgeToEdgePadAdjuster =
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediator.java index 609586e..cde008d1 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediator.java
@@ -29,6 +29,7 @@ import org.chromium.base.CallbackController; import org.chromium.base.task.TaskTraits; import org.chromium.chrome.browser.bookmarks.PendingRunnable; +import org.chromium.chrome.browser.data_sharing.DataSharingTabManager; import org.chromium.chrome.browser.hub.PaneManager; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab_ui.ActionConfirmationManager; @@ -53,7 +54,6 @@ import org.chromium.components.tab_group_sync.TabGroupSyncService.Observer; import org.chromium.components.tab_group_sync.TabGroupUiActionHandler; import org.chromium.components.tab_group_sync.TriggerSource; -import org.chromium.ui.modaldialog.ModalDialogManager; import org.chromium.ui.modelutil.MVCListAdapter.ListItem; import org.chromium.ui.modelutil.MVCListAdapter.ModelList; import org.chromium.ui.modelutil.PropertyModel; @@ -76,7 +76,6 @@ private final TabGroupUiActionHandler mTabGroupUiActionHandler; private final ActionConfirmationManager mActionConfirmationManager; private final SyncService mSyncService; - private final ModalDialogManager mModalDialogManager; private final CallbackController mCallbackController = new CallbackController(); private final @NonNull MessagingBackendService mMessagingBackendService; private final PendingRunnable mPendingRefresh = @@ -84,6 +83,7 @@ TaskTraits.UI_DEFAULT, mCallbackController.makeCancelable(this::repopulateModelList)); private final boolean mEnableContainment; + private final DataSharingTabManager mDataSharingTabManager; private final TabModelObserver mTabModelObserver = new TabModelObserver() { @@ -204,8 +204,8 @@ * @param tabGroupUiActionHandler Used to open hidden tab groups. * @param actionConfirmationManager Used to show confirmation dialogs. * @param syncService Used to query active sync types. - * @param modalDialogManager Used to show error dialogs. * @param enableContainment Whether containment is enabled. + * @param dataSharingTabManager The {@link} DataSharingTabManager to start collaboration flows. */ public TabGroupListMediator( Context context, @@ -221,8 +221,8 @@ TabGroupUiActionHandler tabGroupUiActionHandler, ActionConfirmationManager actionConfirmationManager, SyncService syncService, - ModalDialogManager modalDialogManager, - boolean enableContainment) { + boolean enableContainment, + @NonNull DataSharingTabManager dataSharingTabManager) { mContext = context; mModelList = modelList; mPropertyModel = propertyModel; @@ -236,8 +236,8 @@ mTabGroupUiActionHandler = tabGroupUiActionHandler; mActionConfirmationManager = actionConfirmationManager; mSyncService = syncService; - mModalDialogManager = modalDialogManager; mEnableContainment = enableContainment; + mDataSharingTabManager = dataSharingTabManager; mFilter.addObserver(mTabModelObserver); if (mTabGroupSyncService != null) { @@ -291,11 +291,11 @@ mCollaborationService, mPaneManager, mTabGroupUiActionHandler, - mModalDialogManager, mActionConfirmationManager, mFaviconResolver, () -> sortUtil.getState(savedTabGroup), - mEnableContainment); + mEnableContainment, + mDataSharingTabManager); ListItem listItem = new ListItem(RowType.TAB_GROUP, rowMediator.getModel()); mModelList.add(listItem); }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java index f30ecba..2e97c5c 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java
@@ -57,6 +57,7 @@ import org.chromium.base.test.util.Features.DisableFeatures; import org.chromium.base.test.util.Features.EnableFeatures; import org.chromium.chrome.browser.collaboration.messaging.MessagingBackendServiceFactory; +import org.chromium.chrome.browser.data_sharing.DataSharingTabManager; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.hub.PaneId; import org.chromium.chrome.browser.hub.PaneManager; @@ -94,8 +95,6 @@ import org.chromium.components.tab_group_sync.TriggerSource; import org.chromium.components.tab_groups.TabGroupColorId; import org.chromium.ui.modaldialog.ModalDialogManager; -import org.chromium.ui.modaldialog.ModalDialogProperties; -import org.chromium.ui.modaldialog.ModalDialogProperties.ButtonType; import org.chromium.ui.modelutil.MVCListAdapter.ModelList; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.test.util.MockitoHelper; @@ -143,6 +142,7 @@ @Mock private ModalDialogManager mModalDialogManager; @Mock private MessagingBackendService mMessagingBackendService; @Mock private Runnable mFinishBlocking; + @Mock private DataSharingTabManager mDataSharingTabManager; @Captor private ArgumentCaptor<TabModelObserver> mTabModelObserver; @Captor private ArgumentCaptor<TabGroupSyncService.Observer> mTabGroupSyncObserverCaptor; @@ -206,8 +206,8 @@ mTabGroupUiActionHandler, mActionConfirmationManager, mSyncService, - mModalDialogManager, - /* enableContainment= */ true); + /* enableContainment= */ true, + mDataSharingTabManager); verify(mSyncService).addSyncStateChangedListener(mSyncStateChangedListenerCaptor.capture()); return mediator; } @@ -650,21 +650,7 @@ assertNotNull(model.get(DELETE_RUNNABLE)); model.get(DELETE_RUNNABLE).run(); - verify(mActionConfirmationManager) - .processDeleteSharedGroupAttempt( - any(), mMaybeBlockingResultCallbackCaptor.capture()); - mMaybeBlockingResultCallbackCaptor - .getValue() - .onResult( - new MaybeBlockingResult( - ActionConfirmationResult.CONFIRMATION_POSITIVE, mFinishBlocking)); - - verify(mCollaborationService) - .deleteGroup(eq(COLLABORATION_ID1), mDeleteGroupResultCallbackCaptor.capture()); - mDeleteGroupResultCallbackCaptor.getValue().onResult(true); - verify(mFinishBlocking).run(); - verify(mModalDialogManager, never()) - .showDialog(mModalPropertyModelCaptor.capture(), anyInt()); + verify(mDataSharingTabManager).leaveOrDeleteFlow(any(), anyInt()); } @Test @@ -692,25 +678,7 @@ assertNotNull(model.get(LEAVE_RUNNABLE)); model.get(LEAVE_RUNNABLE).run(); - verify(mActionConfirmationManager) - .processLeaveGroupAttempt(any(), mMaybeBlockingResultCallbackCaptor.capture()); - mMaybeBlockingResultCallbackCaptor - .getValue() - .onResult( - new MaybeBlockingResult( - ActionConfirmationResult.CONFIRMATION_POSITIVE, mFinishBlocking)); - - verify(mCollaborationService) - .leaveGroup(eq(COLLABORATION_ID1), mDeleteGroupResultCallbackCaptor.capture()); - mDeleteGroupResultCallbackCaptor.getValue().onResult(false); - verify(mFinishBlocking).run(); - - verify(mModalDialogManager).showDialog(mModalPropertyModelCaptor.capture(), anyInt()); - - ModalDialogProperties.Controller controller = - mModalPropertyModelCaptor.getValue().get(ModalDialogProperties.CONTROLLER); - controller.onClick(mModalPropertyModelCaptor.getValue(), ButtonType.POSITIVE); - verify(mModalDialogManager).dismissDialog(any(), anyInt()); + verify(mDataSharingTabManager).leaveOrDeleteFlow(any(), anyInt()); } @Test
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediator.java index a434cb2..edcf1af 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediator.java
@@ -8,7 +8,6 @@ import static org.chromium.chrome.browser.tasks.tab_management.TabGroupRowProperties.LEAVE_RUNNABLE; import android.content.Context; -import android.text.TextUtils; import androidx.annotation.ColorInt; import androidx.annotation.Nullable; @@ -18,30 +17,30 @@ import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordUserAction; import org.chromium.chrome.R; +import org.chromium.chrome.browser.data_sharing.DataSharingTabManager; import org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesConfig; import org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesCoordinator; import org.chromium.chrome.browser.hub.PaneId; import org.chromium.chrome.browser.hub.PaneManager; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab_ui.ActionConfirmationManager; -import org.chromium.chrome.browser.tab_ui.ActionConfirmationManager.MaybeBlockingResult; import org.chromium.chrome.browser.tabmodel.TabClosureParams; import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter; -import org.chromium.chrome.browser.tabmodel.TabGroupTitleUtils; import org.chromium.chrome.browser.tasks.tab_management.TabGroupFaviconCluster.ClusterData; import org.chromium.chrome.browser.tasks.tab_management.TabGroupRowView.TabGroupRowViewTitleData; import org.chromium.chrome.browser.tasks.tab_management.TabGroupTimeAgo.TimestampEvent; import org.chromium.components.browser_ui.styles.SemanticColorUtils; import org.chromium.components.browser_ui.widget.ActionConfirmationResult; import org.chromium.components.collaboration.CollaborationService; +import org.chromium.components.collaboration.CollaborationServiceLeaveOrDeleteEntryPoint; import org.chromium.components.data_sharing.DataSharingService; import org.chromium.components.data_sharing.GroupData; import org.chromium.components.data_sharing.member_role.MemberRole; +import org.chromium.components.tab_group_sync.EitherId.EitherGroupId; import org.chromium.components.tab_group_sync.SavedTabGroup; import org.chromium.components.tab_group_sync.SavedTabGroupTab; import org.chromium.components.tab_group_sync.TabGroupSyncService; import org.chromium.components.tab_group_sync.TabGroupUiActionHandler; -import org.chromium.ui.modaldialog.ModalDialogManager; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.url.GURL; @@ -58,10 +57,10 @@ private final CollaborationService mCollaborationService; private final PaneManager mPaneManager; private final TabGroupUiActionHandler mTabGroupUiActionHandler; - private final ModalDialogManager mModalDialogManager; private final ActionConfirmationManager mActionConfirmationManager; private final Supplier<@GroupWindowState Integer> mFetchGroupState; private final PropertyModel mPropertyModel; + private final DataSharingTabManager mDataSharingTabManager; private SharedImageTilesCoordinator mSharedImageTilesCoordinator; @@ -73,11 +72,11 @@ * @param collaborationService Used to fetch collaboration group data. * @param paneManager Used switch panes to show details of a group. * @param tabGroupUiActionHandler Used to open hidden tab groups. - * @param modalDialogManager Used to show error dialogs. * @param actionConfirmationManager Used to show confirmation dialogs. * @param faviconResolver Used to fetch favicon images for some tabs. * @param fetchGroupState Used to fetch which window the group is in. * @param enableContainment Whether the tab group row is in a container. + * @param dataSharingTabManager The {@link} DataSharingTabManager to start collaboration flows. */ public TabGroupRowMediator( Context context, @@ -88,11 +87,11 @@ CollaborationService collaborationService, PaneManager paneManager, TabGroupUiActionHandler tabGroupUiActionHandler, - ModalDialogManager modalDialogManager, ActionConfirmationManager actionConfirmationManager, FaviconResolver faviconResolver, Supplier<@GroupWindowState Integer> fetchGroupState, - boolean enableContainment) { + boolean enableContainment, + DataSharingTabManager dataSharingTabManager) { mContext = context; mSavedTabGroup = savedTabGroup; mTabGroupModelFilter = tabGroupModelFilter; @@ -101,9 +100,9 @@ mDataSharingService = dataSharingService; mCollaborationService = collaborationService; mTabGroupUiActionHandler = tabGroupUiActionHandler; - mModalDialogManager = modalDialogManager; mActionConfirmationManager = actionConfirmationManager; mFetchGroupState = fetchGroupState; + mDataSharingTabManager = dataSharingTabManager; PropertyModel.Builder builder = new PropertyModel.Builder(TabGroupRowProperties.ALL_KEYS); int numberOfTabs = savedTabGroup.savedTabs.size(); @@ -135,7 +134,7 @@ groupData = mCollaborationService.getGroupData(collaborationId); sharedState = TabShareUtils.discernSharedGroupState(groupData); } - setSharedProperties(sharedState, groupData, numberOfTabs, enableContainment); + setSharedProperties(sharedState, groupData, enableContainment, savedTabGroup); } /** @@ -157,8 +156,8 @@ private void setSharedProperties( @GroupSharedState int sharedState, @Nullable GroupData groupData, - int numberOfTabs, - boolean enableContainment) { + boolean enableContainment, + SavedTabGroup savedTabGroup) { if (sharedState == GroupSharedState.NOT_SHARED) { mPropertyModel.set(DELETE_RUNNABLE, this::processDeleteGroup); mPropertyModel.set(LEAVE_RUNNABLE, null); @@ -168,19 +167,17 @@ } String collaborationId = groupData.groupToken.collaborationId; - String groupTitle = groupTitleWithFallback(groupData, numberOfTabs); @MemberRole int memberRole = mCollaborationService.getCurrentUserRoleForGroup(collaborationId); if (memberRole == MemberRole.OWNER) { mPropertyModel.set( - DELETE_RUNNABLE, () -> processDeleteSharedGroup(groupTitle, collaborationId)); + DELETE_RUNNABLE, () -> processLeaveOrDeleteShareGroup(savedTabGroup)); mPropertyModel.set(LEAVE_RUNNABLE, null); } else { // TODO(crbug.com/365852281): Leave action should look like a delete if there are no // other users. mPropertyModel.set(DELETE_RUNNABLE, null); - mPropertyModel.set( - LEAVE_RUNNABLE, () -> processLeaveGroup(groupTitle, collaborationId)); + mPropertyModel.set(LEAVE_RUNNABLE, () -> processLeaveOrDeleteShareGroup(savedTabGroup)); } if (sharedState == GroupSharedState.COLLABORATION_ONLY) { @@ -272,46 +269,16 @@ } } - private void processDeleteSharedGroup(String groupTitle, String collaborationId) { - // TODO(crbug.com/365852281): Confirmation should look like a non-shared delete if there are - // no other users. - mActionConfirmationManager.processDeleteSharedGroupAttempt( - groupTitle, - (result) -> { - exitCollaborationWithoutWarningWrapper( - collaborationId, result, MemberRole.OWNER); - }); - } - - private void processLeaveGroup(String groupTitle, String collaborationId) { - // TODO(crbug.com/365852281): Confirmation should look like a non-shared delete if there are - // no other users. - mActionConfirmationManager.processLeaveGroupAttempt( - groupTitle, - (result) -> { - exitCollaborationWithoutWarningWrapper( - collaborationId, result, MemberRole.MEMBER); - }); - } - - private void exitCollaborationWithoutWarningWrapper( - String collaborationId, - MaybeBlockingResult maybeBlockingResult, - @MemberRole int memberRole) { - if (maybeBlockingResult.result != ActionConfirmationResult.CONFIRMATION_NEGATIVE) { - assert maybeBlockingResult.finishBlocking != null; - TabUiUtils.exitCollaborationWithoutWarning( - mContext, - mModalDialogManager, - mCollaborationService, - collaborationId, - memberRole, - maybeBlockingResult.finishBlocking); - } else if (maybeBlockingResult.finishBlocking != null) { - assert false : "Should not be reachable."; - // Do the safe thing and run the runnable anyway. - maybeBlockingResult.finishBlocking.run(); + private void processLeaveOrDeleteShareGroup(SavedTabGroup savedTabGroup) { + EitherGroupId eitherId; + if (savedTabGroup.syncId != null) { + eitherId = EitherGroupId.createSyncId(savedTabGroup.syncId); + } else { + eitherId = EitherGroupId.createLocalId(savedTabGroup.localId); } + + mDataSharingTabManager.leaveOrDeleteFlow( + eitherId, CollaborationServiceLeaveOrDeleteEntryPoint.ANDROID_TAB_GROUP_ROW); } private void deleteGroup(boolean allowDialog) { @@ -351,13 +318,4 @@ mTabGroupSyncService.removeGroup(mSavedTabGroup.syncId); } } - - private String groupTitleWithFallback(GroupData groupData, int numberOfTabs) { - String groupTitle = groupData.displayName; - if (TextUtils.isEmpty(groupTitle)) { - return TabGroupTitleUtils.getDefaultTitle(mContext, numberOfTabs); - } else { - return groupTitle; - } - } }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediatorUnitTest.java index 9f1a97e..c225f4e 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediatorUnitTest.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediatorUnitTest.java
@@ -51,6 +51,7 @@ import org.chromium.base.Token; import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.Features.EnableFeatures; +import org.chromium.chrome.browser.data_sharing.DataSharingTabManager; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.hub.PaneId; import org.chromium.chrome.browser.hub.PaneManager; @@ -69,6 +70,7 @@ import org.chromium.components.data_sharing.GroupMember; import org.chromium.components.data_sharing.SharedGroupTestHelper; import org.chromium.components.data_sharing.member_role.MemberRole; +import org.chromium.components.tab_group_sync.EitherId.EitherGroupId; import org.chromium.components.tab_group_sync.LocalTabGroupId; import org.chromium.components.tab_group_sync.SavedTabGroup; import org.chromium.components.tab_group_sync.SavedTabGroupTab; @@ -107,6 +109,7 @@ @Mock private FaviconResolver mFaviconResolver; @Mock private Supplier<@GroupWindowState Integer> mFetchGroupState; @Mock private TabSwitcherPaneBase mTabSwitcherPaneBase; + @Mock private DataSharingTabManager mDataSharingTabManager; @Captor private ArgumentCaptor<Callback<@ActionConfirmationResult Integer>> mConfirmationCaptor; @@ -179,11 +182,11 @@ mCollaborationService, mPaneManager, mTabGroupUiActionHandler, - mModalDialogManager, mActionConfirmationManager, mFaviconResolver, mFetchGroupState, - /* enableContainment= */ true); + /* enableContainment= */ true, + mDataSharingTabManager); return mediator.getModel(); } @@ -434,7 +437,8 @@ assertNotNull(propertyModel.get(DELETE_RUNNABLE)); assertNull(propertyModel.get(LEAVE_RUNNABLE)); propertyModel.get(DELETE_RUNNABLE).run(); - verify(mActionConfirmationManager).processDeleteSharedGroupAttempt(eq(TITLE), any()); + EitherGroupId eitherId = EitherGroupId.createSyncId(SYNC_GROUP_ID1); + verify(mDataSharingTabManager).leaveOrDeleteFlow(eq(eitherId), anyInt()); } @Test @@ -454,6 +458,7 @@ assertNull(propertyModel.get(DELETE_RUNNABLE)); assertNotNull(propertyModel.get(LEAVE_RUNNABLE)); propertyModel.get(LEAVE_RUNNABLE).run(); - verify(mActionConfirmationManager).processLeaveGroupAttempt(eq("2 tabs"), any()); + EitherGroupId eitherId = EitherGroupId.createSyncId(SYNC_GROUP_ID1); + verify(mDataSharingTabManager).leaveOrDeleteFlow(eq(eitherId), anyInt()); } }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPane.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPane.java index 27e8019..55ff12eb 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPane.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPane.java
@@ -18,6 +18,7 @@ import org.chromium.base.supplier.ObservableSupplierImpl; import org.chromium.base.supplier.OneshotSupplier; import org.chromium.base.supplier.Supplier; +import org.chromium.chrome.browser.data_sharing.DataSharingTabManager; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.hub.DelegateButtonData; import org.chromium.chrome.browser.hub.DisplayButtonData; @@ -61,6 +62,7 @@ new ObservableSupplierImpl<>(); private final ObservableSupplierImpl<Boolean> mHubSearchEnabledStateSupplier = new ObservableSupplierImpl<>(); + private final DataSharingTabManager mDataSharingTabManager; private TabGroupListCoordinator mTabGroupListCoordinator; private final ObservableSupplier<EdgeToEdgeController> mEdgeToEdgeSupplier; @@ -74,6 +76,7 @@ * @param tabGroupUiActionHandlerSupplier Used to open hidden tab groups. * @param modalDialogManagerSupplier Used to create confirmation dialogs. * @param edgeToEdgeSupplier Supplier to the {@link EdgeToEdgeController} instance. + * @param dataSharingTabManager The {@link} DataSharingTabManager to start collaboration flows. */ TabGroupsPane( @NonNull Context context, @@ -83,7 +86,8 @@ @NonNull Supplier<PaneManager> paneManagerSupplier, @NonNull Supplier<TabGroupUiActionHandler> tabGroupUiActionHandlerSupplier, @NonNull Supplier<ModalDialogManager> modalDialogManagerSupplier, - @NonNull ObservableSupplier<EdgeToEdgeController> edgeToEdgeSupplier) { + @NonNull ObservableSupplier<EdgeToEdgeController> edgeToEdgeSupplier, + @NonNull DataSharingTabManager dataSharingTabManager) { mContext = context; mTabGroupModelFilterSupplier = tabGroupModelFilterSupplier; mOnToolbarAlphaChange = onToolbarAlphaChange; @@ -92,6 +96,7 @@ mTabGroupUiActionHandlerSupplier = tabGroupUiActionHandlerSupplier; mModalDialogManagerSupplier = modalDialogManagerSupplier; mEdgeToEdgeSupplier = edgeToEdgeSupplier; + mDataSharingTabManager = dataSharingTabManager; if (ChromeFeatureList.sTabGroupEntryPointsAndroid.isEnabled()) { TabGroupCreationUiDelegate flow = new TabGroupCreationUiDelegate( @@ -168,7 +173,8 @@ mTabGroupUiActionHandlerSupplier.get(), mModalDialogManagerSupplier.get(), mHairlineVisibilitySupplier::set, - mEdgeToEdgeSupplier); + mEdgeToEdgeSupplier, + mDataSharingTabManager); mRootView.addView(mTabGroupListCoordinator.getView()); } else if (loadHint == LoadHint.COLD && mTabGroupListCoordinator != null) { destroy();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPaneUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPaneUnitTest.java index c6b44c0f..9078bf6 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPaneUnitTest.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupsPaneUnitTest.java
@@ -39,6 +39,7 @@ import org.chromium.chrome.browser.collaboration.CollaborationServiceFactory; import org.chromium.chrome.browser.collaboration.messaging.MessagingBackendServiceFactory; import org.chromium.chrome.browser.data_sharing.DataSharingServiceFactory; +import org.chromium.chrome.browser.data_sharing.DataSharingTabManager; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.hub.FullButtonData; import org.chromium.chrome.browser.hub.LoadHint; @@ -88,6 +89,7 @@ @Mock private IdentityServicesProvider mIdentityServicesProvider; @Mock private IdentityManager mIdentityManager; @Mock private Supplier<PaneManager> mPaneManagerSupplier; + @Mock private DataSharingTabManager mDataSharingTabManager; @Mock Supplier<TabGroupUiActionHandler> mTabGroupUiActionHandlerSupplier; @Mock FaviconHelper.Natives mFaviconHelperJniMock; @Mock SyncService mSyncService; @@ -135,7 +137,8 @@ mPaneManagerSupplier, mTabGroupUiActionHandlerSupplier, mModalDialogManagerSupplier, - mEdgeToEdgeSupplier); + mEdgeToEdgeSupplier, + mDataSharingTabManager); } @Test
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java index a1ca12a..1ee8269 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -330,7 +330,6 @@ priceWelcomeMessageControllerSupplier, componentName, initialTabActionState, - actionConfirmationManager, dataSharingTabManager, onTabGroupCreation);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java index 7de808c..d6dc1517 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -72,7 +72,6 @@ import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData; import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncFeatures; import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory; -import org.chromium.chrome.browser.tab_ui.ActionConfirmationManager; import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider; import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider.TabFaviconFetcher; import org.chromium.chrome.browser.tab_ui.ThumbnailProvider; @@ -96,6 +95,7 @@ import org.chromium.chrome.tab_ui.R; import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate; import org.chromium.components.collaboration.CollaborationService; +import org.chromium.components.collaboration.CollaborationServiceLeaveOrDeleteEntryPoint; import org.chromium.components.collaboration.CollaborationServiceShareOrManageEntryPoint; import org.chromium.components.data_sharing.DataSharingService; import org.chromium.components.embedder_support.util.UrlUtilities; @@ -375,7 +375,6 @@ private final GridCardOnClickListenerProvider mGridCardOnClickListenerProvider; private final TabGridDialogHandler mTabGridDialogHandler; private final Supplier<PriceWelcomeMessageController> mPriceWelcomeMessageControllerSupplier; - private final @Nullable ActionConfirmationManager mActionConfirmationManager; private final @Nullable DataSharingTabManager mDataSharingTabManager; private final Runnable mOnTabGroupCreation; private final TabModelObserver mTabModelObserver; @@ -945,7 +944,6 @@ * @param componentName This is a unique string to identify different components. * @param initialTabActionState The initial {@link TabActionState} to use for the shown tabs. * Must always be CLOSABLE for TabListMode.STRIP. - * @param actionConfirmationManager Used for showing confirmation dialogs. * @param dataSharingTabManager The service used to initiate data sharing. * @param onTabGroupCreation Should be run when the UI is used to create a tab group. */ @@ -964,7 +962,6 @@ @NonNull Supplier<PriceWelcomeMessageController> priceWelcomeMessageControllerSupplier, String componentName, @TabActionState int initialTabActionState, - @Nullable ActionConfirmationManager actionConfirmationManager, @Nullable DataSharingTabManager dataSharingTabManager, @Nullable Runnable onTabGroupCreation) { mActivity = activity; @@ -981,7 +978,6 @@ mPriceWelcomeMessageControllerSupplier = priceWelcomeMessageControllerSupplier; mComponentName = componentName; mTabActionState = initialTabActionState; - mActionConfirmationManager = actionConfirmationManager; mDataSharingTabManager = dataSharingTabManager; mOnTabGroupCreation = onTabGroupCreation; @@ -2819,6 +2815,7 @@ void onMenuItemClicked(@IdRes int menuId, Token tabGroupId, @Nullable String collaborationId) { TabGroupModelFilter filter = mCurrentTabGroupModelFilterSupplier.get(); int tabId = filter.getGroupLastShownTabId(tabGroupId); + EitherGroupId eitherId = EitherGroupId.createLocalId(new LocalTabGroupId(tabGroupId)); if (tabId == Tab.INVALID_TAB_ID) return; if (menuId == R.id.close_tab_group || menuId == R.id.delete_tab_group) { @@ -2844,25 +2841,21 @@ TabUiUtils.ungroupTabGroup(filter, tabGroupId); } else if (menuId == R.id.delete_shared_group) { RecordUserAction.record("TabGroupItemMenu.DeleteShared"); - TabUiUtils.exitSharedTabGroupWithDialog( - mActivity, filter, mActionConfirmationManager, mModalDialogManager, tabId); + mDataSharingTabManager.leaveOrDeleteFlow( + eitherId, + CollaborationServiceLeaveOrDeleteEntryPoint.ANDROID_TAB_GROUP_ITEM_MENU_DELETE); } else if (menuId == R.id.leave_group) { RecordUserAction.record("TabGroupItemMenu.LeaveShared"); - TabUiUtils.exitSharedTabGroupWithDialog( - mActivity, filter, mActionConfirmationManager, mModalDialogManager, tabId); + mDataSharingTabManager.leaveOrDeleteFlow( + eitherId, + CollaborationServiceLeaveOrDeleteEntryPoint.ANDROID_TAB_GROUP_ITEM_MENU_LEAVE); } else if (menuId == R.id.share_group) { assert mDataSharingTabManager != null; RecordUserAction.record("TabGroupItemMenu.ShareGroup"); - @Nullable PropertyModel model = mModelList.getModelFromTabId(tabId); - if (model == null) return; - - TabUiUtils.startShareTabGroupFlow( - mActivity, - filter, - mDataSharingTabManager, - tabId, - model.get(TabProperties.TITLE), - CollaborationServiceShareOrManageEntryPoint.TAB_GROUP_ITEM_MENU_SHARE); + mDataSharingTabManager.createOrManageFlow( + eitherId, + CollaborationServiceShareOrManageEntryPoint.TAB_GROUP_ITEM_MENU_SHARE, + /* createGroupFinishedCallback= */ null); } }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java index 3e291eb3..546e38b 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegate.java
@@ -171,6 +171,7 @@ * groups. * @param modalDialogManagerSupplier Used to show confirmation dialogs. * @param edgeToEdgeSupplier Supplier to the {@link EdgeToEdgeController} instance. + * @param dataSharingTabManager The {@link} DataSharingTabManager to start collaboration flows. * @return The pane implementation that displays and allows interactions with tab groups. */ Pane createTabGroupsPane( @@ -181,7 +182,8 @@ @NonNull LazyOneshotSupplier<HubManager> hubManagerSupplier, @NonNull Supplier<TabGroupUiActionHandler> tabGroupUiActionHandlerSupplier, @NonNull Supplier<ModalDialogManager> modalDialogManagerSupplier, - @NonNull ObservableSupplier<EdgeToEdgeController> edgeToEdgeSupplier); + @NonNull ObservableSupplier<EdgeToEdgeController> edgeToEdgeSupplier, + @NonNull DataSharingTabManager dataSharingTabManager); /** * Create a {@link TabGroupCreationUiDelegate} for tab group creation UI flows.
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java index a00ccab..5753f69 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateImpl.java
@@ -202,7 +202,8 @@ @NonNull LazyOneshotSupplier<HubManager> hubManagerSupplier, @NonNull Supplier<TabGroupUiActionHandler> tabGroupUiActionHandlerSupplier, @NonNull Supplier<ModalDialogManager> modalDialogManagerSupplier, - @NonNull ObservableSupplier<EdgeToEdgeController> edgeToEdgeSupplier) { + @NonNull ObservableSupplier<EdgeToEdgeController> edgeToEdgeSupplier, + @NonNull DataSharingTabManager dataSharingTabManager) { LazyOneshotSupplier<TabGroupModelFilter> tabGroupModelFilterSupplier = LazyOneshotSupplier.fromSupplier( () -> @@ -217,7 +218,8 @@ () -> hubManagerSupplier.get().getPaneManager(), tabGroupUiActionHandlerSupplier, modalDialogManagerSupplier, - edgeToEdgeSupplier); + edgeToEdgeSupplier, + dataSharingTabManager); } @Override
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java index 09e060d..68bd91e 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java
@@ -325,7 +325,7 @@ LocalTabGroupId localTabGroupId = TabGroupSyncUtils.getLocalTabGroupId(tab); dataSharingTabManager.createOrManageFlow( - activity, EitherGroupId.createLocalId(localTabGroupId), entry, (ignored) -> {}); + EitherGroupId.createLocalId(localTabGroupId), entry, (ignored) -> {}); } /**
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorTest.java index 3044916..98edf322e 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorTest.java
@@ -45,7 +45,6 @@ import org.chromium.base.test.util.CallbackHelper; 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.Feature; import org.chromium.base.test.util.Features.DisableFeatures; @@ -84,7 +83,6 @@ @DoNotBatch(reason = "TODO(crbug.com/348068134): Batch this test suite.") @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) @DisableFeatures("IPH_AndroidTabDeclutter") -@DisabledTest(message = "crbug.com/397759336") public class ArchivedTabsDialogCoordinatorTest { @Rule public FreshCtaTransitTestRule mCtaTestRule =
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java index 6388bbb..df8e0c50 100644 --- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
@@ -326,11 +326,11 @@ mModel.get(TabGridDialogProperties.SHARE_BUTTON_CLICK_LISTENER).onClick(null); verify(mDataSharingTabManager) - .createOrManageFlow(eq(mActivity), eq(EITHER_LOCAL_TAB_GROUP_ID), anyInt(), any()); + .createOrManageFlow(eq(EITHER_LOCAL_TAB_GROUP_ID), anyInt(), any()); mModel.get(TabGridDialogProperties.SHARE_IMAGE_TILES_CLICK_LISTENER).onClick(null); verify(mDataSharingTabManager, times(2)) - .createOrManageFlow(eq(mActivity), eq(EITHER_LOCAL_TAB_GROUP_ID), anyInt(), any()); + .createOrManageFlow(eq(EITHER_LOCAL_TAB_GROUP_ID), anyInt(), any()); mModel.get(TabGridDialogProperties.SEND_FEEDBACK_RUNNABLE).run(); ArgumentCaptor<String> categoryCaptor = ArgumentCaptor.forClass(String.class); @@ -1526,7 +1526,7 @@ mMediator.onToolbarMenuItemClick(R.id.manage_sharing, TAB_GROUP_ID, COLLABORATION_ID1); assertEquals(1, mActionTester.getActionCount("TabGridDialogMenu.ManageSharing")); verify(mDataSharingTabManager) - .createOrManageFlow(any(), eq(EITHER_LOCAL_TAB_GROUP_ID), anyInt(), eq(null)); + .createOrManageFlow(eq(EITHER_LOCAL_TAB_GROUP_ID), anyInt(), eq(null)); } @Test @@ -1551,8 +1551,7 @@ .thenReturn(MemberRole.OWNER); mMediator.onToolbarMenuItemClick(R.id.delete_shared_group, TAB_GROUP_ID, COLLABORATION_ID1); - verify(mActionConfirmationManager).processDeleteSharedGroupAttempt(eq(GROUP_TITLE), any()); - assertEquals(1, mActionTester.getActionCount("TabGridDialogMenu.DeleteShared")); + verify(mDataSharingTabManager).leaveOrDeleteFlow(eq(EITHER_LOCAL_TAB_GROUP_ID), anyInt()); } @Test @@ -1567,8 +1566,7 @@ .thenReturn(MemberRole.MEMBER); mMediator.onToolbarMenuItemClick(R.id.leave_group, TAB_GROUP_ID, COLLABORATION_ID1); - verify(mActionConfirmationManager).processLeaveGroupAttempt(eq(GROUP_TITLE), any()); - assertEquals(1, mActionTester.getActionCount("TabGridDialogMenu.LeaveShared")); + verify(mDataSharingTabManager).leaveOrDeleteFlow(eq(EITHER_LOCAL_TAB_GROUP_ID), anyInt()); } @Test @@ -1944,7 +1942,6 @@ mDataSharingTabManager, /* componentName= */ "", mShowColorPickerPopupRunnable, - mActionConfirmationManager, mModalDialogManager, mDesktopWindowStateManager, mTabBookmarkerSupplier,
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java index 2727bfa..5836166 100644 --- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -130,7 +130,6 @@ import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncFeatures; import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncFeaturesJni; import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory; -import org.chromium.chrome.browser.tab_ui.ActionConfirmationManager; import org.chromium.chrome.browser.tab_ui.TabContentManager; import org.chromium.chrome.browser.tab_ui.TabContentManagerThumbnailProvider; import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider; @@ -339,7 +338,6 @@ @Mock ShoppingPersistedTabData mShoppingPersistedTabData; @Mock SelectionDelegate<TabListEditorItemSelectionId> mSelectionDelegate; @Mock ModalDialogManager mModalDialogManager; - @Mock ActionConfirmationManager mActionConfirmationManager; @Mock DataSharingTabManager mDataSharingTabManager; @Mock TabGroupSyncFeatures.Natives mTabGroupSyncFeaturesJniMock; @Mock IdentityServicesProvider mIdentityServicesProvider; @@ -1742,7 +1740,6 @@ null, getClass().getSimpleName(), TabActionState.CLOSABLE, - mActionConfirmationManager, mDataSharingTabManager, /* onTabGroupCreation= */ null); mMediator.initWithNative(mProfile); @@ -3522,7 +3519,6 @@ null, getClass().getSimpleName(), TabProperties.TabActionState.CLOSABLE, - mActionConfirmationManager, mDataSharingTabManager, /* onTabGroupCreation= */ null); mMediator.registerOrientationListener(mGridLayoutManager); @@ -3556,7 +3552,6 @@ null, getClass().getSimpleName(), TabProperties.TabActionState.CLOSABLE, - mActionConfirmationManager, mDataSharingTabManager, /* onTabGroupCreation= */ null); mMediator.registerOrientationListener(mGridLayoutManager); @@ -4016,7 +4011,6 @@ null, getClass().getSimpleName(), TabProperties.TabActionState.SELECTABLE, - mActionConfirmationManager, mDataSharingTabManager, /* onTabGroupCreation= */ null); mMediator.registerOrientationListener(mGridLayoutManager); @@ -4063,7 +4057,6 @@ null, getClass().getSimpleName(), TabProperties.TabActionState.SELECTABLE, - mActionConfirmationManager, mDataSharingTabManager, /* onTabGroupCreation= */ null); mMediator.registerOrientationListener(mGridLayoutManager); @@ -4110,7 +4103,6 @@ null, getClass().getSimpleName(), TabProperties.TabActionState.SELECTABLE, - mActionConfirmationManager, mDataSharingTabManager, /* onTabGroupCreation= */ null); mMediator.registerOrientationListener(mGridLayoutManager); @@ -4418,7 +4410,7 @@ assertNotNull(mModelList.get(POSITION1).model.get(TabProperties.TAB_ACTION_BUTTON_DATA)); when(mTabGroupModelFilter.getGroupLastShownTabId(TAB_GROUP_ID)).thenReturn(TAB1_ID); mMediator.onMenuItemClicked(R.id.share_group, TAB_GROUP_ID, /* collaborationId= */ null); - verify(mDataSharingTabManager).createOrManageFlow(eq(mActivity), any(), anyInt(), any()); + verify(mDataSharingTabManager).createOrManageFlow(any(), anyInt(), any()); } @Test @@ -4530,7 +4522,6 @@ null, getClass().getSimpleName(), TabProperties.TabActionState.CLOSABLE, - mActionConfirmationManager, mDataSharingTabManager, /* onTabGroupCreation= */ null); mMediator.registerOrientationListener(mGridLayoutManager); @@ -4903,7 +4894,6 @@ null, getClass().getSimpleName(), TabProperties.TabActionState.CLOSABLE, - mActionConfirmationManager, mDataSharingTabManager, /* onTabGroupCreation= */ null); mMediator.registerOrientationListener(mGridLayoutManager); @@ -5128,7 +5118,6 @@ null, getClass().getSimpleName(), tabActionState, - mActionConfirmationManager, mDataSharingTabManager, /* onTabGroupCreation= */ null); TrackerFactory.setTrackerForTests(mTracker);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index 817db4f0..c55b465 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1060,7 +1060,8 @@ ((TabbedRootUiCoordinator) mRootUiCoordinator) .getTabGroupSyncController(), getModalDialogManagerSupplier(), - mEdgeToEdgeControllerSupplier); + mEdgeToEdgeControllerSupplier, + mRootUiCoordinator.getDataSharingTabManager()); } private Pane createHistoryPane() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java index 2a8db4f..dc605e9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
@@ -49,6 +49,7 @@ import org.chromium.chrome.tab_ui.R; import org.chromium.components.browser_ui.widget.BrowserUiListMenuUtils; import org.chromium.components.collaboration.CollaborationService; +import org.chromium.components.collaboration.CollaborationServiceLeaveOrDeleteEntryPoint; import org.chromium.components.collaboration.CollaborationServiceShareOrManageEntryPoint; import org.chromium.components.data_sharing.member_role.MemberRole; import org.chromium.components.embedder_support.util.UrlConstants; @@ -180,6 +181,8 @@ DataSharingTabManager dataSharingTabManager) { return (menuId, tabGroupId, collaborationId) -> { int tabId = tabGroupModelFilter.getGroupLastShownTabId(tabGroupId); + EitherGroupId eitherId = EitherGroupId.createLocalId(new LocalTabGroupId(tabGroupId)); + if (tabId == Tab.INVALID_TAB_ID) return; if (menuId == org.chromium.chrome.R.id.ungroup_tab) { @@ -209,26 +212,16 @@ TabLaunchType.FROM_TAB_GROUP_UI); recordUserAction("NewTabInGroup"); } else if (menuId == org.chromium.chrome.R.id.share_group) { - // Get user assigned group title or the default title "N tabs" if no title is - // assigned. - String tabGroupDisplayName = - TabGroupTitleUtils.getDisplayableTitle( - activity, tabGroupModelFilter, tabGroupId); - // Create the group share flow and display the share bottom sheet. - TabUiUtils.startShareTabGroupFlow( - activity, - tabGroupModelFilter, - dataSharingTabManager, - tabId, - tabGroupDisplayName, + dataSharingTabManager.createOrManageFlow( + eitherId, CollaborationServiceShareOrManageEntryPoint - .ANDROID_TAB_GROUP_CONTEXT_MENU_SHARE); + .ANDROID_TAB_GROUP_CONTEXT_MENU_SHARE, + /* createGroupFinishedCallback= */ null); recordUserAction("ShareGroup"); } else if (menuId == R.id.manage_sharing) { dataSharingTabManager.createOrManageFlow( - activity, - EitherGroupId.createLocalId(new LocalTabGroupId(tabGroupId)), + eitherId, CollaborationServiceShareOrManageEntryPoint .ANDROID_TAB_GROUP_CONTEXT_MENU_MANAGE, /* createGroupFinishedCallback= */ null); @@ -237,20 +230,16 @@ dataSharingTabManager.showRecentActivity(activity, collaborationId); recordUserAction("RecentActivity"); } else if (menuId == R.id.delete_shared_group) { - TabUiUtils.exitSharedTabGroupWithDialog( - activity, - tabGroupModelFilter, - actionConfirmationManager, - modalDialogManager, - tabId); + dataSharingTabManager.leaveOrDeleteFlow( + eitherId, + CollaborationServiceLeaveOrDeleteEntryPoint + .ANDROID_TAB_GROUP_CONTEXT_MENU_DELETE); recordUserAction("DeleteSharedGroup"); } else if (menuId == R.id.leave_group) { - TabUiUtils.exitSharedTabGroupWithDialog( - activity, - tabGroupModelFilter, - actionConfirmationManager, - modalDialogManager, - tabId); + dataSharingTabManager.leaveOrDeleteFlow( + eitherId, + CollaborationServiceLeaveOrDeleteEntryPoint + .ANDROID_TAB_GROUP_CONTEXT_MENU_LEAVE); recordUserAction("LeaveSharedGroup"); } };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java index 32aae124..7f73d731 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -19,16 +19,20 @@ import androidx.annotation.CallSuper; import androidx.annotation.ColorInt; +import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.WindowInsetsCompat; import org.chromium.base.BuildInfo; import org.chromium.base.Callback; import org.chromium.base.CallbackController; import org.chromium.base.TraceEvent; import org.chromium.base.lifetime.Destroyable; +import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordUserAction; import org.chromium.base.supplier.ObservableSupplier; import org.chromium.base.supplier.ObservableSupplierImpl; @@ -95,6 +99,7 @@ import org.chromium.chrome.browser.messages.MessagesResourceMapperInitializer; import org.chromium.chrome.browser.metrics.UmaSessionStats; import org.chromium.chrome.browser.multiwindow.MultiInstanceManager; +import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; import org.chromium.chrome.browser.omnibox.OmniboxFocusReason; import org.chromium.chrome.browser.omnibox.geo.GeolocationHeader; import org.chromium.chrome.browser.omnibox.suggestions.action.OmniboxActionDelegateImpl; @@ -141,6 +146,7 @@ import org.chromium.chrome.browser.ui.appmenu.AppMenuDelegate; import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler; import org.chromium.chrome.browser.ui.appmenu.AppMenuObserver; +import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils; import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController; import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeControllerFactory; import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeUtils; @@ -205,6 +211,22 @@ AppMenuBlocker, ContextualSearchTabPromotionDelegate, WindowFocusChangedObserver { + private static final String MISSING_NAVBAR_INSETS_HISTOGRAM = + "Android.EdgeToEdge.MissingNavbarInsets"; + + @IntDef({ + MissingNavbarInsetsReason.OTHER, + MissingNavbarInsetsReason.IN_MULTI_WINDOW, + MissingNavbarInsetsReason.IN_DESKTOP_WINDOW, + MissingNavbarInsetsReason.NUM_ENTRIES + }) + @interface MissingNavbarInsetsReason { + int OTHER = 0; + int IN_MULTI_WINDOW = 1; + int IN_DESKTOP_WINDOW = 2; + int NUM_ENTRIES = 3; + } + protected final UnownedUserDataSupplier<TabObscuringHandler> mTabObscuringHandlerSupplier = new TabObscuringHandlerSupplier(); @@ -1831,6 +1853,34 @@ mFullscreenManager); mEdgeToEdgeControllerSupplier.set(mEdgeToEdgeController); mEdgeToEdgeBottomChin = createEdgeToEdgeBottomChin(); + + recordIfMissingNavigationBar(); + } + } + + private void recordIfMissingNavigationBar() { + var rootInsets = mActivity.getWindow().getDecorView().getRootWindowInsets(); + assert rootInsets != null; + Insets navigationBarInsets = + WindowInsetsCompat.toWindowInsetsCompat(rootInsets) + .getInsets(WindowInsetsCompat.Type.navigationBars()); + if (!navigationBarInsets.equals(Insets.NONE)) return; + + if (AppHeaderUtils.isAppInDesktopWindow(getDesktopWindowStateManager())) { + RecordHistogram.recordEnumeratedHistogram( + MISSING_NAVBAR_INSETS_HISTOGRAM, + MissingNavbarInsetsReason.IN_DESKTOP_WINDOW, + MissingNavbarInsetsReason.NUM_ENTRIES); + } else if (MultiWindowUtils.getInstance().isInMultiWindowMode(mActivity)) { + RecordHistogram.recordEnumeratedHistogram( + MISSING_NAVBAR_INSETS_HISTOGRAM, + MissingNavbarInsetsReason.IN_MULTI_WINDOW, + MissingNavbarInsetsReason.NUM_ENTRIES); + } else { + RecordHistogram.recordEnumeratedHistogram( + MISSING_NAVBAR_INSETS_HISTOGRAM, + MissingNavbarInsetsReason.OTHER, + MissingNavbarInsetsReason.NUM_ENTRIES); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java index 7a092d1..9404427b 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java
@@ -136,6 +136,7 @@ @Test @SmallTest @Feature({"Location"}) + @DisabledTest(message = "https://crbug.com/416787235") public void testProtoEncoding() { setPermission(ContentSettingValues.ALLOW); long now = setMockLocationNow();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/WebPaymentIntentHelperTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/WebPaymentIntentHelperTest.java index 7770e380..b4117485 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/WebPaymentIntentHelperTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/WebPaymentIntentHelperTest.java
@@ -121,8 +121,8 @@ Intent intent = WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", "payment.request.id", "merchant.name", "schemeless.origin", @@ -136,8 +136,8 @@ shippingOptions, /* removeDeprecatedFields= */ false); Assert.assertEquals(WebPaymentIntentHelper.ACTION_PAY, intent.getAction()); - Assert.assertEquals("package.name", intent.getComponent().getPackageName()); - Assert.assertEquals("activity.name", intent.getComponent().getClassName()); + Assert.assertEquals("payment.app.package.name", intent.getComponent().getPackageName()); + Assert.assertEquals("payment.app.activity.name", intent.getComponent().getClassName()); Bundle bundle = intent.getExtras(); Assert.assertNotNull(bundle); Assert.assertEquals( @@ -240,8 +240,8 @@ Intent intent = WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", "payment.request.id", "merchant.name", "schemeless.origin", @@ -313,8 +313,8 @@ Intent intent = WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", "payment.request.id", "merchant.name", "schemeless.origin", @@ -345,9 +345,9 @@ @Test @SmallTest @Feature({"Payments"}) - public void nullPackageNameExceptionTest() throws Throwable { + public void nullPaymentAppPackageNameExceptionTest() throws Throwable { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("packageName should not be null or empty."); + thrown.expectMessage("paymentAppPackageName should not be null or empty."); Map<String, PaymentMethodData> methodDataMap = new HashMap<String, PaymentMethodData>(); PaymentMethodData bobPayMethodData = new PaymentMethodData("method", "null"); @@ -356,8 +356,8 @@ PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200")); WebPaymentIntentHelper.createPayIntent( - /* packageName= */ null, - "activity.name", + /* paymentAppPackageName= */ null, + "payment.app.activity.name", "payment.request.id", "merchant.name", "schemeless.origin", @@ -377,7 +377,7 @@ @Feature({"Payments"}) public void nullActivityNameExceptionTest() throws Throwable { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("activityName should not be null or empty."); + thrown.expectMessage("paymentAppActivityName should not be null or empty."); Map<String, PaymentMethodData> methodDataMap = new HashMap<String, PaymentMethodData>(); PaymentMethodData bobPayMethodData = new PaymentMethodData("method", "null"); @@ -386,8 +386,8 @@ PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200")); WebPaymentIntentHelper.createPayIntent( - "package.name", - /* activityName= */ null, + "payment.app.package.name", + /* paymentAppActivityName= */ null, "payment.request.id", "merchant.name", "schemeless.origin", @@ -416,8 +416,8 @@ PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200")); WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", /* id= */ null, "merchant.name", "schemeless.origin", @@ -446,8 +446,8 @@ PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200")); WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", /* id= */ "", "merchant.name", "schemeless.origin", @@ -476,8 +476,8 @@ PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200")); WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", "id", /* merchantName= */ null, "schemeless.origin", @@ -503,8 +503,8 @@ PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200")); WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", "id", /* merchantName= */ "", "schemeless.origin", @@ -533,8 +533,8 @@ PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200")); WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", "id", "merchant.name", /* schemelessOrigin= */ null, @@ -563,8 +563,8 @@ PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200")); WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", "id", "merchant.name", /* schemelessOrigin= */ "", @@ -593,8 +593,8 @@ PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200")); WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", "id", "merchant.name", "schemeless.origin", @@ -623,8 +623,8 @@ PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200")); WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", "id", "merchant.name", "schemeless.origin", @@ -655,8 +655,8 @@ PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200")); WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", "id", "merchant.name", "schemeless.origin", @@ -685,8 +685,8 @@ PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200")); WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", "payment.request.id", "merchant.name", "schemeless.origin", @@ -712,8 +712,8 @@ PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200")); WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", "payment.request.id", "merchant.name", "schemeless.origin", @@ -741,8 +741,8 @@ PaymentItem total = new PaymentItem(new PaymentCurrencyAmount("CAD", "200")); WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", "payment.request.id", "merchant.name", "schemeless.origin", @@ -769,8 +769,8 @@ methodDataMap.put("bobPay", bobPayMethodData); WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", "id", "merchant.name", "schemeless.origin", @@ -804,8 +804,8 @@ modifiers.put("bobPay", null); WebPaymentIntentHelper.createPayIntent( - "package.name", - "activity.name", + "payment.app.package.name", + "payment.app.activity.name", "payment.request.id", "merchant.name", "schemeless.origin", @@ -1129,8 +1129,9 @@ Intent intent = WebPaymentIntentHelper.createIsReadyToPayIntent( - "package.name", - "service.name", + "caller.package.name", + "payment.app.package.name", + "payment.app.service.name", "schemeless.origin", "schemeless.iframe.origin", certificateChain, @@ -1138,11 +1139,14 @@ /* clearIdFields= */ false, /* removeDeprecatedFields= */ false); Assert.assertEquals(WebPaymentIntentHelper.ACTION_IS_READY_TO_PAY, intent.getAction()); - Assert.assertEquals("package.name", intent.getComponent().getPackageName()); - Assert.assertEquals("service.name", intent.getComponent().getClassName()); + Assert.assertEquals("payment.app.package.name", intent.getComponent().getPackageName()); + Assert.assertEquals("payment.app.service.name", intent.getComponent().getClassName()); Bundle bundle = intent.getExtras(); Assert.assertNotNull(bundle); Assert.assertEquals( + "caller.package.name", + bundle.get(WebPaymentIntentHelper.EXTRA_CALLER_PACKAGE_NAME)); + Assert.assertEquals( "schemeless.origin", bundle.get(WebPaymentIntentHelper.EXTRA_TOP_ORIGIN)); Assert.assertEquals( "schemeless.iframe.origin", @@ -1181,8 +1185,9 @@ Intent intent = WebPaymentIntentHelper.createIsReadyToPayIntent( - "package.name", - "service.name", + "caller.package.name", + "payment.app.package.name", + "payment.app.service.name", "schemeless.origin", "schemeless.iframe.origin", certificateChain, @@ -1222,8 +1227,9 @@ Intent intent = WebPaymentIntentHelper.createIsReadyToPayIntent( - "package.name", - "service.name", + "caller.package.name", + "payment.app.package.name", + "payment.app.service.name", "schemeless.origin", "schemeless.iframe.origin", certificateChain, @@ -1257,8 +1263,9 @@ Intent intent = WebPaymentIntentHelper.createIsReadyToPayIntent( - "package.name", - "service.name", + "caller.package.name", + "payment.app.package.name", + "payment.app.service.name", "schemeless.origin", "schemeless.iframe.origin", certificateChain, @@ -1266,8 +1273,8 @@ /* clearIdFields= */ true, /* removeDeprecatedFields= */ false); Assert.assertEquals(WebPaymentIntentHelper.ACTION_IS_READY_TO_PAY, intent.getAction()); - Assert.assertEquals("package.name", intent.getComponent().getPackageName()); - Assert.assertEquals("service.name", intent.getComponent().getClassName()); + Assert.assertEquals("payment.app.package.name", intent.getComponent().getPackageName()); + Assert.assertEquals("payment.app.service.name", intent.getComponent().getClassName()); Bundle bundle = intent.getExtras(); Assert.assertNotNull(bundle); Assert.assertEquals(null, bundle.get(WebPaymentIntentHelper.EXTRA_TOP_ORIGIN)); @@ -1285,18 +1292,43 @@ @Test @SmallTest @Feature({"Payments"}) - public void createIsReadyToPayIntentNullPackageNameExceptionTestWithIdentity() + public void createIsReadyToPayIntentNullCallerPackageNameExceptionTestWithIdentity() throws Throwable { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("packageName should not be null or empty."); + thrown.expectMessage("callerPackageName should not be null or empty."); Map<String, PaymentMethodData> methodDataMap = new HashMap<String, PaymentMethodData>(); PaymentMethodData bobPayMethodData = new PaymentMethodData("method", "null"); methodDataMap.put("bobPay", bobPayMethodData); WebPaymentIntentHelper.createIsReadyToPayIntent( - /* packageName= */ null, - "service.name", + /* callerPackageName= */ null, + "payment.app.package.name", + "payment.app.service.name", + "schemeless.origin", + "schemeless.iframe.origin", + /* certificateChain= */ null, + methodDataMap, + /* clearIdFields= */ false, + /* removeDeprecatedFields= */ false); + } + + @Test + @SmallTest + @Feature({"Payments"}) + public void createIsReadyToPayIntentNullPaymentAppPackageNameExceptionTestWithIdentity() + throws Throwable { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("paymentAppPackageName should not be null or empty."); + + Map<String, PaymentMethodData> methodDataMap = new HashMap<String, PaymentMethodData>(); + PaymentMethodData bobPayMethodData = new PaymentMethodData("method", "null"); + methodDataMap.put("bobPay", bobPayMethodData); + + WebPaymentIntentHelper.createIsReadyToPayIntent( + "caller.package.name", + /* paymentAppPackageName= */ null, + "payment.app.service.name", "schemeless.origin", "schemeless.iframe.origin", /* certificateChain= */ null, @@ -1311,15 +1343,16 @@ public void createIsReadyToPayIntentNullPackageNameExceptionTestWithoutIdentity() throws Throwable { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("packageName should not be null or empty."); + thrown.expectMessage("paymentAppPackageName should not be null or empty."); Map<String, PaymentMethodData> methodDataMap = new HashMap<String, PaymentMethodData>(); PaymentMethodData bobPayMethodData = new PaymentMethodData("method", "null"); methodDataMap.put("bobPay", bobPayMethodData); WebPaymentIntentHelper.createIsReadyToPayIntent( - /* packageName= */ null, - "service.name", + "caller.package.name", + /* paymentAppPackageName= */ null, + "payment.app.service.name", "schemeless.origin", "schemeless.iframe.origin", /* certificateChain= */ null, @@ -1334,23 +1367,46 @@ public void createPaymentDetailsUpdateServiceIntent() throws Throwable { Intent intent = WebPaymentIntentHelper.createPaymentDetailsUpdateServiceIntent( - "package.name", "service.name"); + "caller.package.name", + "payment.app.package.name", + "payment.app.service.name"); Assert.assertEquals( WebPaymentIntentHelper.ACTION_UPDATE_PAYMENT_DETAILS, intent.getAction()); - Assert.assertEquals("package.name", intent.getComponent().getPackageName()); - Assert.assertEquals("service.name", intent.getComponent().getClassName()); - Assert.assertNull(intent.getExtras()); + Assert.assertEquals("payment.app.package.name", intent.getComponent().getPackageName()); + Assert.assertEquals("payment.app.service.name", intent.getComponent().getClassName()); + Bundle bundle = intent.getExtras(); + Assert.assertNotNull(bundle); + Assert.assertEquals( + "caller.package.name", + bundle.get(WebPaymentIntentHelper.EXTRA_CALLER_PACKAGE_NAME)); } @Test @SmallTest @Feature({"Payments"}) - public void createPaymentDetailsUpdateServiceIntentThrowsWithoutPackageName() throws Throwable { + public void createPaymentDetailsUpdateServiceIntentThrowsWithoutCallerPackageName() + throws Throwable { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("packageName should not be null or empty."); + thrown.expectMessage("callerPackageName should not be null or empty."); WebPaymentIntentHelper.createPaymentDetailsUpdateServiceIntent( - /* packageName= */ null, "service.name"); + /* callerPackageName= */ null, + "payment.app.package.name", + "payment.app.service.name"); + } + + @Test + @SmallTest + @Feature({"Payments"}) + public void createPaymentDetailsUpdateServiceIntentThrowsWithoutPaymentAppPackageName() + throws Throwable { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("paymentAppPackageName should not be null or empty."); + + WebPaymentIntentHelper.createPaymentDetailsUpdateServiceIntent( + "caller.package.name", + /* paymentAppPackageName= */ null, + "payment.app.service.name"); } @Test @@ -1358,9 +1414,11 @@ @Feature({"Payments"}) public void createPaymentDetailsUpdateServiceIntentThrowsWithoutServiceName() throws Throwable { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("serviceName should not be null or empty."); + thrown.expectMessage("paymentAppServiceName should not be null or empty."); WebPaymentIntentHelper.createPaymentDetailsUpdateServiceIntent( - "package.name", /* serviceName= */ null); + "caller.package.name", + "payment.app.package.name", + /* paymentAppServiceName= */ null); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplTest.java index a001907..926e7f2 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/tab_restore/HistoricalTabSaverImplTest.java
@@ -21,6 +21,7 @@ import org.chromium.base.test.util.Batch; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CriteriaHelper; +import org.chromium.base.test.util.DisabledTest; import org.chromium.chrome.browser.ChromeTabbedActivity; import org.chromium.chrome.browser.app.tabmodel.ArchivedTabModelOrchestrator; import org.chromium.chrome.browser.flags.ChromeSwitches; @@ -119,6 +120,7 @@ */ @Test @MediumTest + @DisabledTest(message = "https://crbug.com/416777174") public void testCreateHistoricalTab_Frozen_HistoricalTabCreated() { final Tab tab = sActivityTestRule.loadUrlInNewTab(getUrl(TEST_PAGE_1), /* incognito= */ false);
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt index 9795381..e339a6c 100644 --- a/chrome/android/profiles/arm.newest.txt +++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@ -chromeos-chrome-arm-138.0.7166.0_rc-r1-merged.afdo.bz2 +chromeos-chrome-arm-138.0.7171.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt index b3e118f7..ebbf4391 100644 --- a/chrome/android/profiles/newest.txt +++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@ -chromeos-chrome-amd64-138.0.7166.0_rc-r1-merged.afdo.bz2 +chromeos-chrome-amd64-138.0.7171.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/browser/ai/ai_data_keyed_service.cc b/chrome/browser/ai/ai_data_keyed_service.cc index f22d2e4d..1582b0f 100644 --- a/chrome/browser/ai/ai_data_keyed_service.cc +++ b/chrome/browser/ai/ai_data_keyed_service.cc
@@ -986,20 +986,18 @@ } // TODO(https://crbug.com/398271171): Remove when the actor coordinator // handles getting a new observation. - auto fetcher = std::make_unique<glic::GlicPageContextFetcher>(); + glic::FocusedTabData focused_tab_data{tab_->GetContents()->GetWeakPtr()}; - fetcher->Fetch( + glic::GlicPageContextFetcher::Fetch( focused_tab_data, DefaultOptions(), base::BindOnce(&AiDataKeyedService::ConvertToBrowserActionResult, - weak_factory_.GetWeakPtr(), std::move(callback), - std::move(fetcher), task_id_, tab_id_, - std::move(action_result))); + weak_factory_.GetWeakPtr(), std::move(callback), task_id_, + tab_id_, std::move(action_result))); } void AiDataKeyedService::ConvertToBrowserActionResult( base::OnceCallback<void(optimization_guide::proto::BrowserActionResult)> callback, - std::unique_ptr<glic::GlicPageContextFetcher> fetcher, int task_id, int tab_id, actor::mojom::ActionResultPtr action_result,
diff --git a/chrome/browser/ai/ai_data_keyed_service.h b/chrome/browser/ai/ai_data_keyed_service.h index 9141b7b..287bbdaa 100644 --- a/chrome/browser/ai/ai_data_keyed_service.h +++ b/chrome/browser/ai/ai_data_keyed_service.h
@@ -107,7 +107,6 @@ void ConvertToBrowserActionResult( base::OnceCallback<void(optimization_guide::proto::BrowserActionResult)> callback, - std::unique_ptr<glic::GlicPageContextFetcher> fetcher, int task_id, int tab_id, actor::mojom::ActionResultPtr action_result,
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkSaveFlowView.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkSaveFlowView.java index 17aca55a..6dfd417 100644 --- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkSaveFlowView.java +++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkSaveFlowView.java
@@ -14,11 +14,13 @@ import android.widget.TextView; import org.chromium.build.annotations.NullMarked; +import org.chromium.ui.base.LocalizationUtils; /** Controls the bookmarks save-flow view. */ @NullMarked public class ImprovedBookmarkSaveFlowView extends FrameLayout { private View mBookmarkContainer; + private ImageView mEditChevron; private ImageView mBookmarkImageView; private TextView mBookmarkTitleView; private TextView mBookmarkSubtitleView; @@ -35,6 +37,7 @@ super.onFinishInflate(); mBookmarkContainer = findViewById(R.id.bookmark_container); + mEditChevron = findViewById(R.id.edit_chev); mBookmarkImageView = findViewById(R.id.bookmark_image); mBookmarkTitleView = findViewById(R.id.bookmark_title); mBookmarkSubtitleView = findViewById(R.id.bookmark_subtitle); @@ -43,7 +46,7 @@ mBookmarkContainer.setBackgroundResource( R.drawable.improved_bookmark_save_flow_single_pane_background); - + mEditChevron.setScaleX(LocalizationUtils.isLayoutRtl() ? -1 : 1); mBookmarkTitleView.setTextAppearance(R.style.TextAppearance_TextMedium_Secondary); mBookmarkSubtitleView.setTextAppearance(R.style.TextAppearance_TextMedium_Secondary); }
diff --git a/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkSaveFlowRenderTest.java b/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkSaveFlowRenderTest.java index c8e9cdd..cea6dba 100644 --- a/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkSaveFlowRenderTest.java +++ b/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkSaveFlowRenderTest.java
@@ -34,6 +34,7 @@ import org.chromium.chrome.browser.bookmarks.ImprovedBookmarkSaveFlowProperties.FolderText; import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate; import org.chromium.chrome.test.util.ChromeRenderTestRule; +import org.chromium.ui.base.LocalizationUtils; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModelChangeProcessor; import org.chromium.ui.test.util.BlankUiTestActivity; @@ -220,4 +221,22 @@ }); mRenderTestRule.render(mContentView, "title_and_subtitle"); } + + @Test + @MediumTest + @Feature({"RenderTest"}) + public void testRtlFlipsChevron() throws IOException { + ThreadUtils.runOnUiThreadBlocking( + () -> { + LocalizationUtils.setRtlForTesting(true); + mModel.set( + ImprovedBookmarkSaveFlowProperties.TITLE, + BookmarkSaveFlowMediator.createHighlightedCharSequence( + mActivity, new FolderText("Saved in Mobile bookmarks", 9, 16))); + mModel.set(ImprovedBookmarkSaveFlowProperties.SUBTITLE, "On this device "); + }); + + mRenderTestRule.render(mContentView, "rtl_chevron"); + ThreadUtils.runOnUiThreadBlocking(() -> LocalizationUtils.setRtlForTesting(true)); + } }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 6f72b63..0b04c4e 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -61,7 +61,6 @@ #include "chrome/browser/browsing_topics/browsing_topics_service_factory.h" #include "chrome/browser/btm/btm_browser_signin_detector.h" #include "chrome/browser/btm/stateful_bounce_counter.h" -#include "chrome/browser/captive_portal/captive_portal_service_factory.h" #include "chrome/browser/child_process_host_flags.h" #include "chrome/browser/chrome_browser_main_extra_parts_nacl_deprecation.h" #include "chrome/browser/chrome_content_browser_client_binder_policies.h" @@ -93,7 +92,6 @@ #include "chrome/browser/language_detection/language_detection_model_service_factory.h" #include "chrome/browser/lifetime/browser_shutdown.h" #include "chrome/browser/loader/keep_alive_request_tracker.h" -#include "chrome/browser/lookalikes/lookalike_url_navigation_throttle.h" #include "chrome/browser/media/audio_service_util.h" #include "chrome/browser/media/prefs/capture_device_ranking.h" #include "chrome/browser/media/router/media_router_feature.h" @@ -117,7 +115,6 @@ #include "chrome/browser/performance_manager/public/chrome_browser_main_extra_parts_performance_manager.h" #include "chrome/browser/performance_manager/public/chrome_content_browser_client_performance_manager_part.h" #include "chrome/browser/performance_monitor/chrome_browser_main_extra_parts_performance_monitor.h" -#include "chrome/browser/plugins/pdf_iframe_navigation_throttle.h" #include "chrome/browser/plugins/plugin_utils.h" #include "chrome/browser/policy/policy_util.h" #include "chrome/browser/policy/profile_policy_connector.h" @@ -127,7 +124,6 @@ #include "chrome/browser/preloading/prefetch/no_state_prefetch/chrome_no_state_prefetch_contents_delegate.h" #include "chrome/browser/preloading/prefetch/no_state_prefetch/chrome_speculation_host_delegate.h" #include "chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_manager_factory.h" -#include "chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_navigation_throttle.h" #include "chrome/browser/preloading/prefetch/prefetch_service/chrome_prefetch_service_delegate.h" #include "chrome/browser/preloading/prefetch/search_prefetch/field_trial_settings.h" #include "chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_url_loader.h" @@ -162,14 +158,10 @@ #include "chrome/browser/speech/chrome_speech_recognition_manager_delegate.h" #include "chrome/browser/ssl/chrome_security_blocking_page_factory.h" #include "chrome/browser/ssl/chrome_security_state_tab_helper.h" -#include "chrome/browser/ssl/https_defaulted_callbacks.h" #include "chrome/browser/ssl/https_upgrades_interceptor.h" #include "chrome/browser/ssl/https_upgrades_navigation_throttle.h" #include "chrome/browser/ssl/sct_reporting_service.h" #include "chrome/browser/ssl/ssl_client_certificate_selector.h" -#include "chrome/browser/ssl/typed_navigation_upgrade_throttle.h" -#include "chrome/browser/supervised_user/classify_url_navigation_throttle.h" -#include "chrome/browser/supervised_user/supervised_user_google_auth_navigation_throttle.h" #include "chrome/browser/tab_group_sync/tab_group_sync_utils.h" #include "chrome/browser/task_manager/sampling/task_manager_impl.h" #include "chrome/browser/task_manager/task_manager_interface.h" @@ -179,16 +171,12 @@ #include "chrome/browser/translate/translate_service.h" #include "chrome/browser/ui/blocked_content/blocked_window_params.h" #include "chrome/browser/ui/blocked_content/chrome_popup_navigation_delegate.h" -#include "chrome/browser/ui/blocked_content/tab_under_navigation_throttle.h" #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_navigator_params.h" #include "chrome/browser/ui/chrome_select_file_policy.h" #include "chrome/browser/ui/color/chrome_color_id.h" #include "chrome/browser/ui/lens/lens_overlay_side_panel_navigation_throttle.h" #include "chrome/browser/ui/login/http_auth_coordinator.h" -#include "chrome/browser/ui/login/login_navigation_throttle.h" -#include "chrome/browser/ui/passwords/password_manager_navigation_throttle.h" -#include "chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.h" #include "chrome/browser/ui/prefs/pref_watcher.h" #include "chrome/browser/ui/tab_contents/chrome_web_contents_view_delegate.h" #include "chrome/browser/ui/ui_features.h" @@ -252,9 +240,6 @@ #include "components/error_page/common/error.h" #include "components/error_page/common/error_page_switches.h" #include "components/error_page/common/localized_error.h" -#include "components/error_page/content/browser/net_error_auto_reloader.h" -#include "components/fingerprinting_protection_filter/browser/throttle_manager.h" -#include "components/fingerprinting_protection_filter/common/fingerprinting_protection_filter_features.h" #include "components/google/core/common/google_switches.h" #include "components/heap_profiling/in_process/heap_profiler_controller.h" #include "components/history/content/browser/visited_link_navigation_throttle.h" @@ -276,14 +261,11 @@ #include "components/no_state_prefetch/browser/no_state_prefetch_manager.h" #include "components/no_state_prefetch/common/no_state_prefetch_final_status.h" #include "components/no_state_prefetch/common/no_state_prefetch_url_loader_throttle.h" -#include "components/omnibox/common/omnibox_features.h" #include "components/page_load_metrics/browser/metrics_web_contents_observer.h" -#include "components/payments/content/payment_handler_navigation_throttle.h" #include "components/payments/content/payment_request_display_manager.h" #include "components/payments/content/secure_payment_confirmation_service_factory.h" #include "components/pdf/common/pdf_util.h" #include "components/permissions/permission_context_base.h" -#include "components/policy/content/policy_blocklist_navigation_throttle.h" #include "components/policy/content/policy_blocklist_service.h" #include "components/policy/core/common/management/management_service.h" #include "components/policy/core/common/policy_pref_names.h" @@ -298,7 +280,6 @@ #include "components/safe_browsing/content/browser/async_check_tracker.h" #include "components/safe_browsing/content/browser/browser_url_loader_throttle.h" #include "components/safe_browsing/content/browser/password_protection/password_protection_commit_deferring_condition.h" -#include "components/safe_browsing/content/browser/ui_manager.h" #include "components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.h" #include "components/safe_browsing/core/browser/realtime/policy_engine.h" #include "components/safe_browsing/core/browser/realtime/url_lookup_service.h" @@ -308,9 +289,6 @@ #include "components/safe_browsing/core/common/hashprefix_realtime/hash_realtime_utils.h" #include "components/safe_browsing/core/common/safe_browsing_prefs.h" #include "components/search_engines/template_url_service.h" -#include "components/security_interstitials/content/insecure_form_navigation_throttle.h" -#include "components/security_interstitials/content/ssl_error_handler.h" -#include "components/security_interstitials/content/ssl_error_navigation_throttle.h" #include "components/security_state/core/security_state.h" #include "components/services/on_device_translation/buildflags/buildflags.h" #include "components/site_isolation/pref_names.h" @@ -452,7 +430,6 @@ #elif BUILDFLAG(IS_MAC) #include "chrome/browser/browser_process_platform_part_mac.h" #include "chrome/browser/chrome_browser_main_mac.h" -#include "chrome/browser/mac/auth_session_request.h" #include "chrome/browser/mac/chrome_browser_main_extra_parts_mac.h" #include "components/soda/constants.h" #include "sandbox/mac/sandbox_serializer.h" @@ -473,7 +450,6 @@ #include "chrome/app/chrome_crash_reporter_client.h" #include "chrome/browser/ash/arc/fileapi/arc_content_file_system_backend_delegate.h" #include "chrome/browser/ash/arc/fileapi/arc_documents_provider_backend_delegate.h" -#include "chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle.h" #include "chrome/browser/ash/drive/fileapi/drivefs_file_system_backend_delegate.h" #include "chrome/browser/ash/file_system_provider/fileapi/backend_delegate.h" #include "chrome/browser/ash/fileapi/external_file_url_loader_factory.h" @@ -487,7 +463,6 @@ #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/ash/smb_client/fileapi/smbfs_file_system_backend_delegate.h" #include "chrome/browser/ash/system/input_device_settings.h" -#include "chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h" #include "chrome/browser/speech/tts_chromeos.h" #include "chrome/browser/speech/tts_controller_delegate_impl.h" #include "chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.h" @@ -585,10 +560,7 @@ #include "chrome/browser/ui/browser_dialogs.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/chrome_pages.h" -#include "chrome/browser/ui/search/new_tab_page_navigation_throttle.h" #include "chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_navigation_throttle.h" -#include "chrome/browser/ui/web_applications/tabbed_web_app_navigation_throttle.h" -#include "chrome/browser/ui/web_applications/webui_web_app_navigation_throttle.h" #include "chrome/browser/ui/webui/chrome_content_browser_client_webui_part.h" #include "chrome/browser/ui/webui/webui_util_desktop.h" #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_error_page.h" @@ -608,10 +580,6 @@ #include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom.h" #endif // !BUILDFLAG(IS_ANDROID) -#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) -#include "chrome/browser/browser_switcher/browser_switcher_navigation_throttle.h" -#endif - #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) #include "components/crash/core/app/crash_switches.h" #include "components/crash/core/app/crashpad.h" @@ -623,18 +591,9 @@ #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) #include "chrome/browser/enterprise/chrome_browser_main_extra_parts_enterprise.h" -#include "chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle.h" -#include "chrome/browser/enterprise/profile_management/profile_management_navigation_throttle.h" -#include "chrome/browser/enterprise/signin/managed_profile_required_navigation_throttle.h" -#include "chrome/browser/enterprise/webstore/chrome_web_store_navigation_throttle.h" -#include "chrome/browser/enterprise/webstore/features.h" -#include "chrome/browser/ui/webui/app_settings/web_app_settings_navigation_throttle.h" #endif #if BUILDFLAG(ENABLE_EXTENSIONS_CORE) -#if !BUILDFLAG(IS_ANDROID) -#include "chrome/browser/enterprise/connectors/device_trust/navigation_throttle.h" -#endif // !BUILDFLAG(IS_ANDROID) #include "chrome/browser/enterprise/incognito/incognito_navigation_throttle.h" #endif // BUILDFLAG(ENABLE_EXTENSIONS_CORE) @@ -701,7 +660,6 @@ #if BUILDFLAG(ENABLE_PDF) #include "chrome/browser/pdf/chrome_pdf_stream_delegate.h" -#include "components/pdf/browser/pdf_navigation_throttle.h" #include "components/pdf/browser/pdf_url_loader_request_interceptor.h" #include "components/pdf/common/constants.h" #include "pdf/pdf_features.h" @@ -727,10 +685,8 @@ #include "chrome/browser/safe_browsing/chrome_password_protection_service.h" #include "chrome/browser/safe_browsing/chrome_ping_manager_factory.h" #include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h" -#include "chrome/browser/safe_browsing/delayed_warning_navigation_throttle.h" #include "chrome/browser/safe_browsing/safe_browsing_service.h" #include "chrome/browser/safe_browsing/url_lookup_service_factory.h" -#include "components/safe_browsing/content/browser/safe_browsing_navigation_throttle.h" #include "components/safe_browsing/core/browser/realtime/chrome_enterprise_url_lookup_service.h" #endif @@ -857,40 +813,6 @@ base::OnceClosure threads_ready_closure_; }; -// Wrapper for SSLErrorHandler::HandleSSLError() that supplies //chrome-level -// parameters. -void HandleSSLErrorWrapper( - content::WebContents* web_contents, - int cert_error, - const net::SSLInfo& ssl_info, - const GURL& request_url, - SSLErrorHandler::BlockingPageReadyCallback blocking_page_ready_callback) { - DCHECK(request_url.SchemeIsCryptographic()); - - Profile* profile = - Profile::FromBrowserContext(web_contents->GetBrowserContext()); - // Profile should always outlive a WebContents - DCHECK(profile); - - captive_portal::CaptivePortalService* captive_portal_service = nullptr; - -#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) - captive_portal_service = CaptivePortalServiceFactory::GetForProfile(profile); -#endif - - const bool is_ssl_error_override_allowed_for_origin = - policy::IsOriginInAllowlist(request_url, profile->GetPrefs(), - prefs::kSSLErrorOverrideAllowedForOrigins, - prefs::kSSLErrorOverrideAllowed); - - SSLErrorHandler::HandleSSLError( - web_contents, cert_error, ssl_info, request_url, - std::move(blocking_page_ready_callback), - g_browser_process->network_time_tracker(), captive_portal_service, - std::make_unique<ChromeSecurityBlockingPageFactory>(), - is_ssl_error_override_allowed_for_origin); -} - // Cached version of the locale so we can return the locale on the I/O // thread. std::string& GetIOThreadApplicationLocale() { @@ -1314,31 +1236,6 @@ } #endif -// Returns whether |web_contents| is within a hosted app. -bool IsInHostedApp(WebContents* web_contents) { -#if BUILDFLAG(ENABLE_EXTENSIONS) - Browser* browser = chrome::FindBrowserWithTab(web_contents); - return web_app::AppBrowserController::IsWebApp(browser); -#else - return false; -#endif -} - -bool IsErrorPageAutoReloadEnabled() { - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - if (command_line.HasSwitch(switches::kEnableAutomation)) { - return false; - } - if (command_line.HasSwitch(embedder_support::kEnableAutoReload)) { - return true; - } - if (command_line.HasSwitch(embedder_support::kDisableAutoReload)) { - return false; - } - return true; -} - #if BUILDFLAG(IS_CHROMEOS) void NotifyMultiCaptureStarted(const std::string& label, content::WebContents* web_contents, @@ -5385,171 +5282,6 @@ // TODO(https://crbug.com/412524375): Move the following code to // CreateAndAddChromeThrottlesForNavigation(). -#if BUILDFLAG(ENABLE_GUEST_VIEW) - registry.MaybeAddThrottle( - extensions::WebViewGuest::MaybeCreateNavigationThrottle(&handle)); -#endif - - registry.MaybeAddThrottle( - SupervisedUserGoogleAuthNavigationThrottle::MaybeCreate(&handle)); - - registry.MaybeAddThrottle( - supervised_user::MaybeCreateClassifyUrlNavigationThrottleFor(&handle)); - - if (auto* throttle_manager = - subresource_filter::ContentSubresourceFilterThrottleManager:: - FromNavigationHandle(handle)) { - throttle_manager->MaybeAppendNavigationThrottles(registry); - } - - if (fingerprinting_protection_filter::features:: - IsFingerprintingProtectionEnabledForIncognitoState( - profile ? profile->IsIncognitoProfile() : false)) { - if (auto* throttle_manager = fingerprinting_protection_filter:: - ThrottleManager::FromNavigationHandle(handle)) { - throttle_manager->MaybeAppendNavigationThrottles(registry); - } - } - - registry.MaybeAddThrottle( - LookalikeUrlNavigationThrottle::MaybeCreateNavigationThrottle(&handle)); - - registry.MaybeAddThrottle( - PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(&handle)); -#if BUILDFLAG(ENABLE_PDF) - registry.AddThrottle(std::make_unique<pdf::PdfNavigationThrottle>( - &handle, std::make_unique<ChromePdfStreamDelegate>())); -#endif // BUILDFLAG(ENABLE_PDF) - - registry.MaybeAddThrottle(TabUnderNavigationThrottle::MaybeCreate(&handle)); - - registry.MaybeAddThrottle( - WellKnownChangePasswordNavigationThrottle::MaybeCreateThrottleFor( - &handle)); - - registry.MaybeAddThrottle( - PasswordManagerNavigationThrottle::MaybeCreateThrottleFor(&handle)); - - registry.AddThrottle(std::make_unique<PolicyBlocklistNavigationThrottle>( - registry, handle.GetWebContents()->GetBrowserContext())); - - // Before setting up SSL error detection, configure SSLErrorHandler to invoke - // the relevant extension API whenever an SSL interstitial is shown. - SSLErrorHandler::SetClientCallbackOnInterstitialsShown( - base::BindRepeating(&MaybeTriggerSecurityInterstitialShownEvent)); - registry.AddThrottle(std::make_unique<SSLErrorNavigationThrottle>( - &handle, base::BindOnce(&HandleSSLErrorWrapper), - base::BindOnce(&IsInHostedApp), - base::BindOnce( - &ShouldIgnoreSslInterstitialBecauseNavigationDefaultedToHttps))); - - registry.AddThrottle(std::make_unique<LoginNavigationThrottle>(&handle)); - - if (base::FeatureList::IsEnabled(omnibox::kDefaultTypedNavigationsToHttps)) { - registry.MaybeAddThrottle( - TypedNavigationUpgradeThrottle::MaybeCreateThrottleFor(&handle)); - } - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) - registry.MaybeAddThrottle( - WebAppSettingsNavigationThrottle::MaybeCreateThrottleFor(&handle)); - registry.MaybeAddThrottle( - profile_management::ProfileManagementNavigationThrottle:: - MaybeCreateThrottleFor(&handle)); - registry.MaybeAddThrottle( - profile_management::OidcAuthResponseCaptureNavigationThrottle:: - MaybeCreateThrottleFor(&handle)); - registry.MaybeAddThrottle( - ManagedProfileRequiredNavigationThrottle::MaybeCreateThrottleFor( - &handle)); - - if (base::FeatureList::IsEnabled( - enterprise::webstore::kChromeWebStoreNavigationThrottle)) { - registry.AddThrottle( - std::make_unique<enterprise_webstore::ChromeWebStoreNavigationThrottle>( - &handle)); - } -#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \ - BUILDFLAG(IS_CHROMEOS) - registry.MaybeAddThrottle( - enterprise_connectors::DeviceTrustNavigationThrottle:: - MaybeCreateThrottleFor(&handle)); -#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || - // BUILDFLAG(IS_CHROMEOS) - -#if !BUILDFLAG(IS_ANDROID) - registry.MaybeAddThrottle( - DevToolsWindow::MaybeCreateNavigationThrottle(&handle)); - - registry.MaybeAddThrottle( - NewTabPageNavigationThrottle::MaybeCreateThrottleFor(&handle)); - - registry.MaybeAddThrottle( - web_app::TabbedWebAppNavigationThrottle::MaybeCreateThrottleFor(&handle)); - - registry.MaybeAddThrottle( - web_app::WebUIWebAppNavigationThrottle::MaybeCreateThrottleFor(&handle)); -#endif - -#if BUILDFLAG(SAFE_BROWSING_AVAILABLE) - // g_browser_process->safe_browsing_service() may be null in unittests. - safe_browsing::SafeBrowsingUIManager* ui_manager = - g_browser_process->safe_browsing_service() - ? g_browser_process->safe_browsing_service()->ui_manager().get() - : nullptr; - registry.MaybeAddThrottle( - safe_browsing::SafeBrowsingNavigationThrottle::MaybeCreateThrottleFor( - &handle, ui_manager)); - - if (base::FeatureList::IsEnabled(safe_browsing::kDelayedWarnings)) { - registry.AddThrottle( - std::make_unique<safe_browsing::DelayedWarningNavigationThrottle>( - &handle)); - } -#endif - -#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) - browser_switcher::BrowserSwitcherNavigationThrottle::MaybeCreateAndAdd( - registry); -#endif - -#if BUILDFLAG(IS_CHROMEOS) - registry.MaybeAddThrottle( - chromeos::KioskSettingsNavigationThrottle::MaybeCreateThrottleFor( - &handle)); - - registry.MaybeAddThrottle( - ash::OnTaskLockedSessionNavigationThrottle::MaybeCreateThrottleFor( - &handle)); -#endif - -#if BUILDFLAG(IS_MAC) - registry.MaybeAddThrottle(MaybeCreateAuthSessionThrottleFor(&handle)); -#endif - - if (profile && profile->GetPrefs()) { - registry.MaybeAddThrottle( - security_interstitials::InsecureFormNavigationThrottle:: - MaybeCreateNavigationThrottle( - &handle, std::make_unique<ChromeSecurityBlockingPageFactory>(), - profile->GetPrefs())); - } - - if (IsErrorPageAutoReloadEnabled()) { - registry.MaybeAddThrottle( - error_page::NetErrorAutoReloader::MaybeCreateThrottleFor(&handle)); - } - - registry.MaybeAddThrottle( - payments::PaymentHandlerNavigationThrottle::MaybeCreateThrottleFor( - &handle)); - - registry.MaybeAddThrottle( - prerender::NoStatePrefetchNavigationThrottle::MaybeCreateThrottleFor( - &handle)); - #if !BUILDFLAG(IS_ANDROID) registry.MaybeAddThrottle( ReadAnythingSidePanelNavigationThrottle::CreateFor(&handle));
diff --git a/chrome/browser/chrome_content_browser_client_navigation_throttles.cc b/chrome/browser/chrome_content_browser_client_navigation_throttles.cc index 07c231c84..ed54f03 100644 --- a/chrome/browser/chrome_content_browser_client_navigation_throttles.cc +++ b/chrome/browser/chrome_content_browser_client_navigation_throttles.cc
@@ -4,15 +4,52 @@ #include "chrome/browser/chrome_content_browser_client_navigation_throttles.h" +#include "base/command_line.h" +#include "base/feature_list.h" +#include "build/build_config.h" #include "build/buildflag.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/interstitials/enterprise_util.h" +#include "chrome/browser/lookalikes/lookalike_url_navigation_throttle.h" +#include "chrome/browser/plugins/pdf_iframe_navigation_throttle.h" +#include "chrome/browser/policy/policy_util.h" #include "chrome/browser/preloading/prefetch/no_state_prefetch/chrome_no_state_prefetch_contents_delegate.h" +#include "chrome/browser/preloading/prefetch/no_state_prefetch/no_state_prefetch_navigation_throttle.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ssl/chrome_security_blocking_page_factory.h" +#include "chrome/browser/ssl/https_defaulted_callbacks.h" +#include "chrome/browser/ssl/typed_navigation_upgrade_throttle.h" +#include "chrome/browser/supervised_user/classify_url_navigation_throttle.h" +#include "chrome/browser/supervised_user/supervised_user_google_auth_navigation_throttle.h" +#include "chrome/browser/ui/blocked_content/tab_under_navigation_throttle.h" +#include "chrome/browser/ui/login/login_navigation_throttle.h" +#include "chrome/browser/ui/passwords/password_manager_navigation_throttle.h" +#include "chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.h" #include "chrome/browser/ui/web_applications/navigation_capturing_redirection_throttle.h" +#include "chrome/common/pref_names.h" +#include "components/captive_portal/content/captive_portal_service.h" +#include "components/captive_portal/core/buildflags.h" +#include "components/embedder_support/switches.h" +#include "components/error_page/content/browser/net_error_auto_reloader.h" +#include "components/fingerprinting_protection_filter/browser/throttle_manager.h" +#include "components/fingerprinting_protection_filter/common/fingerprinting_protection_filter_features.h" +#include "components/guest_view/buildflags/buildflags.h" +#include "components/omnibox/common/omnibox_features.h" #include "components/page_load_metrics/browser/metrics_navigation_throttle.h" +#include "components/payments/content/payment_handler_navigation_throttle.h" +#include "components/policy/content/policy_blocklist_navigation_throttle.h" +#include "components/safe_browsing/buildflags.h" +#include "components/security_interstitials/content/insecure_form_navigation_throttle.h" +#include "components/security_interstitials/content/ssl_error_handler.h" +#include "components/security_interstitials/content/ssl_error_navigation_throttle.h" +#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/navigation_throttle_registry.h" #include "content/public/browser/web_contents.h" +#include "content/public/common/content_switches.h" #include "extensions/buildflags/buildflags.h" +#include "url/gurl.h" +#include "pdf/buildflags.h" #if BUILDFLAG(IS_ANDROID) #include "chrome/android/features/dev_ui/buildflags.h" @@ -26,14 +63,20 @@ #else // BUILDFLAG(IS_ANDROID) #include "chrome/browser/apps/link_capturing/link_capturing_navigation_throttle.h" #include "chrome/browser/apps/link_capturing/web_app_link_capturing_delegate.h" +#include "chrome/browser/devtools/devtools_window.h" +#include "chrome/browser/ui/search/new_tab_page_navigation_throttle.h" +#include "chrome/browser/ui/web_applications/tabbed_web_app_navigation_throttle.h" +#include "chrome/browser/ui/web_applications/webui_web_app_navigation_throttle.h" #endif // BUILDFLAG(IS_ANDROID) #if BUILDFLAG(IS_CHROMEOS) #include "chrome/browser/apps/intent_helper/chromeos_disabled_apps_throttle.h" #include "chrome/browser/apps/link_capturing/chromeos_link_capturing_delegate.h" #include "chrome/browser/apps/link_capturing/chromeos_reimpl_navigation_capturing_throttle.h" +#include "chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle.h" #include "chrome/browser/ash/login/signin/merge_session_navigation_throttle.h" #include "chrome/browser/ash/login/signin/merge_session_throttling_utils.h" +#include "chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h" #endif // BUILDFLAG(IS_CHROMEOS) #if BUILDFLAG(ENABLE_PLATFORM_APPS) @@ -43,10 +86,118 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) #include "chrome/browser/extensions/chrome_content_browser_client_extensions_part.h" #include "chrome/browser/extensions/user_script_listener.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/web_applications/app_browser_controller.h" #include "extensions/browser/extension_navigation_throttle.h" #include "extensions/browser/extensions_browser_client.h" #endif // BUILDFLAG(ENABLE_EXTENSIONS) +#if BUILDFLAG(ENABLE_GUEST_VIEW) +#include "extensions/browser/guest_view/web_view/web_view_guest.h" +#endif + +#if BUILDFLAG(ENABLE_PDF) +#include "chrome/browser/pdf/chrome_pdf_stream_delegate.h" +#include "components/pdf/browser/pdf_navigation_throttle.h" +#endif // BUILDFLAG(ENABLE_PDF) + +#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) +#include "chrome/browser/captive_portal/captive_portal_service_factory.h" +#endif // BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) +#include "chrome/browser/browser_switcher/browser_switcher_navigation_throttle.h" +#include "chrome/browser/enterprise/profile_management/oidc_auth_response_capture_navigation_throttle.h" +#include "chrome/browser/enterprise/profile_management/profile_management_navigation_throttle.h" +#include "chrome/browser/enterprise/signin/managed_profile_required_navigation_throttle.h" +#include "chrome/browser/enterprise/webstore/chrome_web_store_navigation_throttle.h" +#include "chrome/browser/enterprise/webstore/features.h" +#include "chrome/browser/ui/webui/app_settings/web_app_settings_navigation_throttle.h" +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) + + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \ + BUILDFLAG(IS_CHROMEOS) +#include "chrome/browser/enterprise/connectors/device_trust/navigation_throttle.h" +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || + // BUILDFLAG(IS_CHROMEOS) + +#if BUILDFLAG(SAFE_BROWSING_AVAILABLE) +#include "chrome/browser/safe_browsing/delayed_warning_navigation_throttle.h" +#include "chrome/browser/safe_browsing/safe_browsing_service.h" +#include "components/safe_browsing/content/browser/safe_browsing_navigation_throttle.h" +#include "components/safe_browsing/content/browser/ui_manager.h" +#include "components/safe_browsing/core/common/features.h" +#endif // BUILDFLAG(SAFE_BROWSING_AVAILABLE) + +#if BUILDFLAG(IS_MAC) +#include "chrome/browser/mac/auth_session_request.h" +#endif // BUILDFLAG(IS_MAC) + +namespace { + +// Wrapper for SSLErrorHandler::HandleSSLError() that supplies //chrome-level +// parameters. +void HandleSSLErrorWrapper( + content::WebContents* web_contents, + int cert_error, + const net::SSLInfo& ssl_info, + const GURL& request_url, + SSLErrorHandler::BlockingPageReadyCallback blocking_page_ready_callback) { + DCHECK(request_url.SchemeIsCryptographic()); + + Profile* profile = + Profile::FromBrowserContext(web_contents->GetBrowserContext()); + // Profile should always outlive a WebContents + DCHECK(profile); + + captive_portal::CaptivePortalService* captive_portal_service = nullptr; + +#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) + captive_portal_service = CaptivePortalServiceFactory::GetForProfile(profile); +#endif + + const bool is_ssl_error_override_allowed_for_origin = + policy::IsOriginInAllowlist(request_url, profile->GetPrefs(), + prefs::kSSLErrorOverrideAllowedForOrigins, + prefs::kSSLErrorOverrideAllowed); + + SSLErrorHandler::HandleSSLError( + web_contents, cert_error, ssl_info, request_url, + std::move(blocking_page_ready_callback), + g_browser_process->network_time_tracker(), captive_portal_service, + std::make_unique<ChromeSecurityBlockingPageFactory>(), + is_ssl_error_override_allowed_for_origin); +} + +// Returns whether `web_contents` is within a hosted app. +bool IsInHostedApp(content::WebContents* web_contents) { +#if BUILDFLAG(ENABLE_EXTENSIONS) + Browser* browser = chrome::FindBrowserWithTab(web_contents); + return web_app::AppBrowserController::IsWebApp(browser); +#else + return false; +#endif +} + +bool IsErrorPageAutoReloadEnabled() { + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch(switches::kEnableAutomation)) { + return false; + } + if (command_line.HasSwitch(embedder_support::kEnableAutoReload)) { + return true; + } + if (command_line.HasSwitch(embedder_support::kDisableAutoReload)) { + return false; + } + return true; +} + +} // namespace + + void CreateAndAddChromeThrottlesForNavigation( content::NavigationThrottleRegistry& registry) { content::NavigationHandle& handle = registry.GetNavigationHandle(); @@ -142,10 +293,10 @@ web_app::NavigationCapturingRedirectionThrottle::MaybeCreate(&handle)); #endif // !BUILDFLAG(IS_ANDROID) -#if BUILDFLAG(ENABLE_EXTENSIONS) Profile* profile = Profile::FromBrowserContext(handle.GetWebContents()->GetBrowserContext()); +#if BUILDFLAG(ENABLE_EXTENSIONS) if (!extensions::ChromeContentBrowserClientExtensionsPart:: AreExtensionsDisabledForProfile(profile)) { registry.AddThrottle( @@ -157,5 +308,171 @@ } #endif +#if BUILDFLAG(ENABLE_GUEST_VIEW) + registry.MaybeAddThrottle( + extensions::WebViewGuest::MaybeCreateNavigationThrottle(&handle)); +#endif + + registry.MaybeAddThrottle( + SupervisedUserGoogleAuthNavigationThrottle::MaybeCreate(&handle)); + + registry.MaybeAddThrottle( + supervised_user::MaybeCreateClassifyUrlNavigationThrottleFor(&handle)); + + if (auto* throttle_manager = + subresource_filter::ContentSubresourceFilterThrottleManager:: + FromNavigationHandle(handle)) { + throttle_manager->MaybeAppendNavigationThrottles(registry); + } + + if (fingerprinting_protection_filter::features:: + IsFingerprintingProtectionEnabledForIncognitoState( + profile ? profile->IsIncognitoProfile() : false)) { + if (auto* throttle_manager = fingerprinting_protection_filter:: + ThrottleManager::FromNavigationHandle(handle)) { + throttle_manager->MaybeAppendNavigationThrottles(registry); + } + } + + registry.MaybeAddThrottle( + LookalikeUrlNavigationThrottle::MaybeCreateNavigationThrottle(&handle)); + + registry.MaybeAddThrottle( + PDFIFrameNavigationThrottle::MaybeCreateThrottleFor(&handle)); + +#if BUILDFLAG(ENABLE_PDF) + registry.AddThrottle(std::make_unique<pdf::PdfNavigationThrottle>( + &handle, std::make_unique<ChromePdfStreamDelegate>())); +#endif // BUILDFLAG(ENABLE_PDF) + + registry.MaybeAddThrottle(TabUnderNavigationThrottle::MaybeCreate(&handle)); + + registry.MaybeAddThrottle( + WellKnownChangePasswordNavigationThrottle::MaybeCreateThrottleFor( + &handle)); + + registry.MaybeAddThrottle( + PasswordManagerNavigationThrottle::MaybeCreateThrottleFor(&handle)); + + registry.AddThrottle(std::make_unique<PolicyBlocklistNavigationThrottle>( + registry, handle.GetWebContents()->GetBrowserContext())); + + // Before setting up SSL error detection, configure SSLErrorHandler to invoke + // the relevant extension API whenever an SSL interstitial is shown. + SSLErrorHandler::SetClientCallbackOnInterstitialsShown( + base::BindRepeating(&MaybeTriggerSecurityInterstitialShownEvent)); + registry.AddThrottle(std::make_unique<SSLErrorNavigationThrottle>( + &handle, base::BindOnce(&HandleSSLErrorWrapper), + base::BindOnce(&IsInHostedApp), + base::BindOnce( + &ShouldIgnoreSslInterstitialBecauseNavigationDefaultedToHttps))); + + registry.AddThrottle(std::make_unique<LoginNavigationThrottle>(&handle)); + + if (base::FeatureList::IsEnabled(omnibox::kDefaultTypedNavigationsToHttps)) { + registry.MaybeAddThrottle( + TypedNavigationUpgradeThrottle::MaybeCreateThrottleFor(&handle)); + } + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) + registry.MaybeAddThrottle( + WebAppSettingsNavigationThrottle::MaybeCreateThrottleFor(&handle)); + registry.MaybeAddThrottle( + profile_management::ProfileManagementNavigationThrottle:: + MaybeCreateThrottleFor(&handle)); + registry.MaybeAddThrottle( + profile_management::OidcAuthResponseCaptureNavigationThrottle:: + MaybeCreateThrottleFor(&handle)); + registry.MaybeAddThrottle( + ManagedProfileRequiredNavigationThrottle::MaybeCreateThrottleFor( + &handle)); + + if (base::FeatureList::IsEnabled( + enterprise::webstore::kChromeWebStoreNavigationThrottle)) { + registry.AddThrottle( + std::make_unique<enterprise_webstore::ChromeWebStoreNavigationThrottle>( + &handle)); + } +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \ + BUILDFLAG(IS_CHROMEOS) + registry.MaybeAddThrottle( + enterprise_connectors::DeviceTrustNavigationThrottle:: + MaybeCreateThrottleFor(&handle)); +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || + // BUILDFLAG(IS_CHROMEOS) + +#if !BUILDFLAG(IS_ANDROID) + registry.MaybeAddThrottle( + DevToolsWindow::MaybeCreateNavigationThrottle(&handle)); + + registry.MaybeAddThrottle( + NewTabPageNavigationThrottle::MaybeCreateThrottleFor(&handle)); + + registry.MaybeAddThrottle( + web_app::TabbedWebAppNavigationThrottle::MaybeCreateThrottleFor(&handle)); + + registry.MaybeAddThrottle( + web_app::WebUIWebAppNavigationThrottle::MaybeCreateThrottleFor(&handle)); +#endif // !BUILDFLAG(IS_ANDROID) + +#if BUILDFLAG(SAFE_BROWSING_AVAILABLE) + // g_browser_process->safe_browsing_service() may be null in unittests. + safe_browsing::SafeBrowsingUIManager* ui_manager = + g_browser_process->safe_browsing_service() + ? g_browser_process->safe_browsing_service()->ui_manager().get() + : nullptr; + registry.MaybeAddThrottle( + safe_browsing::SafeBrowsingNavigationThrottle::MaybeCreateThrottleFor( + &handle, ui_manager)); + + if (base::FeatureList::IsEnabled(safe_browsing::kDelayedWarnings)) { + registry.AddThrottle( + std::make_unique<safe_browsing::DelayedWarningNavigationThrottle>( + &handle)); + } +#endif // BUILDFLAG(SAFE_BROWSING_AVAILABLE) + +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) + browser_switcher::BrowserSwitcherNavigationThrottle::MaybeCreateAndAdd( + registry); +#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) + +#if BUILDFLAG(IS_CHROMEOS) + registry.MaybeAddThrottle( + chromeos::KioskSettingsNavigationThrottle::MaybeCreateThrottleFor( + &handle)); + + registry.MaybeAddThrottle( + ash::OnTaskLockedSessionNavigationThrottle::MaybeCreateThrottleFor( + &handle)); +#endif + +#if BUILDFLAG(IS_MAC) + registry.MaybeAddThrottle(MaybeCreateAuthSessionThrottleFor(&handle)); +#endif + + if (profile && profile->GetPrefs()) { + registry.MaybeAddThrottle( + security_interstitials::InsecureFormNavigationThrottle:: + MaybeCreateNavigationThrottle( + &handle, std::make_unique<ChromeSecurityBlockingPageFactory>(), + profile->GetPrefs())); + } + + if (IsErrorPageAutoReloadEnabled()) { + registry.MaybeAddThrottle( + error_page::NetErrorAutoReloader::MaybeCreateThrottleFor(&handle)); + } + + registry.MaybeAddThrottle( + payments::PaymentHandlerNavigationThrottle::MaybeCreateThrottleFor( + &handle)); + + registry.MaybeAddThrottle( + prerender::NoStatePrefetchNavigationThrottle::MaybeCreateThrottleFor( + &handle)); + // Add new throttles here. }
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java index e23095e29..f6f6ce3 100644 --- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java +++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java
@@ -45,6 +45,7 @@ import org.chromium.components.browser_ui.share.ShareParams; import org.chromium.components.collaboration.CollaborationControllerDelegate; import org.chromium.components.collaboration.CollaborationService; +import org.chromium.components.collaboration.CollaborationServiceLeaveOrDeleteEntryPoint; import org.chromium.components.collaboration.CollaborationServiceShareOrManageEntryPoint; import org.chromium.components.collaboration.FlowType; import org.chromium.components.collaboration.Outcome; @@ -537,7 +538,6 @@ getTabGroupModelFilter().createSingleTabGroup(tab); } createOrManageFlow( - activity, EitherGroupId.createLocalId( new LocalTabGroupId(assumeNonNull(tab.getTabGroupId()))), entryPoint, @@ -547,13 +547,11 @@ /** * Creates or manage a collaboration group. * - * @param activity The activity in which the group is to be created. * @param eitherId The sync ID or local tab group ID of the tab group. * @param entry The entry point of the flow. * @param createGroupFinishedCallback Callback invoked when the creation flow is finished. */ public void createOrManageFlow( - Activity activity, EitherGroupId eitherId, @CollaborationServiceShareOrManageEntryPoint int entry, @Nullable Callback<Boolean> createGroupFinishedCallback) { @@ -570,6 +568,21 @@ } /** + * Leave or delete a collaboration group. + * + * @param eitherId The sync ID or local tab group ID of the tab group. + * @param entry The entry point of the flow. + */ + public void leaveOrDeleteFlow( + EitherGroupId eitherId, @CollaborationServiceLeaveOrDeleteEntryPoint int entry) { + mCurrentDelegate = + mCollaborationControllerDelegateFactory.create( + FlowType.LEAVE_OR_DELETE, /* switchToTabSwitcherCallback= */ null); + assumeNonNull(mCollaborationService); + mCollaborationService.startLeaveOrDeleteFlow(mCurrentDelegate, eitherId, entry); + } + + /** * Show the share dialog screen. * * @param activity The activity to show the UI for. @@ -895,7 +908,6 @@ Runnable manageSharingCallback = () -> createOrManageFlow( - activity, EitherGroupId.createSyncId(assumeNonNull(existingGroup.syncId)), CollaborationServiceShareOrManageEntryPoint.RECENT_ACTIVITY, /* createGroupFinishedCallback= */ null);
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManagerUnitTest.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManagerUnitTest.java index 98bfcc0..fe84be6 100644 --- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManagerUnitTest.java +++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManagerUnitTest.java
@@ -241,7 +241,7 @@ doReturn(mSavedTabGroup).when(mTabGroupSyncService).getGroup(LOCAL_ID); EitherGroupId either_id = EitherGroupId.createLocalId(LOCAL_ID); mDataSharingTabManager.createOrManageFlow( - mActivity, either_id, CollaborationServiceShareOrManageEntryPoint.UNKNOWN, null); + either_id, CollaborationServiceShareOrManageEntryPoint.UNKNOWN, null); verify(mCollaborationService).startShareOrManageFlow(any(), eq(either_id), anyInt()); }
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImpl.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImpl.java index baf58a47..98a04a4 100644 --- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImpl.java +++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImpl.java
@@ -369,7 +369,6 @@ if (mTabGroupSyncService.getGroup(syncId) == null) return; dataSharingTabManager.createOrManageFlow( - activity, EitherGroupId.createSyncId(syncId), CollaborationServiceShareOrManageEntryPoint.ANDROID_MESSAGE, /* createGroupFinishedCallback= */ null);
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImplUnitTest.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImplUnitTest.java index 2f8d23ad..ebd1cc9 100644 --- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImplUnitTest.java +++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImplUnitTest.java
@@ -314,7 +314,7 @@ Supplier<Integer> action = propertyModel.get(ON_PRIMARY_ACTION); assertNotNull(action); assertEquals(DISMISS_IMMEDIATELY, action.get().intValue()); - verify(mDataSharingTabManager).createOrManageFlow(any(), any(), anyInt(), any()); + verify(mDataSharingTabManager).createOrManageFlow(any(), anyInt(), any()); } @Test @@ -329,7 +329,7 @@ Supplier<Integer> action = propertyModel.get(ON_PRIMARY_ACTION); assertNotNull(action); assertEquals(DISMISS_IMMEDIATELY, action.get().intValue()); - verify(mDataSharingTabManager, never()).createOrManageFlow(any(), any(), anyInt(), any()); + verify(mDataSharingTabManager, never()).createOrManageFlow(any(), anyInt(), any()); } @Test
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc index bcd95d3..43f09064 100644 --- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc +++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
@@ -404,7 +404,7 @@ show_fail_closed_ui ? FinalContentAnalysisResult::FAIL_CLOSED : FinalContentAnalysisResult::SUCCESS; -#if BUILDFLAG(ENABLE_GLIC) +#if BUILDFLAG(IS_WIN) content::WebContents* top_web_contents = guest_view::GuestViewBase::GetTopLevelWebContents( web_contents->GetResponsibleWebContents());
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc index ba50a6c..ab04e8a 100644 --- a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc +++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.cc
@@ -13,9 +13,11 @@ #include "cc/paint/paint_flags.h" #include "chrome/browser/enterprise/connectors/analysis/content_analysis_features.h" #include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h" +#include "chrome/common/buildflags.h" #include "chrome/grit/generated_resources.h" #include "chrome/grit/theme_resources.h" #include "components/constrained_window/constrained_window_views.h" +#include "components/guest_view/browser/guest_view_base.h" #include "components/strings/grit/components_strings.h" #include "components/vector_icons/vector_icons.h" #include "components/web_modal/web_contents_modal_dialog_manager.h" @@ -53,6 +55,13 @@ #include "base/win/windows_h_disallowed.h" +#if BUILDFLAG(ENABLE_GLIC) +#include "base/metrics/histogram_functions.h" +#include "chrome/browser/glic/host/guest_util.h" +#include "chrome/browser/glic/widget/glic_widget.h" +#include "components/guest_view/browser/guest_view_base.h" +#endif + namespace enterprise_connectors { namespace { @@ -105,6 +114,25 @@ }; +gfx::Rect GetDialogBounds(content::WebContents* contents, + const gfx::Rect& current_widget_bounds) { + gfx::Rect rect = contents->GetContainerBounds(); + + // This will show the dialog right above the top of the contents. + rect.set_y(rect.y() - 40); +#if BUILDFLAG(ENABLE_GLIC) + if (glic::IsGlicWebUI(contents)) { + // This will show the dialog right below the "header" part of Glic. + rect.set_y(rect.y() + 80); + } +#endif // BUILDFLAG(ENABLE_GLIC) + + rect.set_x(rect.x() + (rect.width() / 2) - + (current_widget_bounds.width() / 2)); + + return rect; +} + ContentAnalysisDialog::TestObserver* observer_for_testing = nullptr; } // namespace @@ -246,10 +274,11 @@ download_item_->AddObserver(this); // Because the display of the dialog is delayed, it won't block UI - // interaction with the tab until it is visible. To block interaction as of - // now, ignore input events manually. + // interaction with the top level web contents until it is visible. To block + // interaction as of now, ignore input events manually. top_level_contents_ = constrained_window::GetTopLevelWebContents(web_contents())->GetWeakPtr(); + top_level_contents_->StoreFocus(); scoped_ignore_input_events_ = top_level_contents_->IgnoreInputEvents(std::nullopt); @@ -278,6 +307,22 @@ return; } +// Glic port enabled for Mac only at the moment until fixed on Windows. +// TODO(416748209): Follow up with full port of ContentAnalysisDialog to use +// non web modals on both Mac and Windows for all sources. +#if BUILDFLAG(IS_MAC) + if (glic::IsGlicWebUI(top_level_contents_.get())) { + // make sure only one dialog is displayed at a time. If a dialog exists we + // just update the view. + if (contents_view_) { + return; + } + ShowNonTabDialogNow(); + base::UmaHistogramEnumeration("Glic.Modal.DeepScan", access_point_); + return; + } +#endif + auto* manager = web_modal::WebContentsModalDialogManager::FromWebContents(web_contents()); if (!manager) { @@ -303,6 +348,23 @@ } } +void ContentAnalysisDialog::ShowNonTabDialogNow() { + content::WebContents* top_web_contents = + guest_view::GuestViewBase::GetTopLevelWebContents(web_contents()); + raw_ptr<views::Widget> dialog_widget = + views::DialogDelegate::CreateDialogWidget( + weak_ptr_factory_.GetWeakPtr().get(), gfx::NativeWindow(), + top_web_contents->GetNativeView()); + + dialog_widget->SetBounds(GetDialogBounds( + top_web_contents, dialog_widget->GetWindowBoundsInScreen())); + + dialog_widget->Show(); + if (observer_for_testing) { + observer_for_testing->ViewsFirstShown(this, first_shown_timestamp_); + } +} + std::u16string ContentAnalysisDialog::GetWindowTitle() const { return std::u16string(); }
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.h b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.h index 2b76ff3..58649d5d 100644 --- a/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.h +++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_dialog.h
@@ -213,6 +213,7 @@ // Callback function of delayed timer to make the dialog visible. void ShowDialogNow(); + void ShowNonTabDialogNow(); void UpdateStateFromFinalResult(FinalContentAnalysisResult final_result);
diff --git a/chrome/browser/extensions/service_worker_tracking_browsertest.cc b/chrome/browser/extensions/service_worker_tracking_browsertest.cc index 76aec9e9..a03c0c6 100644 --- a/chrome/browser/extensions/service_worker_tracking_browsertest.cc +++ b/chrome/browser/extensions/service_worker_tracking_browsertest.cc
@@ -482,10 +482,10 @@ // Test that if a browser stop notification is received before the render stop // notification (since these things can be triggered independently) the worker's -// browser readiness remains not ready. +// browser and renderer state are both set to not ready. IN_PROC_BROWSER_TEST_F( ServiceWorkerStopTrackingBrowserTest, - OnStoppedUpdatesBrowserState_BeforeRenderStopNotification) { + OnStoppedUpdatesBrowserAndRendererState_BeforeRenderStopNotification) { ASSERT_NO_FATAL_FAILURE(LoadServiceWorkerExtension()); // Get information about worker for extension that will be stopped soon. @@ -522,6 +522,8 @@ // notification reset it to no longer ready. EXPECT_EQ(worker_state->browser_state(), ServiceWorkerTaskQueue::BrowserState::kInitial); + EXPECT_EQ(worker_state->renderer_state(), + ServiceWorkerTaskQueue::RendererState::kNotActive); // Simulate the render stop notification arriving afterwards. task_queue->DidStopServiceWorkerContext( @@ -531,17 +533,19 @@ stopped_service_worker_id->version_id, stopped_service_worker_id->thread_id); - // Confirm the worker state still exists and browser state remains the same. + // Confirm the worker state still exists and state remains the same. EXPECT_EQ(worker_state->browser_state(), ServiceWorkerTaskQueue::BrowserState::kInitial); + EXPECT_EQ(worker_state->renderer_state(), + ServiceWorkerTaskQueue::RendererState::kNotActive); } // Test that if a browser stop notification is received after the render stop // notification (since these things can be triggered independently) -// it updates the worker's browser readiness information to not ready. +// the worker's browser and renderer readiness information remains not ready. IN_PROC_BROWSER_TEST_F( ServiceWorkerStopTrackingBrowserTest, - OnStoppedUpdatesBrowserState_AfterRenderStopNotification) { + OnStoppedUpdatesBrowserAndRendererState_AfterRenderStopNotification) { ASSERT_NO_FATAL_FAILURE(LoadServiceWorkerExtension()); // Get information about worker for extension that will be stopped soon. @@ -575,17 +579,23 @@ ASSERT_TRUE(content::CheckServiceWorkerIsStopped( sw_context, stopped_service_worker_id->version_id)); - // Confirm the worker state still exists and browser state is still ready. + // Confirm the worker state still exists and browser and renderer state are + // not ready. EXPECT_EQ(worker_state->browser_state(), - ServiceWorkerTaskQueue::BrowserState::kReady); + ServiceWorkerTaskQueue::BrowserState::kInitial); + EXPECT_EQ(worker_state->renderer_state(), + ServiceWorkerTaskQueue::RendererState::kNotActive); // Simulate browser stop notification after the render stop notification. ServiceWorkerTaskQueue::Get(profile())->OnStopped( stopped_service_worker_id->version_id, sw_info); - // Confirm the worker state still exists, but browser state is not ready. + // Confirm the worker state still exists, and browser and renderer state + // remain not ready. EXPECT_EQ(worker_state->browser_state(), ServiceWorkerTaskQueue::BrowserState::kInitial); + EXPECT_EQ(worker_state->renderer_state(), + ServiceWorkerTaskQueue::RendererState::kNotActive); } // Test that if a browser stop notification is received after a worker is @@ -634,9 +644,9 @@ // Test that if a renderer process exit notification is received before // a browser stop notification (since these things can be triggered // independently) and a context stop notification, it updates the worker's -// renderer active state to inactive. +// browser and renderer active state to inactive. IN_PROC_BROWSER_TEST_F(ServiceWorkerStopTrackingBrowserTest, - RenderProcessExitedUpdatesRendererState) { + RenderProcessExitedUpdatesBrowserAndRendererState) { ASSERT_NO_FATAL_FAILURE(LoadServiceWorkerExtension()); // Get information about worker for extension that will be stopped soon. @@ -679,8 +689,10 @@ ASSERT_TRUE( content::CheckServiceWorkerIsStopped(sw_context, worker_id->version_id)); - // Confirm the worker state still exists and renderer state has been set to - // inactive by `ServiceWorkerHost::RenderProcessForWorkerExited`. + // Confirm the worker state still exists and browser and renderer states have + // been set to inactive by `ServiceWorkerHost::RenderProcessForWorkerExited`. + EXPECT_EQ(worker_state->browser_state(), + ServiceWorkerTaskQueue::BrowserState::kInitial); EXPECT_EQ(worker_state->renderer_state(), ServiceWorkerTaskQueue::RendererState::kNotActive); }
diff --git a/chrome/browser/glic/glic_keyed_service.cc b/chrome/browser/glic/glic_keyed_service.cc index 34afb46..dccf0b3 100644 --- a/chrome/browser/glic/glic_keyed_service.cc +++ b/chrome/browser/glic/glic_keyed_service.cc
@@ -345,22 +345,8 @@ metrics_->DidRequestContextFromFocusedTab(); - auto fetcher = std::make_unique<glic::GlicPageContextFetcher>(); - fetcher->Fetch( - GetFocusedTabData(), options, - base::BindOnce( - // Bind `fetcher` to the callback to keep it in scope until it - // returns. - // TODO(harringtond): Consider adding throttling of how often we fetch - // context. - // TODO(harringtond): Consider deleting the fetcher if the page - // handler is unbound before the fetch completes. - [](std::unique_ptr<glic::GlicPageContextFetcher> fetcher, - mojom::WebClientHandler::GetContextFromFocusedTabCallback callback, - mojom::GetContextResultPtr result) { - std::move(callback).Run(std::move(result)); - }, - std::move(fetcher), std::move(callback))); + GlicPageContextFetcher::Fetch(GetFocusedTabData(), options, + std::move(callback)); } void GlicKeyedService::ActInFocusedTab(
diff --git a/chrome/browser/glic/host/context/glic_page_context_fetcher.cc b/chrome/browser/glic/host/context/glic_page_context_fetcher.cc index 704cc5bc..c529bf1c 100644 --- a/chrome/browser/glic/host/context/glic_page_context_fetcher.cc +++ b/chrome/browser/glic/host/context/glic_page_context_fetcher.cc
@@ -124,6 +124,26 @@ FocusedTabData focused_tab_data, const mojom::GetTabContextOptions& options, glic::mojom::WebClientHandler::GetContextFromFocusedTabCallback callback) { + CHECK(callback); + auto self = std::make_unique<GlicPageContextFetcher>(); + auto* raw_self = self.get(); + raw_self->FetchStart( + focused_tab_data, options, + base::BindOnce( + // Bind `fetcher` to the callback to keep it in scope until it + // returns. + [](std::unique_ptr<glic::GlicPageContextFetcher> fetcher, + mojom::WebClientHandler::GetContextFromFocusedTabCallback callback, + mojom::GetContextResultPtr result) { + std::move(callback).Run(std::move(result)); + }, + std::move(self), std::move(callback))); +} + +void GlicPageContextFetcher::FetchStart( + FocusedTabData focused_tab_data, + const mojom::GetTabContextOptions& options, + glic::mojom::WebClientHandler::GetContextFromFocusedTabCallback callback) { base::expected<content::WebContents*, std::string_view> focus = focused_tab_data.GetFocus(); if (!focus.has_value()) { @@ -203,6 +223,9 @@ base::BindOnce(&GlicPageContextFetcher::ReceivedContextEligibility, GetWeakPtr()))); + // Note: initialization_done_ guards against processing + // `RunCallbackIfComplete()` until we reach this point. + initialization_done_ = true; RunCallbackIfComplete(); } @@ -300,6 +323,10 @@ } void GlicPageContextFetcher::RunCallbackIfComplete() { + if (!initialization_done_) { + return; + } + // Continue only if the primary page changed or work is complete. bool work_complete = (screenshot_done_ && inner_text_done_ && annotated_page_content_done_ &&
diff --git a/chrome/browser/glic/host/context/glic_page_context_fetcher.h b/chrome/browser/glic/host/context/glic_page_context_fetcher.h index 0b22f02..3937b13 100644 --- a/chrome/browser/glic/host/context/glic_page_context_fetcher.h +++ b/chrome/browser/glic/host/context/glic_page_context_fetcher.h
@@ -32,10 +32,8 @@ GlicPageContextFetcher(); ~GlicPageContextFetcher() override; - // Fetches the page context. May be called at most once. - // TODO(harringtond): This API is error-prone, consider making this a static - // function so that Fetch() can't be called multiple times. - void Fetch( + // Fetches the page context. + static void Fetch( FocusedTabData focused_tab_data, const mojom::GetTabContextOptions& options, glic::mojom::WebClientHandler::GetContextFromFocusedTabCallback callback); @@ -44,6 +42,11 @@ void PrimaryPageChanged(content::Page& page) override; private: + void FetchStart( + FocusedTabData focused_tab_data, + const mojom::GetTabContextOptions& options, + glic::mojom::WebClientHandler::GetContextFromFocusedTabCallback callback); + void GetTabScreenshot(content::WebContents& web_contents); void ReceivedViewportBitmap(const SkBitmap& bitmap); void RecievedJpegScreenshot( @@ -69,6 +72,7 @@ // Intermediate results: // Whether work is complete for each task, does not imply success. + bool initialization_done_ = false; bool screenshot_done_ = false; bool inner_text_done_ = false; bool pdf_done_ = false;
diff --git a/chrome/browser/glic/host/glic_actor_controller.cc b/chrome/browser/glic/host/glic_actor_controller.cc index f8a8423..ae62d22 100644 --- a/chrome/browser/glic/host/glic_actor_controller.cc +++ b/chrome/browser/glic/host/glic_actor_controller.cc
@@ -169,18 +169,7 @@ // with GlicKeyedService::GetContextFromFocusedTab(). It's not clear yet if // the same permission checks, etc. should apply here. - auto fetcher = std::make_unique<glic::GlicPageContextFetcher>(); - fetcher->Fetch( - focused_tab_data, options, - base::BindOnce( - // Bind `fetcher` to the callback to keep it in scope until it - // returns. - [](std::unique_ptr<glic::GlicPageContextFetcher> fetcher, - mojom::WebClientHandler::GetContextFromFocusedTabCallback callback, - mojom::GetContextResultPtr result) { - std::move(callback).Run(std::move(result)); - }, - std::move(fetcher), std::move(callback))); + GlicPageContextFetcher::Fetch(focused_tab_data, options, std::move(callback)); } base::WeakPtr<const GlicActorController> GlicActorController::GetWeakPtr()
diff --git a/chrome/browser/glic/host/glic_annotation_manager_interactive_uitest.cc b/chrome/browser/glic/host/glic_annotation_manager_interactive_uitest.cc index c06d0acc..8c996fd 100644 --- a/chrome/browser/glic/host/glic_annotation_manager_interactive_uitest.cc +++ b/chrome/browser/glic/host/glic_annotation_manager_interactive_uitest.cc
@@ -145,12 +145,11 @@ ASSERT_TRUE(glic_service); base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); - auto fetcher = std::make_unique<GlicPageContextFetcher>(); auto options = mojom::GetTabContextOptions::New(); options->include_annotated_page_content = true; - fetcher->Fetch( + GlicPageContextFetcher::Fetch( glic_service->GetFocusedTabData(), *options, base::BindLambdaForTesting([&](mojom::GetContextResultPtr result) { mojo_base::ProtoWrapper& serialized_apc =
diff --git a/chrome/browser/resources/glic/glic.css b/chrome/browser/resources/glic/glic.css index 4eb5de7..0b30477 100644 --- a/chrome/browser/resources/glic/glic.css +++ b/chrome/browser/resources/glic/glic.css
@@ -21,6 +21,7 @@ body { overflow: hidden; + user-select: none; } webview:not([hidden]) {
diff --git a/chrome/browser/resources/pdf/ink2_manager.ts b/chrome/browser/resources/pdf/ink2_manager.ts index 4fcf73b..cd6d5fd6 100644 --- a/chrome/browser/resources/pdf/ink2_manager.ts +++ b/chrome/browser/resources/pdf/ink2_manager.ts
@@ -4,6 +4,7 @@ import {assert} from 'chrome://resources/js/assert.js'; import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js'; +import {isRTL} from 'chrome://resources/js/util.js'; import type {AnnotationBrush, Color, Point, TextAnnotation, TextAttributes, TextBoxRect, TextStyles} from './constants.js'; import {AnnotationBrushType, TextAlignment, TextStyle, TextTypeface} from './constants.js'; @@ -68,6 +69,22 @@ // No-op if there is no PDF page at `location`. initializeTextAnnotation(location: Point) { assert(this.viewport_); + // First check if the click was on a scrollbar. If so, ignore it to avoid + // interfering with scroll. + const hasScrollbars = this.viewport_.documentHasScrollbars(); + if (hasScrollbars.vertical && + (isRTL() && location.x <= this.viewport_.scrollbarWidth) || + (!isRTL() && + location.x >= + (this.viewport_.size.width - this.viewport_.scrollbarWidth))) { + return; + } + if (hasScrollbars.horizontal && + location.y >= + (this.viewport_.size.height - this.viewport_.scrollbarWidth)) { + return; + } + const page = this.viewport_.getPageAtPoint(location); if (page === -1) { return;
diff --git a/chrome/browser/resources/side_panel/read_anything/app.ts b/chrome/browser/resources/side_panel/read_anything/app.ts index a89c1f24..219332b 100644 --- a/chrome/browser/resources/side_panel/read_anything/app.ts +++ b/chrome/browser/resources/side_panel/read_anything/app.ts
@@ -150,10 +150,6 @@ private accessor imagesEnabled: boolean = false; - // If the node id of the first text node that should be used by Read Aloud - // has been set. This is null if the id has not been set. - firstTextNodeSetForReadAloud: number|null = null; - constructor() { super(); this.constructorTime = Date.now(); @@ -197,7 +193,6 @@ // not always reliabled called. this.speech_.cancel(); this.hasContent_ = false; - this.firstTextNodeSetForReadAloud = null; this.nodeStore_.clearDomNodes(); } @@ -432,10 +427,7 @@ // node id to call InitAXPosition in playSpeech. If it's not saved here, // we have to retrieve it through a DOM search such as createTreeWalker, // which can be computationally expensive. - if (!this.firstTextNodeSetForReadAloud) { - this.firstTextNodeSetForReadAloud = nodeId; - this.speechController_.initializeSpeechTree(nodeId); - } + this.speechController_.initializeSpeechTree(nodeId); const textContent = chrome.readingMode.getTextContent(nodeId); const textNode = document.createTextNode(textContent); @@ -485,10 +477,6 @@ // TODO: crbug.com/40927698 - Handle focus changes for speech, including // updating speech state. updateContent() { - // Each time we rebuild the subtree, we should clear the node id of the - // first text node. - this.firstTextNodeSetForReadAloud = null; - // This shouldn't happen. If it does, there is likely a bug, so log it so // we can monitor it. if (this.speechController_.isSpeechActive()) { @@ -786,16 +774,23 @@ } protected onPlayPauseClick_() { - if (this.speechController_.isSpeechActive()) { - this.speechController_.stopSpeech(PauseActionSource.BUTTON_CLICK); - } else { - this.playSpeech(); - this.speechController_.onPlay(); - } + this.speechController_.onPlayPauseToggle( + this.getSelection(), this.$.container.textContent); } onIsSpeechActiveChange(): void { this.isSpeechActive_ = this.speechController_.isSpeechActive(); + if (!chrome.readingMode.linksEnabled) { + return; + } + + // Restore links if they're enabled when speech pauses via button click or + // when it finishes the page. + const pauseSource = this.speechController_.getPauseSource(); + if ((pauseSource === PauseActionSource.BUTTON_CLICK) || + pauseSource === PauseActionSource.SPEECH_FINISHED) { + this.updateLinks_(); + } } onIsAudioCurrentlyPlayingChange(): void { @@ -815,20 +810,6 @@ this.resetSpeechPostSettingChange_(); } - onStop() { - if (!chrome.readingMode.linksEnabled) { - return; - } - - // Restore links if they're enabled when speech pauses via button click or - // when it finishes the page. - const pauseSource = this.speechController_.getPauseSource(); - if ((pauseSource === PauseActionSource.BUTTON_CLICK) || - pauseSource === PauseActionSource.SPEECH_FINISHED) { - this.updateLinks_(); - } - } - onEnabledLangsChange(): void { this.enabledLangs_ = this.voicePackController_.getEnabledLangs(); } @@ -854,83 +835,8 @@ playSpeech() { const container = this.$.container; - const {anchorNode, anchorOffset, focusNode, focusOffset} = - this.getSelection(); - const hasSelection = - anchorNode !== focusNode || anchorOffset !== focusOffset; - if (this.speechController_.hasSpeechBeenTriggered() && - !this.speechController_.isSpeechActive()) { - const pausedFromButton = this.speechController_.isPausedFromButton(); - - let playedFromSelection = false; - if (hasSelection) { - this.speech_.cancel(); - this.wordBoundaries_.resetToDefaultState(); - playedFromSelection = this.playFromSelection(); - } - - if (!playedFromSelection) { - if (pausedFromButton && !this.wordBoundaries_.hasBoundaries()) { - // If word boundaries aren't supported for the given voice, we should - // still continue to use synth.resume, as this is preferable to - // restarting the current message. - this.speech_.resume(); - } else { - this.speech_.cancel(); - if (!this.speechController_.highlightAndPlayInterruptedMessage()) { - // Ensure we're updating Read Aloud state if there's no text to - // speak. - this.speechController_.onSpeechFinished(); - } - } - } - - this.speechController_.setIsSpeechActive(true); - this.speechController_.setIsSpeechBeingRepositioned(false); - - // Hide links when speech resumes. We only hide links when the page was - // paused from the play/pause button. - if (chrome.readingMode.linksEnabled && pausedFromButton) { - // Toggle links and ensure that the new nodes are also highlighted. - this.updateLinks_( - /* shouldRehiglightCurrentNodes= */ !playedFromSelection); - } - - // If the current read highlight has been cleared from a call to - // updateContent, such as via a preference change, rehighlight the nodes - // after a pause. - if (!playedFromSelection) { - this.speechController_.highlightCurrentGranularity( - chrome.readingMode.getCurrentText()); - } - - return; - } - if (container.textContent) { - // Log that we're playing speech on a new page, but not when resuming. - // This helps us compare how many reading mode pages are opened with - // speech played and without speech played. Counting resumes would - // inflate the speech played number. - this.logger_.logNewPage(/*speechPlayed=*/ true); - this.speechController_.setIsSpeechActive(true); - this.speechController_.setHasSpeechBeenTriggered(true); - this.speechController_.setIsSpeechBeingRepositioned(false); - - // Hide links when speech begins playing. - if (chrome.readingMode.linksEnabled) { - this.updateLinks_(); - } - - const playedFromSelection = hasSelection && this.playFromSelection(); - if (!playedFromSelection && this.firstTextNodeSetForReadAloud) { - this.speechController_.initializeSpeechTree( - this.firstTextNodeSetForReadAloud); - if (!this.speechController_.highlightAndPlayMessage()) { - // Ensure we're updating Read Aloud state if there's no text to speak. - this.speechController_.onSpeechFinished(); - } - } - } + this.speechController_.playSpeech( + this.getSelection(), container.textContent); } private getSelectedIds(): { @@ -961,45 +867,6 @@ }; } - playFromSelection(): boolean { - const selection = this.getSelection(); - if (!this.firstTextNodeSetForReadAloud || !selection) { - return false; - } - - const anchorNodeId = chrome.readingMode.startNodeId; - const anchorOffset = chrome.readingMode.startOffset; - const focusNodeId = chrome.readingMode.endNodeId; - const focusOffset = chrome.readingMode.endOffset; - - // If only one of the ids is present, use that one. - let startingNodeId: number|undefined = - anchorNodeId ? anchorNodeId : focusNodeId; - let startingOffset = anchorNodeId ? anchorOffset : focusOffset; - // If both are present, start with the node that is sooner in the page. - if (anchorNodeId && focusNodeId) { - if (anchorNodeId === focusNodeId) { - startingOffset = Math.min(anchorOffset, focusOffset); - } else { - const pos = - selection.anchorNode.compareDocumentPosition(selection.focusNode); - const focusIsFirst = pos === Node.DOCUMENT_POSITION_PRECEDING; - startingNodeId = focusIsFirst ? focusNodeId : anchorNodeId; - startingOffset = focusIsFirst ? focusOffset : anchorOffset; - } - } - - if (!startingNodeId) { - return false; - } - - // Clear the selection so we don't keep trying to play from the same - // selection every time they press play. - selection.removeAllRanges(); - this.speechController_.playFromSelection(startingNodeId, startingOffset); - return true; - } - protected onSelectVoice_( event: CustomEvent<{selectedVoice: SpeechSynthesisVoice}>) { event.preventDefault();
diff --git a/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_controller.ts b/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_controller.ts index 5e8a18f..ab656e71 100644 --- a/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_controller.ts +++ b/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_controller.ts
@@ -22,7 +22,6 @@ export const MAX_SPEECH_LENGTH: number = 175; export interface SpeechListener { - onStop(): void; onIsSpeechActiveChange(): void; onIsAudioCurrentlyPlayingChange(): void; onEngineStateChange(): void; @@ -145,14 +144,19 @@ this.listeners_.forEach(l => l.onIsAudioCurrentlyPlayingChange()); } - initializeSpeechTree(startingNodeId: number|null) { - if (!startingNodeId || this.isSpeechTreeInitialized()) { + initializeSpeechTree(startingNodeId?: number) { + if (startingNodeId && !this.model_.getFirstTextNode()) { + this.model_.setFirstTextNode(startingNodeId); + } + + const firstTextNode = this.model_.getFirstTextNode(); + if (!firstTextNode || this.isSpeechTreeInitialized()) { return; } // TODO: crbug.com/40927698 - There should be a way to use AXPosition so // that this step can be skipped. - chrome.readingMode.initAxPositionWithNode(startingNodeId); + chrome.readingMode.initAxPositionWithNode(firstTextNode); this.model_.setIsSpeechTreeInitialized(true); chrome.readingMode.preprocessTextForSpeech(); } @@ -191,8 +195,21 @@ this.logger_.logHighlightGranularity(newGranularity); } - onPlay() { - this.model_.setPlaySessionStartTime(Date.now()); + onPlayPauseToggle(selection: Selection|null, textContent: string|null) { + if (this.isSpeechActive()) { + this.stopSpeech(PauseActionSource.BUTTON_CLICK); + } else { + this.playSpeech(selection, textContent); + this.model_.setPlaySessionStartTime(Date.now()); + } + } + + playSpeech(selection: Selection|null, textContent: string|null) { + if (this.hasSpeechBeenTriggered() && !this.isSpeechActive()) { + this.resumeSpeech_(selection); + } else { + this.playSpeechForTheFirstTime_(selection, textContent); + } } playNextGranularity() { @@ -228,7 +245,108 @@ } } - playFromSelection(startingNodeId: number, startingOffset: number) { + private resumeSpeech_(selection: Selection|null) { + let playedFromSelection = false; + if (this.hasSelection_(selection)) { + this.speech_.cancel(); + this.wordBoundaries_.resetToDefaultState(); + playedFromSelection = this.playFromSelection_(selection); + } + + if (!playedFromSelection) { + if (this.isPausedFromButton() && !this.wordBoundaries_.hasBoundaries()) { + // If word boundaries aren't supported for the given voice, we should + // still continue to use synth.resume, as this is preferable to + // restarting the current message. + this.speech_.resume(); + } else { + this.speech_.cancel(); + if (!this.highlightAndPlayInterruptedMessage_()) { + // Ensure we're updating Read Aloud state if there's no text to + // speak. + this.onSpeechFinished(); + } + } + } + + this.setIsSpeechActive(true); + this.setIsSpeechBeingRepositioned(false); + + // If the current read highlight has been cleared from a call to + // updateContent, such as via a preference change, rehighlight the nodes + // after a pause. + if (!playedFromSelection) { + this.highlightCurrentGranularity(chrome.readingMode.getCurrentText()); + } + } + + private playSpeechForTheFirstTime_( + selection: Selection|null, textContent: string|null) { + if (!textContent) { + return; + } + + // Log that we're playing speech on a new page, but not when resuming. + // This helps us compare how many reading mode pages are opened with + // speech played and without speech played. Counting resumes would + // inflate the speech played number. + this.logger_.logNewPage(/*speechPlayed=*/ true); + this.setIsSpeechActive(true); + this.setHasSpeechBeenTriggered(true); + this.setIsSpeechBeingRepositioned(false); + + const playedFromSelection = this.playFromSelection_(selection); + if (playedFromSelection) { + return; + } + + this.initializeSpeechTree(); + if (this.isSpeechTreeInitialized() && !this.highlightAndPlayMessage()) { + // Ensure we're updating Read Aloud state if there's no text to speak. + this.onSpeechFinished(); + } + } + + private hasSelection_(selection: Selection|null): boolean { + return !selection || selection.anchorNode !== selection.focusNode || + selection.anchorOffset !== selection.focusOffset; + } + + private playFromSelection_(selection: Selection|null): boolean { + if (!this.isSpeechTreeInitialized() || !selection || + !this.hasSelection_(selection)) { + return false; + } + + const anchorNodeId = chrome.readingMode.startNodeId; + const anchorOffset = chrome.readingMode.startOffset; + const focusNodeId = chrome.readingMode.endNodeId; + const focusOffset = chrome.readingMode.endOffset; + + // If only one of the ids is present, use that one. + let startingNodeId: number|undefined = + anchorNodeId ? anchorNodeId : focusNodeId; + let startingOffset = anchorNodeId ? anchorOffset : focusOffset; + // If both are present, start with the node that is sooner in the page. + if (anchorNodeId && focusNodeId) { + if (anchorNodeId === focusNodeId) { + startingOffset = Math.min(anchorOffset, focusOffset); + } else if (selection.anchorNode && selection.focusNode) { + const pos = + selection.anchorNode.compareDocumentPosition(selection.focusNode); + const focusIsFirst = pos === Node.DOCUMENT_POSITION_PRECEDING; + startingNodeId = focusIsFirst ? focusNodeId : anchorNodeId; + startingOffset = focusIsFirst ? focusOffset : anchorOffset; + } + } + + if (!startingNodeId) { + return false; + } + + // Clear the selection so we don't keep trying to play from the same + // selection every time they press play. + selection.removeAllRanges(); // Iterate through the page from the beginning until we get to the // selection. This is so clicking previous works before the selection and // so the previous highlights are properly set. @@ -244,9 +362,10 @@ this.onSpeechFinished(); } }, playFromSelectionTimeout); + return true; } - highlightAndPlayInterruptedMessage(): boolean { + private highlightAndPlayInterruptedMessage_(): boolean { return this.highlightAndPlayMessage(/* isInterrupted = */ true); } @@ -464,8 +583,6 @@ // Canceling clears all the Utterances that are queued up via synth.play() this.speech_.cancel(); } - - this.listeners_.forEach(l => l.onStop()); } setOnSpeechSynthesisUtteranceStart(message: SpeechSynthesisUtterance) { @@ -582,7 +699,6 @@ onSpeechFinished() { this.clearReadAloudState(); this.model_.setPauseSource(PauseActionSource.SPEECH_FINISHED); - this.listeners_.forEach(l => l.onStop()); this.logger_.logSpeechStopSource( chrome.readingMode.contentFinishedStopSource); this.logSpeechPlaySession_(); @@ -590,6 +706,7 @@ clearReadAloudState() { this.reset(); + this.model_.setFirstTextNode(null); this.highlighter_.clearHighlightFormatting(); this.wordBoundaries_.resetToDefaultState(); }
diff --git a/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_model.ts b/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_model.ts index 59f0769..fccc43e3 100644 --- a/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_model.ts +++ b/chrome/browser/resources/side_panel/read_anything/read_aloud/speech_model.ts
@@ -71,6 +71,10 @@ // Used for logging play time. private playSessionStartTime_: number|null = null; + // If the node id of the first text node that should be used by Read Aloud + // has been set. This is null if the id has not been set. + private firstTextNodeSetForReadAloud_: number|null = null; + reset(): void { this.speechPlayingState_ = { isSpeechTreeInitialized: false, @@ -84,6 +88,14 @@ this.previewVoicePlaying_ = null; } + getFirstTextNode(): number|null { + return this.firstTextNodeSetForReadAloud_; + } + + setFirstTextNode(node: number|null) { + this.firstTextNodeSetForReadAloud_ = node; + } + getPlaySessionStartTime(): number|null { return this.playSessionStartTime_; }
diff --git a/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java b/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java index d48fa99f..e30f578 100644 --- a/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java +++ b/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java
@@ -59,6 +59,8 @@ LayoutStateProvider.LayoutStateObserver, FullscreenManager.Observer { private static final String TAG = "E2E_ControllerImpl"; + private static final String DRAW_TO_EDGE_UNSUPPORTED_CONFIG_HISTOGRAM = + "Android.EdgeToEdge.DrawToEdgeInUnsupportedConfiguration"; private static final String SUPPORTED_CONFIGURATION_SWITCH_HISTOGRAM = "Android.EdgeToEdge.SupportedConfigurationSwitch"; @@ -414,6 +416,10 @@ */ @VisibleForTesting void drawToEdge(boolean pageOptedIntoEdgeToEdge, boolean changedWindowState) { + if (!EdgeToEdgeControllerFactory.isSupportedConfiguration(mActivity)) { + RecordHistogram.recordBooleanHistogram( + DRAW_TO_EDGE_UNSUPPORTED_CONFIG_HISTOGRAM, changedWindowState); + } // Exit early if there is a tappable navbar (3-button) as the controller should not function // when 3-button nav is enabled. if (EdgeToEdgeUtils.hasTappableNavigationBar(mActivity.getWindow())) {
diff --git a/chrome/browser/ui/android/omnibox/BUILD.gn b/chrome/browser/ui/android/omnibox/BUILD.gn index 54c9c58..245e534e 100644 --- a/chrome/browser/ui/android/omnibox/BUILD.gn +++ b/chrome/browser/ui/android/omnibox/BUILD.gn
@@ -82,6 +82,7 @@ "java/src/org/chromium/chrome/browser/omnibox/suggestions/PreWarmingRecycledViewPool.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/RecyclerViewSelectionController.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionController.java", + "java/src/org/chromium/chrome/browser/omnibox/suggestions/SimpleSelectionController.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionCommonProperties.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHorizontalDivider.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHost.java", @@ -426,7 +427,7 @@ "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownUnitTest.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/PreWarmingRecycledViewPoolTest.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/RecyclerViewSelectionControllerUnitTest.java", - "java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionControllerUnitTest.java", + "java/src/org/chromium/chrome/browser/omnibox/suggestions/SimpleSelectionControllerUnitTest.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHorizontalDividerTest.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinderUnitTest.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/UnsyncedSuggestionsListAnimationDriverTest.java",
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionController.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionController.java index 6be37e2..3a4e769 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionController.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionController.java
@@ -16,20 +16,9 @@ import java.lang.annotation.Target; import java.util.OptionalInt; -/** - * Helper class allowing advancing forward/backward while saturating outside the valid range. - * - * <p>TODO(344930378): Explore possibility to reconcile this with RecyclerViewSelectionController. - * The two classes serve similar purpose, but the complexity of view recycling may make the merge - * difficult. This controller expands RVSC capabilities, however the following aspects make - * immediate merge difficult: - volume of items changing at runtime, - exposure triggering (to - * ensure we can locate views for items not currently bound), - reused views propagate the selected - * state when rebound to a different item, - * - * <p>Consider adding WRAPPING and WRAPPING_WITH_SENTINEL variants to allow cycling through. - */ +/** Helper class allowing advancing forward/backward while saturating outside the valid range. */ @NullMarked -public class SelectionController { +public abstract class SelectionController { /** * Operational modes of the SelectionController * @@ -54,44 +43,17 @@ int SATURATING_WITH_SENTINEL = 1; } - private final OnSelectionChangedListener mListener; - private final @Mode int mMode; - private final int mDefaultPosition; - private int mNumItems; + protected final @Mode int mMode; + protected final int mDefaultPosition; + private int mPosition; - @FunctionalInterface - public interface OnSelectionChangedListener { - /** - * Invoked whenever selected state at specific position changed. - * - * @param position the position to apply selection change to - * @param isSelected whether that position should be selected - * @return whether selection was applied at requested position - */ - boolean onSelectionChanged(int position, boolean isSelected); - } - /** * SelectionController constructor. * - * @param listener the listener receiving notifications about selection changes - */ - public SelectionController(OnSelectionChangedListener listener, @Mode int mode) { - this(listener, 0, mode); - } - - /** - * SelectionController constructor. - * - * @param listener the listener receiving notifications about selection changes - * @param itemCount the number of valid positions [0; itemCount-1) * @param mode Selection mode that defines how the controller will behave */ - public SelectionController(OnSelectionChangedListener listener, int itemCount, @Mode int mode) { - assert itemCount < Integer.MAX_VALUE; - assert itemCount >= 0; - + public SelectionController(@Mode int mode) { switch (mode) { case Mode.SATURATING: mDefaultPosition = 0; @@ -103,29 +65,8 @@ break; } - // Initialization step only, to ensure we do not emit bogus selection change event. mPosition = Integer.MIN_VALUE; - mListener = listener; mMode = mode; - updateMaxPosition(itemCount); - } - - /** - * Update range of valid positions. - * - * @param numItems total number of items, determining the upper position. - */ - public void updateMaxPosition(int numItems) { - if (!isParkedAtSentinel()) { - mListener.onSelectionChanged(mPosition, false); - } - - mNumItems = numItems; - mPosition = mDefaultPosition; - - if (!isParkedAtSentinel()) { - mListener.onSelectionChanged(mPosition, true); - } } /** Resets the controller, making the current position point to default item. */ @@ -133,6 +74,9 @@ setPosition(mDefaultPosition); } + /** Returns the maximum valid position the SelectionController can assume. */ + protected abstract int getItemCount(); + /** * Advances the counter towards the maxPosition, returning false if the held value has * saturated. @@ -153,7 +97,7 @@ */ public boolean advanceBack() { if (mPosition == Integer.MIN_VALUE) return false; - if (mPosition == Integer.MAX_VALUE) return setPosition(mNumItems - 1); + if (mPosition == Integer.MAX_VALUE) return setPosition(getItemCount()); return setPosition(mPosition - 1); } @@ -177,17 +121,18 @@ @VisibleForTesting boolean setPosition(int newPosition) { if (!isParkedAtSentinel()) { - mListener.onSelectionChanged(mPosition, false); + setItemState(mPosition, false); } int oldPosition = mPosition; + int itemCount = getItemCount(); mPosition = newPosition; switch (mMode) { case Mode.SATURATING: - if (mNumItems == 0) { + if (itemCount == 0) { mPosition = Integer.MIN_VALUE; } else { - mPosition = MathUtils.clamp(mPosition, 0, mNumItems - 1); + mPosition = MathUtils.clamp(mPosition, 0, itemCount - 1); } break; @@ -195,7 +140,7 @@ // Park outside the valid range, keeping the information which edge we hit. if (mPosition < 0) { // Underflow mPosition = Integer.MIN_VALUE; - } else if (mPosition >= mNumItems) { + } else if (mPosition >= itemCount) { mPosition = Integer.MAX_VALUE; } break; @@ -204,13 +149,22 @@ if (isParkedAtSentinel()) return false; // Select new item, fall back to old position if not possible. - if (!mListener.onSelectionChanged(mPosition, true)) { + if (!setItemState(mPosition, true)) { mPosition = oldPosition; - mListener.onSelectionChanged(mPosition, true); + setItemState(mPosition, true); // We failed to select the requested entry. return false; } return true; } + + /** + * Applies selection change at specific position. + * + * @param position the index of an element to change the state of + * @param state the desired new state + * @return the applied state of the item at specified position. + */ + protected abstract boolean setItemState(int position, boolean isSelected); }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SimpleSelectionController.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SimpleSelectionController.java new file mode 100644 index 0000000..efd4f1d --- /dev/null +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SimpleSelectionController.java
@@ -0,0 +1,63 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.omnibox.suggestions; + +import org.chromium.build.annotations.NullMarked; + +/** + * Specialization of the SimpleSelectionController, that works with a fixed, known set of elements. + */ +@NullMarked +public class SimpleSelectionController extends SelectionController { + private OnSelectionChangedListener mListener; + private int mItemCount; + + @FunctionalInterface + public interface OnSelectionChangedListener { + /** + * Invoked whenever selected state at specific position changed. + * + * @param position the position to apply selection change to + * @param isSelected whether that position should be selected + * @return whether selection was applied at requested position + */ + boolean onSelectionChanged(int position, boolean isSelected); + } + + /** + * @param listener the listener receiving notifications about selection changes + * @param itemCount the number of items + * @param mode Selection mode that defines how the controller will behave + */ + public SimpleSelectionController( + OnSelectionChangedListener listener, int itemCount, @Mode int mode) { + super(mode); + + assert itemCount < Integer.MAX_VALUE; + assert itemCount >= 0; + + mListener = listener; + mItemCount = itemCount; + reset(); + } + + /** Returns the number of items in the container controlled by the SelectionController. */ + @Override + protected int getItemCount() { + return mItemCount; + } + + /** Update the number of items SelectionController can choose from. */ + public void setItemCount(int newItemCount) { + mItemCount = newItemCount; + // Reset position if out of range. + setPosition(getPosition().orElse(mDefaultPosition)); + } + + @Override + protected boolean setItemState(int position, boolean isSelected) { + return mListener.onSelectionChanged(position, isSelected); + } +}
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionControllerUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SimpleSelectionControllerUnitTest.java similarity index 69% rename from chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionControllerUnitTest.java rename to chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SimpleSelectionControllerUnitTest.java index e5d0379..d30f68a 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionControllerUnitTest.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SimpleSelectionControllerUnitTest.java
@@ -27,13 +27,13 @@ import java.util.OptionalInt; -/** Robolectric unit tests for {@link SelectionController}. */ +/** Robolectric unit tests for {@link SimpleSelectionController}. */ @RunWith(BaseRobolectricTestRunner.class) -public class SelectionControllerUnitTest { +public class SimpleSelectionControllerUnitTest { private static final int MAX_POSITION = 3; // Items 0‒2 inclusive. public @Rule MockitoRule mMockitoRule = MockitoJUnit.rule(); - private @Mock SelectionController.OnSelectionChangedListener mListener; + private @Mock SimpleSelectionController.OnSelectionChangedListener mListener; @Before public void setUp() { @@ -42,7 +42,7 @@ when(mListener.onSelectionChanged(anyInt(), eq(false))).thenReturn(true); } - private void verifyPositionReset(SelectionController c, int position) { + private void verifyPositionReset(SimpleSelectionController c, int position) { verify(mListener).onSelectionChanged(position, false); assertEquals(OptionalInt.empty(), c.getPosition()); assertTrue(c.isParkedAtSentinel()); @@ -63,7 +63,8 @@ @Test public void advanceForward_saturating() { - SelectionController c = new SelectionController(mListener, MAX_POSITION, Mode.SATURATING); + SimpleSelectionController c = + new SimpleSelectionController(mListener, MAX_POSITION, Mode.SATURATING); verifyPositionSet(c, 0); assertTrue(c.advanceForward()); @@ -81,8 +82,9 @@ @Test public void advanceForward_saturatingWithSentinel() { - SelectionController c = - new SelectionController(mListener, MAX_POSITION, Mode.SATURATING_WITH_SENTINEL); + SimpleSelectionController c = + new SimpleSelectionController( + mListener, MAX_POSITION, Mode.SATURATING_WITH_SENTINEL); assertTrue(c.isParkedAtSentinel()); assertTrue(c.advanceForward()); @@ -103,7 +105,8 @@ @Test public void advanceBack_saturating() { - SelectionController c = new SelectionController(mListener, MAX_POSITION, Mode.SATURATING); + SimpleSelectionController c = + new SimpleSelectionController(mListener, MAX_POSITION, Mode.SATURATING); c.setPosition(MAX_POSITION); verifyPositionChanged(c, 0, 2); @@ -122,8 +125,9 @@ @Test public void advanceBack_saturatingWithSentinel() { - SelectionController c = - new SelectionController(mListener, MAX_POSITION, Mode.SATURATING_WITH_SENTINEL); + SimpleSelectionController c = + new SimpleSelectionController( + mListener, MAX_POSITION, Mode.SATURATING_WITH_SENTINEL); c.setPosition(MAX_POSITION - 1); verifyPositionSet(c, 2); @@ -144,21 +148,21 @@ public void advanceForward_saturating_listenerReturnsFalse() { when(mListener.onSelectionChanged(1, true)).thenReturn(false); - SelectionController c = - new SelectionController( - mListener, MAX_POSITION, SelectionController.Mode.SATURATING); + SimpleSelectionController c = + new SimpleSelectionController(mListener, MAX_POSITION, Mode.SATURATING); verifyPositionSet(c, 0); assertFalse(c.advanceForward()); verifyPositionChanged(c, 0, 0); } @Test - public void updateMaxPosition() { - SelectionController c = new SelectionController(mListener, MAX_POSITION, Mode.SATURATING); + public void setItemCount() { + SimpleSelectionController c = + new SimpleSelectionController(mListener, MAX_POSITION, Mode.SATURATING); verifyPositionSet(c, 0); // Grow list of items - c.updateMaxPosition(5); + c.setItemCount(5); verifyPositionSet(c, 0); assertTrue(c.advanceForward()); // Should now reach index 4 without saturating @@ -171,13 +175,33 @@ verifyPositionChanged(c, 3, 4); // Shrink list of items - c.updateMaxPosition(2); - verifyPositionSet(c, 0); + c.setItemCount(2); + verifyPositionSet(c, 1); + } + + @Test + public void selectionControllerWithNoItems() { + SimpleSelectionController c = new SimpleSelectionController(mListener, 0, Mode.SATURATING); + // Normally, saturating controller should start at valid range, but this is an edge case. + assertTrue(c.isParkedAtSentinel()); + assertEquals(OptionalInt.empty(), c.getPosition()); + + // Simulate we now have an item. This should make the saturating controller immediately jump + // to the first valid item. + c.setItemCount(1); + assertFalse(c.isParkedAtSentinel()); + assertEquals(OptionalInt.of(0), c.getPosition()); + + // Simulate we lost all items. This should make the saturating controller revert to sentnel. + c.setItemCount(0); + assertTrue(c.isParkedAtSentinel()); + assertEquals(OptionalInt.empty(), c.getPosition()); } @Test public void reset_saturating() { - SelectionController c = new SelectionController(mListener, MAX_POSITION, Mode.SATURATING); + SimpleSelectionController c = + new SimpleSelectionController(mListener, MAX_POSITION, Mode.SATURATING); verifyPositionSet(c, 0); c.advanceForward(); // 1
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java index 832273a..50f6167 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java
@@ -20,7 +20,7 @@ import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.omnibox.R; -import org.chromium.chrome.browser.omnibox.suggestions.SelectionController; +import org.chromium.chrome.browser.omnibox.suggestions.SimpleSelectionController; import org.chromium.chrome.browser.util.KeyNavigationUtil; import org.chromium.components.browser_ui.widget.RoundedCornerOutlineProvider; @@ -42,7 +42,7 @@ public final ActionChipsView actionChipsView; public final RoundedCornerOutlineProvider decorationIconOutline; private final List<ImageView> mActionButtons; - private final SelectionController mActionButtonsHighlighter; + private final SimpleSelectionController mActionButtonsHighlighter; private Optional<Runnable> mOnFocusViaSelectionListener = Optional.empty(); /** @@ -87,10 +87,10 @@ addView(contentView); mActionButtonsHighlighter = - new SelectionController( + new SimpleSelectionController( this::highlightActionButton, 0, - SelectionController.Mode.SATURATING_WITH_SENTINEL); + SimpleSelectionController.Mode.SATURATING_WITH_SENTINEL); } /** @@ -107,7 +107,7 @@ decreaseActionButtonsCount(desiredViewCount); } - mActionButtonsHighlighter.updateMaxPosition(desiredViewCount); + mActionButtonsHighlighter.setItemCount(desiredViewCount); } /**
diff --git a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc index 941ff22..f521b90 100644 --- a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc +++ b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc
@@ -47,6 +47,7 @@ #include "components/autofill/core/browser/payments/otp_unmask_result.h" #include "components/autofill/core/browser/payments/payments_autofill_client.h" #include "components/autofill/core/browser/payments/payments_network_interface.h" +#include "components/autofill/core/browser/payments/save_and_fill_manager.h" #include "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h" #include "components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.h" #include "components/autofill/core/browser/suggestions/suggestion.h" @@ -964,6 +965,18 @@ #endif // !BUILDFLAG(IS_ANDROID) } +SaveAndFillManager* ChromePaymentsAutofillClient::GetSaveAndFillManager() { +#if BUILDFLAG(IS_ANDROID) + return nullptr; +#else + if (!save_and_fill_manager_) { + save_and_fill_manager_ = + std::make_unique<payments::SaveAndFillManager>(this); + } + return save_and_fill_manager_.get(); +#endif // BUILDFLAG(IS_ANDROID) +} + void ChromePaymentsAutofillClient::ShowSelectBnplIssuerDialog( std::vector<BnplIssuerContext> bnpl_issuer_context, std::string app_locale,
diff --git a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.h b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.h index fcc7ec3..97289113 100644 --- a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.h +++ b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.h
@@ -64,6 +64,7 @@ enum class OtpUnmaskResult; class PaymentsDataManager; class SaveAndFillDialogControllerImpl; +class SaveAndFillManager; class TouchToFillDelegate; struct VirtualCardEnrollmentFields; class VirtualCardEnrollmentManager; @@ -209,6 +210,7 @@ override; PaymentsDataManager& GetPaymentsDataManager() final; void ShowCreditCardSaveAndFillDialog() override; + payments::SaveAndFillManager* GetSaveAndFillManager() override; void ShowSelectBnplIssuerDialog( std::vector<BnplIssuerContext> bnpl_issuer_context, std::string app_locale, @@ -331,6 +333,8 @@ std::unique_ptr<SaveAndFillDialogControllerImpl> save_and_fill_dialog_controller_; + std::unique_ptr<SaveAndFillManager> save_and_fill_manager_; + std::unique_ptr<SelectBnplIssuerDialogControllerImpl> select_bnpl_issuer_dialog_controller_;
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.cc b/chrome/browser/ui/lens/lens_overlay_controller.cc index 3090b8af..7a37d26 100644 --- a/chrome/browser/ui/lens/lens_overlay_controller.cc +++ b/chrome/browser/ui/lens/lens_overlay_controller.cc
@@ -691,6 +691,10 @@ } void LensOverlayController::CopyImage(lens::mojom::CenterRotatedBoxPtr region) { + if (initialization_data_->initial_screenshot_.drawsNothing()) { + return; + } + SkBitmap cropped = lens::CropBitmapToRegion( initialization_data_->initial_screenshot_, std::move(region)); ui::ScopedClipboardWriter clipboard_writer(ui::ClipboardBuffer::kCopyPaste);
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index 3e97bf7c..0597ca4b 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -4346,12 +4346,15 @@ switch (tab_data.collaboration_messaging->collaboration_event()) { case collaboration::messaging::CollaborationEvent::TAB_ADDED: title = l10n_util::GetStringFUTF16( - IDS_DATA_SHARING_RECENT_ACTIVITY_MEMBER_ADDED_THIS_TAB, given_name); + IDS_DATA_SHARING_RECENT_ACTIVITY_MEMBER_ADDED_THIS_TAB, + given_name) + + u", " + title; break; case collaboration::messaging::CollaborationEvent::TAB_UPDATED: title = l10n_util::GetStringFUTF16( - IDS_DATA_SHARING_RECENT_ACTIVITY_MEMBER_CHANGED_THIS_TAB, - given_name); + IDS_DATA_SHARING_RECENT_ACTIVITY_MEMBER_CHANGED_THIS_TAB, + given_name) + + u", " + title; break; default: NOTREACHED();
diff --git a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_browsertest.cc b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_browsertest.cc index efa1d97..969da7d 100644 --- a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_browsertest.cc +++ b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_browsertest.cc
@@ -36,8 +36,6 @@ constexpr int kAverageBrowserHeight = 700; constexpr base::TimeDelta kMaxWaitTime = base::Seconds(30); -// TODO(crbug.com/371041180): Refactor tests to remove these scripts and notify -// from the notice components instead. std::string ScrollToBottomScript() { return R"```( (async () => { @@ -80,35 +78,6 @@ )```"; } -std::string WaitForPrivacyPolicyPageLoadScript() { - return R"( - (async () => { - return new Promise(async (resolve) => { - requestAnimationFrame(async () => { - dialogElement = document.querySelector("body > "+$1); - if($2 !== "") dialogElement = dialogElement.shadowRoot.querySelector($2); - waitForPrivacyPolicyResolve = (el) => new Promise(privacyPolicyResolve => { - let timeout = setTimeout(() => { - el.removeEventListener('privacy-policy-loaded', privacyPolicyLoadCallback); - privacyPolicyResolve(); - }, 2000); - const privacyPolicyLoadCallback = () => { - clearTimeout(timeout); - el.removeEventListener('privacy-policy-loaded', privacyPolicyLoadCallback); - privacyPolicyResolve(); - }; - el.addEventListener('privacy-policy-loaded', privacyPolicyLoadCallback); - }); - const privacyPolicyEl = dialogElement.shadowRoot.querySelector('privacy-sandbox-privacy-policy-dialog'); - privacyPolicyEl.shadowRoot.querySelector('#privacyPolicy').src = "about:blank"; - await waitForPrivacyPolicyResolve(privacyPolicyEl); - setTimeout(resolve,0); - }); - }); - })(); - )"; -} - std::string ClickLearnMoreButton3TimesScript() { return R"( (async () => { @@ -154,6 +123,10 @@ } void SetUpOnMainThread() override { + host_resolver()->AddRule("*", "127.0.0.1"); + embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data"); + + ASSERT_TRUE(embedded_test_server()->Start()); mock_service_ = static_cast<MockPrivacySandboxService*>( PrivacySandboxServiceFactory::GetInstance()->SetTestingFactoryAndUse( browser()->profile(), @@ -345,8 +318,7 @@ privacy_sandbox_dialog_view->GetWebContentsForTesting()), nullptr); - // Privacy policy is initially not visible, click expand button and trigger - // visibility. + // Click expand button. EXPECT_TRUE( content::ExecJs(privacy_sandbox_dialog_view->GetWebContentsForTesting(), "document.querySelector('body > " @@ -355,11 +327,6 @@ "privacy-sandbox-dialog-learn-more').shadowRoot." "querySelector('div > cr-expand-button').click()")); - EXPECT_TRUE(content::ExecJs( - privacy_sandbox_dialog_view->GetWebContentsForTesting(), - content::JsReplace(WaitForPrivacyPolicyPageLoadScript(), - "privacy-sandbox-combined-dialog-app", "#consent"))); - // Click Privacy Policy link. EXPECT_TRUE( content::ExecJs(privacy_sandbox_dialog_view->GetWebContentsForTesting(), @@ -370,6 +337,17 @@ GetPrivacyPolicyLinkElementId() + "')" ".click()")); + + // Intentionally navigate to some blocked content to avoid flakiness. + auto script = content::JsReplace( + "document.querySelector('body > " + "privacy-sandbox-combined-dialog-app')" + ".shadowRoot.querySelector('#consent')" + ".shadowRoot.querySelector('privacy-sandbox-privacy-policy-dialog')" + ".shadowRoot.querySelector('#privacyPolicy').src = $1;", + embedded_test_server()->GetURL("/blue.html")); + EXPECT_TRUE(content::ExecJs( + privacy_sandbox_dialog_view->GetWebContentsForTesting(), script)); } private: @@ -378,7 +356,7 @@ // TODO(https://crbug.com/415305952): High failure rate. IN_PROC_BROWSER_TEST_F(PrivacySandboxDialogViewPrivacyPolicyBrowserTest, - InvokeUi_PrivacyPolicy) { + DISABLED_InvokeUi_PrivacyPolicy) { ShowAndVerifyUi(); } @@ -404,7 +382,7 @@ // TODO(https://crbug.com/415305952): High failure rate. IN_PROC_BROWSER_TEST_F( PrivacySandboxDialogViewAdsApiUxEnhancementPrivacyPolicyBrowserTest, - InvokeUi_PrivacyPolicy) { + DISABLED_InvokeUi_PrivacyPolicy) { ShowAndVerifyUi(); }
diff --git a/chrome/browser/ui/views/storage/storage_pressure_bubble_view.cc b/chrome/browser/ui/views/storage/storage_pressure_bubble_view.cc index d88143d..6b9031a 100644 --- a/chrome/browser/ui/views/storage/storage_pressure_bubble_view.cc +++ b/chrome/browser/ui/views/storage/storage_pressure_bubble_view.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ui/views/storage/storage_pressure_bubble_view.h" +#include "base/auto_reset.h" #include "base/feature_list.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" @@ -80,18 +81,27 @@ } StoragePressureBubbleView::~StoragePressureBubbleView() { + CHECK(!in_accept_); if (ignored_) { RecordBubbleHistogramValue(StoragePressureBubbleHistogramValue::kIgnored); } } void StoragePressureBubbleView::OnDialogAccepted() { + base::AutoReset reset_in_accept(&in_accept_, true); + auto weak_this = weak_ptr_factory_.GetWeakPtr(); + ignored_ = false; RecordBubbleHistogramValue( StoragePressureBubbleHistogramValue::kOpenedAllSites); // TODO(ellyjones): What is this doing here? The widget's about to close // anyway? GetWidget()->Close(); + + CHECK(weak_this); + CHECK(browser_); + CHECK(browser_->profile()); + const GURL all_sites_gurl(kAllSitesContentSettingsUrl); NavigateParams params(browser_, all_sites_gurl, ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
diff --git a/chrome/browser/ui/views/storage/storage_pressure_bubble_view.h b/chrome/browser/ui/views/storage/storage_pressure_bubble_view.h index d439446..d17764c 100644 --- a/chrome/browser/ui/views/storage/storage_pressure_bubble_view.h +++ b/chrome/browser/ui/views/storage/storage_pressure_bubble_view.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_UI_VIEWS_STORAGE_STORAGE_PRESSURE_BUBBLE_VIEW_H_ #include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" #include "ui/base/metadata/metadata_header_macros.h" #include "ui/views/bubble/bubble_dialog_delegate_view.h" #include "url/origin.h" @@ -35,6 +36,12 @@ // Whether or not the user opened the all sites page from the notification // positive button. bool ignored_; + + // TODO(https://crbug.com/372479681): Remove these two members and all uses of + // them. They are here for debugging a crash we can't reproduce under + // controlled conditions. + bool in_accept_ = false; + base::WeakPtrFactory<StoragePressureBubbleView> weak_ptr_factory_{this}; }; #endif // CHROME_BROWSER_UI_VIEWS_STORAGE_STORAGE_PRESSURE_BUBBLE_VIEW_H_
diff --git a/chrome/browser/ui/views/tabs/alert_indicator_button.h b/chrome/browser/ui/views/tabs/alert_indicator_button.h index 7cae9b22..141497c 100644 --- a/chrome/browser/ui/views/tabs/alert_indicator_button.h +++ b/chrome/browser/ui/views/tabs/alert_indicator_button.h
@@ -88,7 +88,7 @@ gfx::ImageSkia GetImageToPaint() override; private: - friend class AlertIndicatorButtonTest; + friend class TabContentsTest; friend class TabTest; class FadeAnimationDelegate;
diff --git a/chrome/browser/ui/views/tabs/tab.h b/chrome/browser/ui/views/tabs/tab.h index 07d63054..6894d79 100644 --- a/chrome/browser/ui/views/tabs/tab.h +++ b/chrome/browser/ui/views/tabs/tab.h
@@ -203,7 +203,7 @@ private: class TabCloseButtonObserver; - friend class AlertIndicatorButtonTest; + friend class TabContentsTest; friend class TabTest; friend class TabStripTestBase; #if BUILDFLAG(IS_CHROMEOS) @@ -213,8 +213,8 @@ FRIEND_TEST_ALL_PREFIXES(TabTest, TitleTextHasSufficientContrast); FRIEND_TEST_ALL_PREFIXES(TabHoverCardInteractiveUiTest, HoverCardVisibleOnTabCloseButtonFocusAfterTabFocus); - FRIEND_TEST_ALL_PREFIXES(AlertIndicatorButtonTest, AccessibleNameChanged); - FRIEND_TEST_ALL_PREFIXES(AlertIndicatorButtonTest, + FRIEND_TEST_ALL_PREFIXES(TabContentsTest, AccessibleNameChanged); + FRIEND_TEST_ALL_PREFIXES(TabContentsTest, AccessibleNameChangesWithCollaborationMessages); bool ShouldUpdateAccessibleName(TabRendererData& old_data,
diff --git a/chrome/browser/ui/views/tabs/tab_unittest.cc b/chrome/browser/ui/views/tabs/tab_unittest.cc index 91a4753..bb8ca23a 100644 --- a/chrome/browser/ui/views/tabs/tab_unittest.cc +++ b/chrome/browser/ui/views/tabs/tab_unittest.cc
@@ -262,12 +262,12 @@ base::SimpleTestTickClock fake_clock_; }; -class AlertIndicatorButtonTest : public ChromeViewsTestBase { +class TabContentsTest : public ChromeViewsTestBase { public: - AlertIndicatorButtonTest() = default; - AlertIndicatorButtonTest(const AlertIndicatorButtonTest&) = delete; - AlertIndicatorButtonTest& operator=(const AlertIndicatorButtonTest&) = delete; - ~AlertIndicatorButtonTest() override = default; + TabContentsTest() = default; + TabContentsTest(const TabContentsTest&) = delete; + TabContentsTest& operator=(const TabContentsTest&) = delete; + ~TabContentsTest() override = default; void SetUp() override { ChromeViewsTestBase::SetUp(); @@ -768,7 +768,7 @@ // This test verifies that the tab has its icon state updated when the alert // animation fade-out finishes. -TEST_F(AlertIndicatorButtonTest, ShowsAndHidesAlertIndicator) { +TEST_F(TabContentsTest, ShowsAndHidesAlertIndicator) { controller_->AddTab(0, TabActive::kInactive, TabPinned::kPinned); controller_->AddTab(1, TabActive::kActive); Tab* media_tab = tab_strip_->tab_at(0); @@ -809,7 +809,7 @@ // This test verifies that the alert indicator for a camera and/or mic is // visible at least for 5 seconds even if a camera/mic stopped being used. -TEST_F(AlertIndicatorButtonTest, MinHoldDurationTest) { +TEST_F(TabContentsTest, MinHoldDurationTest) { base::test::ScopedFeatureList scoped_feature_list_; controller_->AddTab(0, TabActive::kActive); @@ -840,7 +840,7 @@ // This test verifies that the alert indicator for a camera and/or mic has // 1-second fadeout animation after it was visible for longer than 5 seconds. -TEST_F(AlertIndicatorButtonTest, 1SecondFadeoutAnimationTest) { +TEST_F(TabContentsTest, 1SecondFadeoutAnimationTest) { base::test::ScopedFeatureList scoped_feature_list_; controller_->AddTab(0, TabActive::kActive); @@ -908,7 +908,7 @@ EXPECT_EQ(ax::mojom::Role::kTab, data.role); } -TEST_F(AlertIndicatorButtonTest, AccessibleNameChanged) { +TEST_F(TabContentsTest, AccessibleNameChanged) { controller_->AddTab(0, TabActive::kInactive, TabPinned::kPinned); TabRendererData old_data = tab_strip_->tab_at(0)->data(); @@ -925,8 +925,7 @@ tab_strip_->tab_at(0)->ShouldUpdateAccessibleName(old_data, new_data)); } -TEST_F(AlertIndicatorButtonTest, - AccessibleNameChangesWithCollaborationMessages) { +TEST_F(TabContentsTest, AccessibleNameChangesWithCollaborationMessages) { TestingProfile profile; controller_->AddTab(0, TabActive::kInactive, TabPinned::kPinned);
diff --git a/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.cc b/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.cc index e213829..2dc647f 100644 --- a/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.cc +++ b/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.cc
@@ -171,8 +171,10 @@ LoadModelCallback callback, ml::ModelPerformanceHint performance_hint, on_device_model::ModelAssets assets) { + on_device_model::ModelFile weights = assets.weights; + auto params = on_device_model::mojom::LoadModelParams::New(); - params->assets = assets; + params->assets = std::move(assets); params->backend_type = optimization_guide::features::ForceCpuBackendForOnDeviceModel() ? ml::ModelBackendType::kCpuBackend @@ -183,19 +185,19 @@ std::move(params), std::move(model), base::BindOnce(&PageHandler::OnModelLoaded, weak_ptr_factory_.GetWeakPtr(), std::move(callback), - std::move(assets))); + std::move(weights))); } void PageHandler::OnModelLoaded( LoadModelCallback callback, - on_device_model::ModelAssets assets, + on_device_model::ModelFile weights, on_device_model::mojom::LoadModelResult result) { if (result != on_device_model::mojom::LoadModelResult::kSuccess) { std::move(callback).Run(result, on_device_model::Capabilities()); return; } GetService().GetCapabilities( - std::move(assets), + std::move(weights), base::BindOnce(std::move(callback), on_device_model::mojom::LoadModelResult::kSuccess)); }
diff --git a/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.h b/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.h index b2650de..de91fa0 100644 --- a/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.h +++ b/chrome/browser/ui/webui/on_device_internals/on_device_internals_page_handler.h
@@ -7,7 +7,8 @@ #include "chrome/browser/ui/webui/on_device_internals/on_device_internals_page.mojom.h" #include "components/optimization_guide/core/optimization_guide_logger.h" -#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "services/on_device_model/public/cpp/buildflags.h" #include "services/on_device_model/public/cpp/model_assets.h" @@ -46,7 +47,7 @@ ml::ModelPerformanceHint performance_hint, on_device_model::ModelAssets assets); void OnModelLoaded(LoadModelCallback callback, - on_device_model::ModelAssets assets, + on_device_model::ModelFile weights, on_device_model::mojom::LoadModelResult result); #endif
diff --git a/chrome/browser/visited_url_ranking/group_suggestions_service_factory_browsertest.cc b/chrome/browser/visited_url_ranking/group_suggestions_service_factory_browsertest.cc index 22d5ccf4..88dd42e 100644 --- a/chrome/browser/visited_url_ranking/group_suggestions_service_factory_browsertest.cc +++ b/chrome/browser/visited_url_ranking/group_suggestions_service_factory_browsertest.cc
@@ -103,7 +103,7 @@ base::test::ScopedFeatureList feature_list_; }; -IN_PROC_BROWSER_TEST_F(GroupSuggestionsServiceFactoryTest, SuggestionsShown) { +IN_PROC_BROWSER_TEST_F(GroupSuggestionsServiceFactoryTest, DISABLED_SuggestionsShown) { TestGroupSuggestionsDelegate delegate; GetService()->RegisterDelegate(&delegate, GroupSuggestionsService::Scope()); GetService()->SetConfigForTesting(base::TimeDelta());
diff --git a/chrome/browser/win/jumplist.h b/chrome/browser/win/jumplist.h index fce59ad1..76b753c9 100644 --- a/chrome/browser/win/jumplist.h +++ b/chrome/browser/win/jumplist.h
@@ -22,6 +22,7 @@ #include "base/timer/timer.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" #include "chrome/browser/win/jumplist_updater.h" +#include "components/favicon_base/favicon_types.h" #include "components/history/core/browser/history_types.h" #include "components/history/core/browser/top_sites_observer.h" #include "components/keyed_service/core/keyed_service.h"
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt index f5df6dd..d3789d2 100644 --- a/chrome/build/android-arm32.pgo.txt +++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@ -chrome-android32-main-1746813443-861fd45026df9940ca2609c2cd6eef7ab45dc762-4cac03e32f70f02cbf11d96b5da54e629616b531.profdata +chrome-android32-main-1746853952-1bd319a1e9a5519b40c220be6ac3c6775850864a-443628585681dafc55c69a328e9f421a1c78cd58.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt index 5ad0293..3905338 100644 --- a/chrome/build/android-arm64.pgo.txt +++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@ -chrome-android64-main-1746811595-150b94fdbfd212c2b14f2e8e65a20de7e3e095cd-7fe9a0e968b77a4cd55eecccb9e306b6cf4cce03.profdata +chrome-android64-main-1746856966-bbfd9ead94ad300041664043c74a8d0fd7003e09-fd3c9752f640f74e4e050f351a86d1d24a99d465.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index d92efeec..f20f5a9 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1746820698-d8dfff23f0e482dea11e74b214574a058d174d1b-b1f57359eeafa0a25b962e9e274fde4973a17d5e.profdata +chrome-mac-arm-main-1746853952-316cdb8201abcfb50caace1a04955bb0ec2f657f-443628585681dafc55c69a328e9f421a1c78cd58.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt index f7214d584..08d1e0cf 100644 --- a/chrome/build/win-arm64.pgo.txt +++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@ -chrome-win-arm64-main-1746813443-6d765e1c4190ba1c54e170988acb74137dc5dc28-4cac03e32f70f02cbf11d96b5da54e629616b531.profdata +chrome-win-arm64-main-1746834836-8f54c968cd61e3c72144519df91694e9f9b6e63e-3179e5ae5e5290f009d4ec21dba738788dc2aa8b.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index 2df4c3f..2680aa24 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1746791612-db6dc6c307b34f3247a88fc82beeaadae347d690-4852d4ffeefa31265675175f00edcf84c5e30ecd.profdata +chrome-win32-main-1746834836-e21ef60066ec33fd83f124ab95a09c9de7f0bced-3179e5ae5e5290f009d4ec21dba738788dc2aa8b.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index ffec439e..c5994bb0 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1746791612-8dda5f0a63e87ebb38f287e78a6ee776d8bb236b-4852d4ffeefa31265675175f00edcf84c5e30ecd.profdata +chrome-win64-main-1746813443-d0177f3ef39a67571bea32758731e6d8f238d388-4cac03e32f70f02cbf11d96b5da54e629616b531.profdata
diff --git a/chrome/test/data/pdf/ink2_manager_test.ts b/chrome/test/data/pdf/ink2_manager_test.ts index 980beb6..8753340 100644 --- a/chrome/test/data/pdf/ink2_manager_test.ts +++ b/chrome/test/data/pdf/ink2_manager_test.ts
@@ -175,6 +175,37 @@ chrome.test.succeed(); }, + async function testNoInitializeOnScrollbar() { + let initEvents = 0; + manager.addEventListener('initialize-text-box', () => { + initEvents++; + }); + + // Zoom in to 2x so that there are scrollbars in both x and y. + let whenViewportChanged = eventToPromise('viewport-changed', manager); + viewport.setZoom(2.0); + await whenViewportChanged; + + // Confirm both scrollbars and mock viewport dimensions. + chrome.test.assertTrue(viewport.documentHasScrollbars().vertical); + chrome.test.assertTrue(viewport.documentHasScrollbars().horizontal); + chrome.test.assertEq(100, viewport.size.width); + chrome.test.assertEq(100, viewport.size.height); + chrome.test.assertFalse(viewport.scrollbarWidth === 0); + + const edge = 100 - viewport.scrollbarWidth; + Ink2Manager.getInstance().initializeTextAnnotation({x: edge, y: 20}); + Ink2Manager.getInstance().initializeTextAnnotation({x: 20, y: edge}); + chrome.test.assertEq(0, initEvents); + + // Reset the zoom for the next test. + whenViewportChanged = eventToPromise('viewport-changed', manager); + viewport.setZoom(1.0); + await whenViewportChanged; + + chrome.test.succeed(); + }, + async function testInitializeTextBox() { // Add listeners for the expected events that fire in response to an // initializeTextAnnotation call.
diff --git a/chrome/test/data/pdf/ink2_test.ts b/chrome/test/data/pdf/ink2_test.ts index 88c77fed..899494a 100644 --- a/chrome/test/data/pdf/ink2_test.ts +++ b/chrome/test/data/pdf/ink2_test.ts
@@ -233,7 +233,7 @@ PluginController.getInstance().getEventTarget().dispatchEvent( new CustomEvent( PluginControllerEventType.PLUGIN_MESSAGE, - {detail: {type: 'sendClickEvent', x: 400, y: 300}})); + {detail: {type: 'sendClickEvent', x: 50, y: 50}})); await microtasksFinished(); chrome.test.assertTrue(isVisible(textbox));
diff --git a/chrome/test/data/pdf/test_util.ts b/chrome/test/data/pdf/test_util.ts index 0556170..f7cd706 100644 --- a/chrome/test/data/pdf/test_util.ts +++ b/chrome/test/data/pdf/test_util.ts
@@ -549,7 +549,7 @@ const mockSizer = new MockSizer(); const viewport = new Viewport( mockWindow as unknown as HTMLElement, mockSizer as unknown as HTMLElement, - dummyContent, /*scrollbarWidth=*/ 0, /*defaultZoom=*/ 1); + dummyContent, /*scrollbarWidth=*/ 5, /*defaultZoom=*/ 1); viewport.setZoomFactorRange([0.25, 0.4, 0.5, 1, 2]); const documentDimensions = new MockDocumentDimensions(0, 0); documentDimensions.addPage(90, 90);
diff --git a/chrome/test/data/webui/side_panel/read_anything/speech_controller_test.ts b/chrome/test/data/webui/side_panel/read_anything/speech_controller_test.ts index b9c686a..b2223ca1 100644 --- a/chrome/test/data/webui/side_panel/read_anything/speech_controller_test.ts +++ b/chrome/test/data/webui/side_panel/read_anything/speech_controller_test.ts
@@ -13,7 +13,6 @@ suite('SpeechController', () => { let speech: TestSpeechBrowserProxy; let speechController: SpeechController; - let onStop: boolean; let isSpeechActiveChanged: boolean; let isAudioCurrentlyPlayingChanged: boolean; let onPreviewVoicePlaying: boolean; @@ -31,14 +30,9 @@ SpeechBrowserProxyImpl.setInstance(speech); isSpeechActiveChanged = false; isAudioCurrentlyPlayingChanged = false; - onStop = false; onPreviewVoicePlaying = false; onEngineStateChange = false; const speechListener = { - onStop() { - onStop = true; - }, - onIsSpeechActiveChange() { isSpeechActiveChanged = true; }, @@ -80,7 +74,6 @@ assertTrue(isSpeechActiveChanged); assertTrue(isAudioCurrentlyPlayingChanged); - assertFalse(onStop); assertNotEquals(state, speechController.getState()); assertTrue(speechController.isSpeechActive()); assertTrue(speechController.isSpeechTreeInitialized()); @@ -106,7 +99,6 @@ assertTrue(isSpeechActiveChanged); assertTrue(isAudioCurrentlyPlayingChanged); - assertFalse(onStop); assertFalse(speechController.isSpeechActive()); assertFalse(speechController.isSpeechTreeInitialized()); assertEquals(PauseActionSource.DEFAULT, speechController.getPauseSource()); @@ -267,7 +259,7 @@ }); test('with no node id does nothing', () => { - speechController.initializeSpeechTree(null); + speechController.initializeSpeechTree(); assertFalse(!!initAxPositionWithNode); assertFalse(startedPreprocess); @@ -303,7 +295,7 @@ speechController.stopSpeech(source); - assertTrue(onStop); + assertTrue(isSpeechActiveChanged); assertFalse(speechController.isSpeechActive()); assertFalse(speechController.isAudioCurrentlyPlaying()); assertEquals(source, speechController.getPauseSource()); @@ -313,8 +305,7 @@ test('stopSpeech with button click logs play session', () => { const source = PauseActionSource.BUTTON_CLICK; - speechController.onPlay(); - speechController.setIsSpeechActive(true); + speechController.onPlayPauseToggle(null, 'New phone who dis?'); speechController.stopSpeech(source); assertEquals(1, metrics.getCallCount('recordSpeechPlaybackLength')); @@ -331,7 +322,7 @@ speechController.stopSpeech(source); - assertTrue(onStop); + assertTrue(isSpeechActiveChanged); assertFalse(speechController.isSpeechActive()); assertFalse(speechController.isAudioCurrentlyPlaying()); assertEquals(source, speechController.getPauseSource()); @@ -347,7 +338,6 @@ speechController.onSpeechInterrupted(); - assertFalse(onStop); assertTrue(speechController.isAudioCurrentlyPlaying()); assertTrue(speechController.isSpeechActive()); assertTrue(speechController.isSpeechBeingRepositioned()); @@ -359,7 +349,6 @@ speechController.onSpeechInterrupted(); - assertTrue(onStop); assertEquals( PauseActionSource.ENGINE_INTERRUPT, speechController.getPauseSource()); assertFalse(speechController.isAudioCurrentlyPlaying()); @@ -370,12 +359,11 @@ await metrics.whenCalled('recordSpeechStopSource')); }); test('onSpeechFinished', () => { - speechController.onPlay(); - speechController.setIsSpeechActive(true); + speechController.onPlayPauseToggle(null, 'New phone who dis?'); speechController.onSpeechFinished(); - assertTrue(onStop); + assertTrue(isSpeechActiveChanged); assertEquals(1, metrics.getCallCount('recordSpeechPlaybackLength')); assertEquals(1, metrics.getCallCount('recordSpeechStopSource')); assertFalse(speechController.isSpeechActive());
diff --git a/chrome/test/data/webui/side_panel/read_anything/word_highlighting_test.ts b/chrome/test/data/webui/side_panel/read_anything/word_highlighting_test.ts index 5ea223a..8369c1f1 100644 --- a/chrome/test/data/webui/side_panel/read_anything/word_highlighting_test.ts +++ b/chrome/test/data/webui/side_panel/read_anything/word_highlighting_test.ts
@@ -281,7 +281,7 @@ // Update the selection directly on the document. const spans = app.$.container.querySelectorAll('span'); - assertEquals(spans.length, 3); + assertEquals(4, spans.length); const anchor = spans[anchorIndex]!; const focus = spans[focusIndex]!; const range = document.createRange();
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn index 9939921..11caa7e 100644 --- a/chromecast/BUILD.gn +++ b/chromecast/BUILD.gn
@@ -728,8 +728,11 @@ "android/*", "androidx/*", "com/google/android/gms/*", + "com/google/androidxr/*", + "com/google/ar/*", "com/google/firebase/*", "com/google/protobuf/*", + "com/google/vr/*", "java/*", "kotlin/*", "kotlinx/*",
diff --git a/chromecast/media/common/media_pipeline_backend_manager.cc b/chromecast/media/common/media_pipeline_backend_manager.cc index 312be939..b7132db 100644 --- a/chromecast/media/common/media_pipeline_backend_manager.cc +++ b/chromecast/media/common/media_pipeline_backend_manager.cc
@@ -87,8 +87,8 @@ std::unique_ptr<CmaBackend> MediaPipelineBackendManager::CreateBackend( const media::MediaPipelineDeviceParams& params) { DCHECK(media_task_runner_->BelongsToCurrentThread()); - return std::make_unique<MediaPipelineBackendWrapper>(params, this, - media_resource_tracker_); + return std::make_unique<MediaPipelineBackendWrapper>( + params, weak_factory_.GetWeakPtr(), media_resource_tracker_); } scoped_refptr<base::SequencedTaskRunner>
diff --git a/chromecast/media/common/media_pipeline_backend_wrapper.cc b/chromecast/media/common/media_pipeline_backend_wrapper.cc index 22a1553..5c3535d 100644 --- a/chromecast/media/common/media_pipeline_backend_wrapper.cc +++ b/chromecast/media/common/media_pipeline_backend_wrapper.cc
@@ -89,7 +89,7 @@ ActiveMediaPipelineBackendWrapper( const media::MediaPipelineDeviceParams& params, MediaPipelineBackendWrapper* wrapping_backend, - MediaPipelineBackendManager* backend_manager, + base::WeakPtr<MediaPipelineBackendManager> backend_manager, MediaResourceTracker* media_resource_tracker); ActiveMediaPipelineBackendWrapper(const ActiveMediaPipelineBackendWrapper&) = @@ -129,7 +129,7 @@ bool video_decoder_created_; const std::unique_ptr<MediaPipelineBackend> backend_; MediaPipelineBackendWrapper* const wrapping_backend_; - MediaPipelineBackendManager* const backend_manager_; + const base::WeakPtr<MediaPipelineBackendManager> backend_manager_; const MediaPipelineDeviceParams::AudioStreamType audio_stream_type_; const AudioContentType content_type_; @@ -143,7 +143,7 @@ ActiveMediaPipelineBackendWrapper::ActiveMediaPipelineBackendWrapper( const media::MediaPipelineDeviceParams& params, MediaPipelineBackendWrapper* wrapping_backend, - MediaPipelineBackendManager* backend_manager, + base::WeakPtr<MediaPipelineBackendManager> backend_manager, MediaResourceTracker* media_resource_tracker) : audio_decoder_ptr_(nullptr), video_decoder_created_(false), @@ -162,14 +162,14 @@ // When the backend is revoked, the video/audio_decoder should be considered // gone to |backend_manager_|. The reason is that the replacement of the // Audio/VideoDecoderWrapper are dummy ones that are not actually playing. - if (audio_decoder_ptr_) { + if (audio_decoder_ptr_ && backend_manager_) { backend_manager_->DecrementDecoderCount( IsSfx() ? DecoderType::SFX_DECODER : DecoderType::AUDIO_DECODER); if (playing_) { backend_manager_->UpdatePlayingAudioCount(IsSfx(), content_type_, -1); } } - if (video_decoder_created_) { + if (video_decoder_created_ && backend_manager_) { backend_manager_->DecrementDecoderCount(DecoderType::VIDEO_DECODER); } } @@ -299,7 +299,7 @@ MediaPipelineBackendWrapper::MediaPipelineBackendWrapper( const media::MediaPipelineDeviceParams& params, - MediaPipelineBackendManager* backend_manager, + base::WeakPtr<MediaPipelineBackendManager> backend_manager, MediaResourceTracker* media_resource_tracker) : revoked_(false), backend_manager_(backend_manager), @@ -309,7 +309,9 @@ } MediaPipelineBackendWrapper::~MediaPipelineBackendWrapper() { - backend_manager_->BackendDestroyed(this); + if (backend_manager_) { + backend_manager_->BackendDestroyed(this); + } } void MediaPipelineBackendWrapper::Revoke() {
diff --git a/chromecast/media/common/media_pipeline_backend_wrapper.h b/chromecast/media/common/media_pipeline_backend_wrapper.h index 6ad8f433..b61917c 100644 --- a/chromecast/media/common/media_pipeline_backend_wrapper.h +++ b/chromecast/media/common/media_pipeline_backend_wrapper.h
@@ -25,9 +25,10 @@ class MediaPipelineBackendWrapper : public CmaBackend { public: - MediaPipelineBackendWrapper(const media::MediaPipelineDeviceParams& params, - MediaPipelineBackendManager* backend_manager, - MediaResourceTracker* media_resource_tracker); + MediaPipelineBackendWrapper( + const media::MediaPipelineDeviceParams& params, + base::WeakPtr<MediaPipelineBackendManager> backend_manager, + MediaResourceTracker* media_resource_tracker); MediaPipelineBackendWrapper(const MediaPipelineBackendWrapper&) = delete; MediaPipelineBackendWrapper& operator=(const MediaPipelineBackendWrapper&) = @@ -59,7 +60,7 @@ bool revoked_; std::unique_ptr<DecoderCreatorCmaBackend> backend_; - MediaPipelineBackendManager* const backend_manager_; + const base::WeakPtr<MediaPipelineBackendManager> backend_manager_; const AudioContentType content_type_; };
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index 1dca82b..0f49a8e 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@ -16277.0.0-1068727 \ No newline at end of file +16279.0.0-1068758 \ No newline at end of file
diff --git a/clank b/clank index af42a48a..687392b 160000 --- a/clank +++ b/clank
@@ -1 +1 @@ -Subproject commit af42a48a53e7717ae26d7cd6e90195216fb9fe51 +Subproject commit 687392ba3de05f6fae3002d7b7435b231d048dce
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn index 827dae8..6b33be9 100644 --- a/components/autofill/core/browser/BUILD.gn +++ b/components/autofill/core/browser/BUILD.gn
@@ -543,6 +543,8 @@ "payments/payments_window_manager_util.cc", "payments/payments_window_manager_util.h", "payments/risk_data_loader.h", + "payments/save_and_fill_manager.cc", + "payments/save_and_fill_manager.h", "payments/virtual_card_enroll_metrics_logger.cc", "payments/virtual_card_enroll_metrics_logger.h", "payments/virtual_card_enrollment_flow.h",
diff --git a/components/autofill/core/browser/payments/payments_autofill_client.cc b/components/autofill/core/browser/payments/payments_autofill_client.cc index 80134d4d..bf9f0c42 100644 --- a/components/autofill/core/browser/payments/payments_autofill_client.cc +++ b/components/autofill/core/browser/payments/payments_autofill_client.cc
@@ -252,6 +252,10 @@ void PaymentsAutofillClient::ShowCreditCardSaveAndFillDialog() {} +payments::SaveAndFillManager* PaymentsAutofillClient::GetSaveAndFillManager() { + return nullptr; +} + void PaymentsAutofillClient::ShowSelectBnplIssuerDialog( std::vector<BnplIssuerContext> bnpl_issuer_context, std::string app_locale,
diff --git a/components/autofill/core/browser/payments/payments_autofill_client.h b/components/autofill/core/browser/payments/payments_autofill_client.h index caf8d47..87915901 100644 --- a/components/autofill/core/browser/payments/payments_autofill_client.h +++ b/components/autofill/core/browser/payments/payments_autofill_client.h
@@ -61,6 +61,7 @@ class MandatoryReauthManager; class PaymentsNetworkInterface; class PaymentsWindowManager; +class SaveAndFillManager; // A payments-specific client interface that handles dependency injection, and // its implementations serve as the integration for platform-specific code. One @@ -574,6 +575,10 @@ // Shows the `Save and Fill` modal dialog. virtual void ShowCreditCardSaveAndFillDialog(); + // Gets the payments Save and Fill manager owned by the client. This will be + // used to handle the Save and Fill dialog. + virtual payments::SaveAndFillManager* GetSaveAndFillManager(); + // Shows the issuer selection dialog for BNPL when the BNPL suggestion is // selected to let users choose a BNPL issuer. virtual void ShowSelectBnplIssuerDialog(
diff --git a/components/autofill/core/browser/payments/save_and_fill_manager.cc b/components/autofill/core/browser/payments/save_and_fill_manager.cc new file mode 100644 index 0000000..e747bc1 --- /dev/null +++ b/components/autofill/core/browser/payments/save_and_fill_manager.cc
@@ -0,0 +1,17 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/payments/save_and_fill_manager.h" + +#include "base/check_deref.h" + +namespace autofill::payments { + +SaveAndFillManager::SaveAndFillManager( + PaymentsAutofillClient* payments_autofill_client) + : payments_autofill_client_(CHECK_DEREF(payments_autofill_client)) {} + +SaveAndFillManager::~SaveAndFillManager() = default; + +} // namespace autofill::payments
diff --git a/components/autofill/core/browser/payments/save_and_fill_manager.h b/components/autofill/core/browser/payments/save_and_fill_manager.h new file mode 100644 index 0000000..580a2ab --- /dev/null +++ b/components/autofill/core/browser/payments/save_and_fill_manager.h
@@ -0,0 +1,30 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_SAVE_AND_FILL_MANAGER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_SAVE_AND_FILL_MANAGER_H_ + +#include "base/memory/raw_ref.h" + +namespace autofill::payments { + +class PaymentsAutofillClient; + +// Owned by PaymentsAutofillClient. There is one instance of this class per Web +// Contents. This class manages the flow for the Save and Fill dialog. +class SaveAndFillManager { + public: + explicit SaveAndFillManager(PaymentsAutofillClient* payments_autofill_client); + SaveAndFillManager(const SaveAndFillManager& other) = delete; + SaveAndFillManager& operator=(const SaveAndFillManager& other) = delete; + ~SaveAndFillManager(); + + private: + // The associated payments autofill client. + const raw_ref<PaymentsAutofillClient> payments_autofill_client_; +}; + +} // namespace autofill::payments + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_SAVE_AND_FILL_MANAGER_H_
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableListToolbar.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableListToolbar.java index 83ce8cc..67d27fc 100644 --- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableListToolbar.java +++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectableListToolbar.java
@@ -281,6 +281,7 @@ getContext(), R.drawable.ic_arrow_back_white_24dp, R.color.default_icon_color_tint_list); + mNavigationIconDrawable.setAutoMirrored(true); mShowInfoIcon = true; mShowInfoStringId = R.string.show_info;
diff --git a/components/browsing_topics/util.cc b/components/browsing_topics/util.cc index 03097a6c..c33c8924 100644 --- a/components/browsing_topics/util.cc +++ b/components/browsing_topics/util.cc
@@ -8,8 +8,8 @@ #include "base/numerics/byte_conversions.h" #include "base/rand_util.h" +#include "crypto/hash.h" #include "crypto/hmac.h" -#include "crypto/sha2.h" #include "third_party/blink/public/common/features.h" namespace browsing_topics { @@ -122,9 +122,9 @@ } HashedHost HashMainFrameHostForStorage(const std::string& main_frame_host) { - int64_t result; - crypto::SHA256HashString(kMainFrameHostStoragePrefix + main_frame_host, - &result, sizeof(result)); + auto hash = + crypto::hash::Sha256(kMainFrameHostStoragePrefix + main_frame_host); + int64_t result = base::I64FromNativeEndian(base::span(hash).first<8u>()); return HashedHost(result); }
diff --git a/components/collaboration/internal/collaboration_service_impl.cc b/components/collaboration/internal/collaboration_service_impl.cc index 02f2b83..cdf8ff2 100644 --- a/components/collaboration/internal/collaboration_service_impl.cc +++ b/components/collaboration/internal/collaboration_service_impl.cc
@@ -134,6 +134,8 @@ std::unique_ptr<CollaborationControllerDelegate> delegate, const tab_groups::EitherGroupID& either_id, CollaborationServiceLeaveOrDeleteEntryPoint entry) { + metrics::RecordLeaveOrDeleteEntryPoint(data_sharing_service_->GetLogger(), + entry); auto it = collaboration_controllers_.find(either_id); if (it != collaboration_controllers_.end()) { it->second->delegate()->PromoteCurrentScreen();
diff --git a/components/collaboration/internal/metrics.cc b/components/collaboration/internal/metrics.cc index eb3069fb..2204b64 100644 --- a/components/collaboration/internal/metrics.cc +++ b/components/collaboration/internal/metrics.cc
@@ -4,8 +4,11 @@ #include "components/collaboration/internal/metrics.h" +#include <string_view> + #include "base/metrics/histogram_functions.h" #include "base/strings/stringprintf.h" +#include "components/collaboration/public/collaboration_flow_entry_point.h" #include "components/data_sharing/public/logger.h" #include "components/data_sharing/public/logger_common.mojom.h" #include "components/data_sharing/public/logger_utils.h" @@ -233,6 +236,34 @@ } } +std::string_view CollaborationServiceLeaveOrDeleteEntryPointToString( + CollaborationServiceLeaveOrDeleteEntryPoint entry) { + switch (entry) { + case CollaborationServiceLeaveOrDeleteEntryPoint::kUnknown: + return "Unknown"; + case CollaborationServiceLeaveOrDeleteEntryPoint:: + kAndroidTabGridDialogLeave: + return "AndroidTabGridDialogLeave"; + case CollaborationServiceLeaveOrDeleteEntryPoint:: + kAndroidTabGridDialogDelete: + return "AndroidTabGridDialogDelete"; + case CollaborationServiceLeaveOrDeleteEntryPoint:: + kAndroidTabGroupContextMenuLeave: + return "AndroidTabGroupContextMenuLeave"; + case CollaborationServiceLeaveOrDeleteEntryPoint:: + kAndroidTabGroupContextMenuDelete: + return "AndroidTabGroupContextMenuDelete"; + case CollaborationServiceLeaveOrDeleteEntryPoint:: + kAndroidTabGroupItemMenuLeave: + return "AndroidTabGroupItemMenuLeave"; + case CollaborationServiceLeaveOrDeleteEntryPoint:: + kAndroidTabGroupItemMenuDelete: + return "AndroidTabGroupItemMenuDelete"; + case CollaborationServiceLeaveOrDeleteEntryPoint::kAndroidTabGroupRow: + return "AndroidTabGroupRow"; + } +} + std::string_view CollaborationServiceStepToString( CollaborationServiceStep step) { switch (step) { @@ -274,6 +305,12 @@ CollaborationServiceShareOrManageEntryPointToString(entry)); } +std::string CreateLeaveOrDeleteEntryLogToString( + CollaborationServiceLeaveOrDeleteEntryPoint entry) { + return base::StringPrintf( + "Leave or Delete Flow Started\n From: %s\n", + CollaborationServiceLeaveOrDeleteEntryPointToString(entry)); +} std::string CreateLatencyLogToString(CollaborationServiceStep step, base::TimeDelta duration) { return base::StringPrintf("Step %s took %dms to complete.", @@ -330,6 +367,15 @@ logger, CreateShareOrManageEntryLogToString(entry)); } +void RecordLeaveOrDeleteEntryPoint( + data_sharing::Logger* logger, + CollaborationServiceLeaveOrDeleteEntryPoint entry) { + base::UmaHistogramEnumeration( + "CollaborationService.LeaveOrDeleteFlow.EntryPoint", entry); + DATA_SHARING_LOG(logger_common::mojom::LogSource::CollaborationService, + logger, CreateLeaveOrDeleteEntryLogToString(entry)); +} + void RecordLatency(data_sharing::Logger* logger, CollaborationServiceStep step, base::TimeDelta duration) {
diff --git a/components/collaboration/internal/metrics.h b/components/collaboration/internal/metrics.h index f64c1c03..1ef7823 100644 --- a/components/collaboration/internal/metrics.h +++ b/components/collaboration/internal/metrics.h
@@ -114,6 +114,9 @@ CollaborationServiceJoinEvent event); void RecordShareOrManageEvent(data_sharing::Logger* logger, CollaborationServiceShareOrManageEvent event); +void RecordLeaveOrDeleteEntryPoint( + data_sharing::Logger* logger, + CollaborationServiceLeaveOrDeleteEntryPoint event); void RecordJoinOrShareOrManageEvent( data_sharing::Logger* logger, FlowType type,
diff --git a/components/collaboration/public/collaboration_flow_entry_point.h b/components/collaboration/public/collaboration_flow_entry_point.h index 1e942ef..ae56915 100644 --- a/components/collaboration/public/collaboration_flow_entry_point.h +++ b/components/collaboration/public/collaboration_flow_entry_point.h
@@ -34,16 +34,20 @@ // org.chromium.components.collaboration) enum class CollaborationServiceShareOrManageEntryPoint { kUnknown = 0, + kDialogToolbarButton = 10, + kRecentActivity = 3, + kNotification = 6, + kTabGroupItemMenuShare = 8, + + // Android specific entry points. kAndroidTabGridDialogShare = 1, kAndroidTabGridDialogManage = 2, - kRecentActivity = 3, kAndroidTabGroupContextMenuShare = 4, kAndroidTabGroupContextMenuManage = 5, - kNotification = 6, kAndroidMessage = 7, - kTabGroupItemMenuShare = 8, kAndroidShareSheetExtra = 9, - kDialogToolbarButton = 10, + + // IOS specific entry points. kiOSTabGroupIndicatorShare = 11, kiOSTabGroupIndicatorManage = 12, kiOSTabGridShare = 13, @@ -52,10 +56,13 @@ kiOSTabStripManage = 16, kiOSTabGroupViewShare = 17, kiOSTabGroupViewManage = 18, + kiOSMessage = 22, + + // Desktop specific entry points. kDesktopGroupEditorShareOrManageButton = 19, kDesktopNotification = 20, kDesktopRecentActivity = 21, - kiOSMessage = 22, + kMaxValue = kiOSMessage, }; // LINT.ThenChange(//tools/metrics/histograms/metadata/collaboration_service/enums.xml:CollaborationServiceShareOrManageEntryPoint) @@ -68,7 +75,16 @@ // org.chromium.components.collaboration) enum class CollaborationServiceLeaveOrDeleteEntryPoint { kUnknown = 0, - kMaxValue = kUnknown, + + // Android specific entry points. + kAndroidTabGridDialogLeave = 1, + kAndroidTabGridDialogDelete = 2, + kAndroidTabGroupContextMenuLeave = 3, + kAndroidTabGroupContextMenuDelete = 4, + kAndroidTabGroupItemMenuLeave = 5, + kAndroidTabGroupItemMenuDelete = 6, + kAndroidTabGroupRow = 7, + kMaxValue = kAndroidTabGroupRow, }; // LINT.ThenChange(//tools/metrics/histograms/metadata/collaboration_service/enums.xml:CollaborationServiceLeaveOrDeleteEntryPoint)
diff --git a/components/history/core/browser/expire_history_backend.h b/components/history/core/browser/expire_history_backend.h index 3f30a43..a127595 100644 --- a/components/history/core/browser/expire_history_backend.h +++ b/components/history/core/browser/expire_history_backend.h
@@ -16,6 +16,7 @@ #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "base/time/time.h" +#include "components/favicon_base/favicon_types.h" #include "components/history/core/browser/history_types.h" class GURL;
diff --git a/components/history/core/browser/history_service.h b/components/history/core/browser/history_service.h index 208f579..205d0cc 100644 --- a/components/history/core/browser/history_service.h +++ b/components/history/core/browser/history_service.h
@@ -34,6 +34,7 @@ #include "base/time/time.h" #include "build/build_config.h" #include "components/favicon_base/favicon_callback.h" +#include "components/favicon_base/favicon_types.h" #include "components/favicon_base/favicon_usage_data.h" #include "components/history/core/browser/history_types.h" #include "components/history/core/browser/keyword_id.h"
diff --git a/components/history/core/browser/history_types.h b/components/history/core/browser/history_types.h index c18f798a..c9835a5 100644 --- a/components/history/core/browser/history_types.h +++ b/components/history/core/browser/history_types.h
@@ -19,7 +19,6 @@ #include "base/containers/flat_map.h" #include "base/functional/callback_forward.h" #include "base/time/time.h" -#include "components/favicon_base/favicon_types.h" #include "components/history/core/browser/history_context.h" #include "components/history/core/browser/url_row.h" #include "components/query_parser/query_parser.h"
diff --git a/components/history_clusters/core/history_clusters_util.h b/components/history_clusters/core/history_clusters_util.h index 4114b32..2cda495 100644 --- a/components/history_clusters/core/history_clusters_util.h +++ b/components/history_clusters/core/history_clusters_util.h
@@ -9,6 +9,7 @@ #include <string> #include <vector> +#include "base/containers/flat_set.h" #include "components/history/core/browser/history_types.h" #include "url/gurl.h"
diff --git a/components/omnibox/browser/autocomplete_controller_metrics.cc b/components/omnibox/browser/autocomplete_controller_metrics.cc index df72368..70cd8f6 100644 --- a/components/omnibox/browser/autocomplete_controller_metrics.cc +++ b/components/omnibox/browser/autocomplete_controller_metrics.cc
@@ -5,13 +5,147 @@ #include "autocomplete_controller_metrics.h" #include <string> +#include <string_view> #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" +#include "base/strings/strcat.h" #include "base/time/time.h" #include "components/omnibox/browser/autocomplete_controller.h" #include "components/omnibox/browser/autocomplete_provider.h" #include "components/omnibox/browser/autocomplete_result.h" +#include "components/omnibox/common/omnibox_feature_configs.h" +#include "third_party/metrics_proto/omnibox_event.pb.h" + +namespace { + +using omnibox_feature_configs::AutocompleteControllerMetricsOptimization; + +enum class MetricNameSuffix { + kDone, + kLastChange, + kLastDefaultChange, + kProvider, + kMaxValue = kProvider +}; + +constexpr std::string_view ToString(MetricNameSuffix name_suffix) { + using enum MetricNameSuffix; + switch (name_suffix) { + case kDone: + return "Done"; + case kLastChange: + return "LastChange"; + case kLastDefaultChange: + return "LastDefaultChange"; + case kProvider: + return "Provider"; + } + NOTREACHED(); +} + +std::string GetMetricName(MetricNameSuffix name_suffix, + const AutocompleteProvider* provider, + std::string_view completion_suffix = "") { + return base::StrCat( + {"Omnibox.AsyncAutocompletionTime2.", ToString(name_suffix), + (provider ? "." : ""), (provider ? provider->GetName() : ""), + (completion_suffix.empty() ? "" : "."), completion_suffix}); +} + +void LogAsyncAutocompletionTimeMetricsImpl(MetricNameSuffix name_suffix, + const AutocompleteProvider* provider, + bool completed, + base::TimeDelta elapsed_time) { + // This may be called up to 40 times per omnibox key-stroke. Cache the + // histograms in a lookup table keyed by name_suffix + provider_number + // (where provider_number is 0 unless name_suffix == kProvider) so that + // we can avoid having to construct their names and look them up each time. + + // The max size of each of the histogram tables. + constexpr int kMaxHistogramIndex = + (static_cast<int>(MetricNameSuffix::kMaxValue) + + metrics::OmniboxEventProto_ProviderType_ProviderType_MAX); + + // Validate the histogram lookup parameters: + // * name_suffix is in [0..kMaxValue] + // * `provider` is non-null iff name_suffix == kProvider, and vice versa. + // * if non-null, provider yields a value <= ProviderType_MAX + DCHECK_GE(static_cast<int>(name_suffix), 0); + DCHECK_LE(static_cast<int>(name_suffix), + static_cast<int>(MetricNameSuffix::kMaxValue)); + DCHECK_EQ(name_suffix == MetricNameSuffix::kProvider, !!provider); + DCHECK(!provider || + provider->AsOmniboxEventProviderType() <= + metrics::OmniboxEventProto_ProviderType_ProviderType_MAX); + + // Each histogram is at the same index in its respective table. + const int histogram_index = + static_cast<int>(name_suffix) + + (provider ? provider->AsOmniboxEventProviderType() : 0); + + // Use the `STATIC_HISTOGRAM_POINTER_GROUP` macro to define a static table of + // atomic histogram pointers which is indexed by `histogram_index`. + // + // I.e., the histograms are ordered as: + // Done, LastChange, DefaultChange, Provider-0, Provider-1, ... +#define STATIC_HISTOGRAM_TIMES_POINTER_GROUP(name, sample) \ + STATIC_HISTOGRAM_POINTER_GROUP( \ + name, histogram_index, kMaxHistogramIndex, \ + AddTimeMillisecondsGranularity(sample), \ + base::Histogram::FactoryTimeGet( \ + name, base::Milliseconds(1), base::Seconds(10), 50, \ + base::HistogramBase::kUmaTargetedHistogramFlag)) + + // These metrics are logged up to about 40 times each per omnibox keystroke. + // `GetMetricName()` is deterministic for any given set of parameters, so each + // histogram name is a run-time constant and a pointer to the corresponding + // histogram object will be cached on first use in a static table. + STATIC_HISTOGRAM_TIMES_POINTER_GROUP(GetMetricName(name_suffix, provider), + elapsed_time); + if (completed) { + STATIC_HISTOGRAM_TIMES_POINTER_GROUP( + GetMetricName(name_suffix, provider, "Completed"), elapsed_time); + } else { + STATIC_HISTOGRAM_TIMES_POINTER_GROUP( + GetMetricName(name_suffix, provider, "Interrupted"), elapsed_time); + } +#undef STATIC_HISTOGRAM_TIMES_POINTER_GROUP +} + +inline void LogAsyncAutocompletionTimeMetrics(MetricNameSuffix metric, + bool completed, + base::TimeDelta elapsed_time) { + DCHECK_NE(metric, MetricNameSuffix::kProvider); + LogAsyncAutocompletionTimeMetricsImpl(metric, nullptr, completed, + elapsed_time); +} + +inline void LogAsyncAutocompletionTimeMetrics( + const AutocompleteProvider& provider, + base::TimeDelta elapsed_time) { + LogAsyncAutocompletionTimeMetricsImpl(MetricNameSuffix::kProvider, &provider, + provider.done(), elapsed_time); +} + +void OldLogAsyncAutocompletionTimeMetrics(const std::string& name, + bool completed, + base::TimeDelta elapsed_time) { + const auto name_prefix = "Omnibox.AsyncAutocompletionTime2." + name; + + // These metrics are logged up to about 40 times per omnibox keystroke. Use + // the, less efficient, UMA histogram functions because the names are dynamic. + // Each histogram function invocation results in a string alloc and a + // histogram lookup => ~120 allocs and 120 lookups per omnibox keystroke. + base::UmaHistogramTimes(name_prefix, elapsed_time); + if (completed) { + base::UmaHistogramTimes(name_prefix + ".Completed", elapsed_time); + } else { + base::UmaHistogramTimes(name_prefix + ".Interrupted", elapsed_time); + } +} + +} // namespace AutocompleteControllerMetrics::AutocompleteControllerMetrics( const AutocompleteController& controller) @@ -29,14 +163,16 @@ std::vector<AutocompleteResult::MatchDedupComparator> last_result, std::vector<AutocompleteResult::MatchDedupComparator> new_result) { // Only log metrics for async requests. - if (controller_->input().omit_asynchronous_matches()) + if (controller_->input().omit_asynchronous_matches()) { return; + } // If results are empty then the omnibox is likely closed, and clearing old // results won't be user visible. E.g., this occurs when opening a new tab // while the popup was open. - if (new_result.empty()) + if (new_result.empty()) { return; + } // Log suggestion changes. @@ -51,25 +187,26 @@ } LogSuggestionChangeInAnyPositionMetrics(any_match_changed_or_removed); - // Log suggestion finalization times. - // This handles logging as soon as the final update occurs, while `OnStop()` - // handles the case where the final update never occurs because of - // interruptions. + // Log suggestion finalization times. This handles logging as soon as the + // final update occurs, while `OnStop()` handles the case where the final + // update never occurs because of interruptions. // E.g., suggestion deletion can call `OnNotifyChanged()` after the controller - // is done and finalization metrics have been logged. They shouldn't be - // re-logged. + // is done and finalization metrics have been logged. They should not be + // logged again. if (controller_->last_update_type() == - AutocompleteController::UpdateType::kMatchDeletion) + AutocompleteController::UpdateType::kMatchDeletion) { return; + } const bool any_match_changed_or_removed_or_added = any_match_changed_or_removed || last_result.size() != new_result.size(); const bool default_match_changed_or_removed_or_added = last_result.empty() || last_result[0] != new_result[0]; - if (any_match_changed_or_removed_or_added) + if (any_match_changed_or_removed_or_added) { last_change_time_ = base::TimeTicks::Now(); + } if (default_match_changed_or_removed_or_added) { DCHECK(any_match_changed_or_removed_or_added); last_default_change_time_ = last_change_time_; @@ -78,55 +215,60 @@ // update. // TODO(crbug.com/364303536): `logged_finalization_metrics_` should be // guaranteed false here (hence the DCHECK in - // `LogSuggestionFinalizationMetrics()`. But because of a temporary bandaid + // `LogSuggestionFinalizationMetrics()`. But because of a temporary band-aid // to allow history embedding answers and unscoped extension suggestions to // ignore the stop timer, we need to check it anyways. - if (controller_->done() && !logged_finalization_metrics_) + if (controller_->done() && !logged_finalization_metrics_) { LogSuggestionFinalizationMetrics(); + } } void AutocompleteControllerMetrics::OnProviderUpdate( const AutocompleteProvider& provider) const { // Only log metrics for async requests. This will likely never happen, since // `OnProviderUpdate()` is only called by async providers (but not necessarily - // async'ly, see the comments in - // `AutocompleteController::OnProviderUpdate()`). - if (controller_->input().omit_asynchronous_matches()) + // async'ly, see the comments in `AutocompleteController::OnProviderUpdate`). + if (controller_->input().omit_asynchronous_matches()) { return; + } // Some async providers may produce multiple updates. Only log the final async // update. - if (provider.done()) + if (provider.done()) { LogProviderTimeMetrics(provider); + } } void AutocompleteControllerMetrics::OnStop() { // Only log metrics for async requests. - if (controller_->input().omit_asynchronous_matches()) + if (controller_->input().omit_asynchronous_matches()) { return; + } // Done providers should already be logged by `OnProviderUpdate()`. for (const auto& provider : controller_->providers()) { - if (!provider->done()) + if (!provider->done()) { LogProviderTimeMetrics(*provider); + } } - // If the controller is done, `OnNotifyChanged()` should have already logged - // finalization metrics. This case, i.e. `OnStop()` invoked even though the - // controller is done, is possible because `OnStart()` calls `OnStop()`. + // If the controller is done, `OnNotifyChanged()` should have already + // logged finalization metrics. This case, i.e. `OnStop()` invoked even + // though the controller is done, is possible because `OnStart()` calls + // `OnStop()`. // TODO(crbug.com/364303536): `logged_finalization_metrics_` should be // guaranteed false here (hence the DCHECK in - // `LogSuggestionFinalizationMetrics()`. But because of a temporary bandaid - // to allow history embedding answers and unscoped extension answers to - // ignore the stop timer, we need to check it anyways. + // `LogSuggestionFinalizationMetrics()`. But because of a temporary + // bandaid to allow history embedding answers and unscoped extension + // answers to ignore the stop timer, we need to check it anyways. if (!controller_->done() && !logged_finalization_metrics_) { LogSuggestionFinalizationMetrics(); } } void AutocompleteControllerMetrics::LogSuggestionFinalizationMetrics() { - // Finalization metrics should be logged once only, either when all async - // providers complete or they're interrupted before completion. + // Finalization metrics should be logged once only, either when all + // async providers complete or they're interrupted before completion. #if BUILDFLAG(IS_IOS) // iOS is weird in that it sometimes calls `InjectAdHocMatch()` when the user // selects a suggestion, thus changing the results when autocompletion is done @@ -140,65 +282,96 @@ controller_->last_update_type()); logged_finalization_metrics_ = true; - LogAsyncAutocompletionTimeMetrics("Done", controller_->done(), - base::TimeTicks::Now()); - LogAsyncAutocompletionTimeMetrics("LastChange", controller_->done(), - last_change_time_); - LogAsyncAutocompletionTimeMetrics("LastDefaultChange", controller_->done(), - last_default_change_time_); + const auto done_elapsed_time = base::TimeTicks::Now() - start_time_; + const auto last_change_elapsed_time = last_change_time_ - start_time_; + const auto last_default_change_elapsed_time = + last_default_change_time_ - start_time_; + const bool is_completed = controller_->done(); + + if (AutocompleteControllerMetricsOptimization::Get().enabled) { + using enum MetricNameSuffix; + LogAsyncAutocompletionTimeMetrics(kDone, is_completed, done_elapsed_time); + LogAsyncAutocompletionTimeMetrics(kLastChange, is_completed, + last_change_elapsed_time); + LogAsyncAutocompletionTimeMetrics(kLastDefaultChange, is_completed, + last_default_change_elapsed_time); + } else { + OldLogAsyncAutocompletionTimeMetrics("Done", is_completed, + done_elapsed_time); + OldLogAsyncAutocompletionTimeMetrics("LastChange", is_completed, + last_change_elapsed_time); + OldLogAsyncAutocompletionTimeMetrics("LastDefaultChange", is_completed, + last_default_change_elapsed_time); + } } void AutocompleteControllerMetrics::LogProviderTimeMetrics( const AutocompleteProvider& provider) const { - LogAsyncAutocompletionTimeMetrics( - std::string("Provider.") + provider.GetName(), provider.done(), - base::TimeTicks::Now()); -} - -void AutocompleteControllerMetrics::LogAsyncAutocompletionTimeMetrics( - const std::string& name, - bool completed, - const base::TimeTicks end_time) const { - const auto name_prefix = "Omnibox.AsyncAutocompletionTime2." + name; - const auto elapsed_time = end_time - start_time_; - // These metrics are logged up to about 40 times per omnibox keystroke. But - // use UMA functions as the names are dynamic. - base::UmaHistogramTimes(name_prefix, elapsed_time); - if (completed) - base::UmaHistogramTimes(name_prefix + ".Completed", elapsed_time); - else - base::UmaHistogramTimes(name_prefix + ".Interrupted", elapsed_time); + const auto elapsed_time = base::TimeTicks::Now() - start_time_; + if (AutocompleteControllerMetricsOptimization::Get().enabled) { + LogAsyncAutocompletionTimeMetrics(provider, elapsed_time); + } else { + OldLogAsyncAutocompletionTimeMetrics( + std::string("Provider.") + provider.GetName(), provider.done(), + elapsed_time); + } } void AutocompleteControllerMetrics::LogSuggestionChangeIndexMetrics( size_t change_index) const { - std::string name = "Omnibox.MatchStability2.MatchChangeIndex"; - size_t max = AutocompleteResult::kMaxAutocompletePositionValue; // These metrics are logged up to about 50 times per omnibox keystroke, so use - // UMA macros for efficiency. - if (controller_->last_update_type() == - AutocompleteController::UpdateType::kSyncPass || - controller_->last_update_type() == - AutocompleteController::UpdateType::kSyncPassOnly) { - UMA_HISTOGRAM_EXACT_LINEAR(name + ".CrossInput", change_index, max); + // the UMA macros (which cache the histogram pointer) for efficiency. + static constexpr char kName[] = "Omnibox.MatchStability2.MatchChangeIndex"; + constexpr size_t max = AutocompleteResult::kMaxAutocompletePositionValue; + const bool is_sync = (controller_->last_update_type() == + AutocompleteController::UpdateType::kSyncPass || + controller_->last_update_type() == + AutocompleteController::UpdateType::kSyncPassOnly); + + if (AutocompleteControllerMetricsOptimization::Get().enabled) { + UMA_HISTOGRAM_EXACT_LINEAR(kName, change_index, max); + if (is_sync) { + UMA_HISTOGRAM_EXACT_LINEAR(base::StrCat({kName, ".CrossInput"}), + change_index, max); + } else { + UMA_HISTOGRAM_EXACT_LINEAR(base::StrCat({kName, ".Async"}), change_index, + max); + } } else { - UMA_HISTOGRAM_EXACT_LINEAR(name + ".Async", change_index, max); + const std::string name = kName; // Unnecessary string alloc! + UMA_HISTOGRAM_EXACT_LINEAR(name, change_index, max); + if (is_sync) { + UMA_HISTOGRAM_EXACT_LINEAR(name + ".CrossInput", change_index, max); + } else { + UMA_HISTOGRAM_EXACT_LINEAR(name + ".Async", change_index, max); + } } - UMA_HISTOGRAM_EXACT_LINEAR(name, change_index, max); } void AutocompleteControllerMetrics::LogSuggestionChangeInAnyPositionMetrics( bool changed) const { - std::string name = "Omnibox.MatchStability2.MatchChangeInAnyPosition"; - // These metrics are logged up to about 5 times per omnibox keystroke, so - // use UMA macros for efficiency. - if (controller_->last_update_type() == - AutocompleteController::UpdateType::kSyncPass || - controller_->last_update_type() == - AutocompleteController::UpdateType::kSyncPassOnly) { - UMA_HISTOGRAM_BOOLEAN(name + ".CrossInput", changed); + // These metrics are logged up to about 5 times per omnibox keystroke, so use + // the UMA macros (which cache the histogram pointer) for efficiency. + static constexpr char kName[] = + "Omnibox.MatchStability2.MatchChangeInAnyPosition"; + const bool is_sync = (controller_->last_update_type() == + AutocompleteController::UpdateType::kSyncPass || + controller_->last_update_type() == + AutocompleteController::UpdateType::kSyncPassOnly); + if (AutocompleteControllerMetricsOptimization::Get().enabled) { + UMA_HISTOGRAM_BOOLEAN(kName, changed); + if (is_sync) { + UMA_HISTOGRAM_BOOLEAN(base::StrCat({kName, ".CrossInput"}), changed); + } else { + UMA_HISTOGRAM_BOOLEAN(base::StrCat({kName, ".Async"}), changed); + } } else { - UMA_HISTOGRAM_BOOLEAN(name + ".Async", changed); + const std::string name = kName; // Unnecessary string alloc! + UMA_HISTOGRAM_BOOLEAN(name, changed); + if (is_sync) { + UMA_HISTOGRAM_BOOLEAN(name + ".CrossInput", changed); + } else { + UMA_HISTOGRAM_BOOLEAN(name + ".Async", changed); + } } - UMA_HISTOGRAM_BOOLEAN(name, changed); }
diff --git a/components/omnibox/browser/autocomplete_controller_metrics.h b/components/omnibox/browser/autocomplete_controller_metrics.h index 6a537d2..8b18ac2 100644 --- a/components/omnibox/browser/autocomplete_controller_metrics.h +++ b/components/omnibox/browser/autocomplete_controller_metrics.h
@@ -86,13 +86,6 @@ // whether the provider completed or was interrupted. void LogProviderTimeMetrics(const AutocompleteProvider& provider) const; - // Helper for the above 2 logging methods. Logs - // 'Omnibox.AsyncAutocompletionTime.<name>'. Additionally logs either - // '*.Completed' or '*.Interrupted' depending on `completed`. - void LogAsyncAutocompletionTimeMetrics(const std::string& name, - bool completed, - const base::TimeTicks end_time) const; - // Logs 'Omnibox.MatchStability.MatchChangeIndex'. Additionally logs // '*.CrossInput' or '*.Async' depending on `controller_.in_start()`. void LogSuggestionChangeIndexMetrics(size_t change_index) const;
diff --git a/components/omnibox/browser/autocomplete_controller_metrics_unittest.cc b/components/omnibox/browser/autocomplete_controller_metrics_unittest.cc index 7d39bf4..eb637bc6 100644 --- a/components/omnibox/browser/autocomplete_controller_metrics_unittest.cc +++ b/components/omnibox/browser/autocomplete_controller_metrics_unittest.cc
@@ -21,6 +21,7 @@ #include "components/omnibox/browser/autocomplete_result.h" #include "components/omnibox/browser/fake_autocomplete_controller.h" #include "components/omnibox/browser/fake_autocomplete_provider.h" +#include "components/omnibox/common/omnibox_feature_configs.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -58,7 +59,7 @@ }; } // namespace -class AutocompleteControllerMetricsTest : public testing::Test { +class AutocompleteControllerMetricsTest : public testing::TestWithParam<bool> { public: AutocompleteControllerMetricsTest() : controller_(&task_environment_), @@ -67,6 +68,8 @@ base::MakeRefCounted<FakeAutocompleteProviderDelayed>( AutocompleteProvider::Type::TYPE_BOOKMARK, &task_environment_)}; + scoped_optimization_config_.Get().enabled = GetParam(); + // Allow tests to simulate an initial update with no changes. Since the // 0-matches cases is special handled, tests can't simply do // `Simulate(true|false, {})` to simulate an initial update with no changes; @@ -211,13 +214,16 @@ // Used to control time passed between calls. Many metrics tested are timing // metrics. + omnibox_feature_configs::ScopedConfigForTesting< + omnibox_feature_configs::AutocompleteControllerMetricsOptimization> + scoped_optimization_config_; base::test::SingleThreadTaskEnvironment task_environment_{ base::test::TaskEnvironment::TimeSource::MOCK_TIME}; FakeAutocompleteController controller_; std::unique_ptr<base::HistogramTester> histogram_tester_; }; -TEST_F(AutocompleteControllerMetricsTest, SuggestionFinalization_SyncInput) { +TEST_P(AutocompleteControllerMetricsTest, SuggestionFinalization_SyncInput) { // Sync inputs should not log metrics. SetInputSync(true); SimulateStart(true, {CreateMatch(1)}); @@ -225,7 +231,7 @@ ExpectNoSuggestionFinalizationMetrics(); } -TEST_F(AutocompleteControllerMetricsTest, +TEST_P(AutocompleteControllerMetricsTest, SuggestionFinalization_OnlySyncUpdate) { // Sync updates should log metrics. SimulateStart(true, {CreateMatch(1)}); @@ -233,7 +239,7 @@ StopAndExpectNoSuggestionFinalizationMetrics(); } -TEST_F(AutocompleteControllerMetricsTest, +TEST_P(AutocompleteControllerMetricsTest, SuggestionFinalization_OnlySyncUpdateWithNoChanges) { // Sync updates without changes should log metrics. SimulateStart(true, {CreateMatch(0)}); @@ -241,7 +247,7 @@ StopAndExpectNoSuggestionFinalizationMetrics(); } -TEST_F(AutocompleteControllerMetricsTest, +TEST_P(AutocompleteControllerMetricsTest, SuggestionFinalization_SyncAnd3AsyncUpdate) { // This is the typical flow: 1 sync update followed by multiple async updates. SimulateStart(false, {CreateMatch(1)}); @@ -255,7 +261,7 @@ StopAndExpectNoSuggestionFinalizationMetrics(); } -TEST_F(AutocompleteControllerMetricsTest, +TEST_P(AutocompleteControllerMetricsTest, SuggestionFinalization_SyncAnd3AsyncUpdateWithNoChanges) { // 1 sync and 3 async updates, none of the 4 has a change. SimulateStart(false, {CreateMatch(0)}); @@ -269,7 +275,7 @@ StopAndExpectNoSuggestionFinalizationMetrics(); } -TEST_F( +TEST_P( AutocompleteControllerMetricsTest, SuggestionFinalization_UnchangedSyncAnd2UnchangedAnd1ChangedAsyncUpdates) { // 1 sync and 3 async updates, only the last of the 4 has a change. @@ -284,7 +290,7 @@ StopAndExpectNoSuggestionFinalizationMetrics(); } -TEST_F( +TEST_P( AutocompleteControllerMetricsTest, SuggestionFinalization_UnchangedSyncAnd1ChangedAnd2UnchangedAsyncUpdates) { // 1 sync and 3 async updates, only the 2nd of the 4 has a change. Because of @@ -300,7 +306,7 @@ StopAndExpectNoSuggestionFinalizationMetrics(); } -TEST_F( +TEST_P( AutocompleteControllerMetricsTest, SuggestionFinalization_UnchangedSyncAnd1ChangedAnd2UnchangedAsyncUpdates_ChangeAppliedBeforeDone) { // 1 sync and 3 async updates, only the 2nd of the 4 has a change. The @@ -317,7 +323,7 @@ StopAndExpectNoSuggestionFinalizationMetrics(); } -TEST_F(AutocompleteControllerMetricsTest, +TEST_P(AutocompleteControllerMetricsTest, SuggestionFinalization_ChangedSyncAnd3UnchangedAsyncUpdates) { // 1 sync and 3 async updates, only the 1st of the 4 has a change. SimulateStart(false, {CreateMatch(1)}); @@ -331,7 +337,7 @@ StopAndExpectNoSuggestionFinalizationMetrics(); } -TEST_F(AutocompleteControllerMetricsTest, +TEST_P(AutocompleteControllerMetricsTest, SuggestionFinalization_StopTimerReached) { // Simulates the case where the async updates take longer than the 1.5s stop // timer. It's not possible for the sync update to take longer, as the stop @@ -346,7 +352,7 @@ StopAndExpectNoSuggestionFinalizationMetrics(); } -TEST_F(AutocompleteControllerMetricsTest, SuggestionFinalization_Interrupted) { +TEST_P(AutocompleteControllerMetricsTest, SuggestionFinalization_Interrupted) { // Start 1st input. SimulateStart(false, {CreateMatch(1)}); // 1 async update for 1st input. @@ -372,7 +378,7 @@ StopAndExpectNoSuggestionFinalizationMetrics(); } -TEST_F(AutocompleteControllerMetricsTest, SuggestionFinalization_ExpireTimer) { +TEST_P(AutocompleteControllerMetricsTest, SuggestionFinalization_ExpireTimer) { SimulateStart(true, {CreateMatch(0), CreateMatch(1)}); ResetHistogramTester(); // A sync update without matches. The transferred match should remain. @@ -387,7 +393,7 @@ StopAndExpectNoSuggestionFinalizationMetrics(); } -TEST_F(AutocompleteControllerMetricsTest, +TEST_P(AutocompleteControllerMetricsTest, SuggestionFinalization_MatchDeletion) { SimulateStart(true, {CreateMatch(0), CreateMatch(1)}); ResetHistogramTester(); @@ -407,7 +413,7 @@ StopAndExpectNoSuggestionFinalizationMetrics(); } -TEST_F(AutocompleteControllerMetricsTest, +TEST_P(AutocompleteControllerMetricsTest, SuggestionFinalization_DefaultUnchanged) { // Sync update with a default match change. SimulateStart(false, {CreateMatch(1)}); @@ -421,7 +427,7 @@ StopAndExpectNoSuggestionFinalizationMetrics(); } -TEST_F(AutocompleteControllerMetricsTest, Provider_SyncAndAsyncCompletion) { +TEST_P(AutocompleteControllerMetricsTest, Provider_SyncAndAsyncCompletion) { controller_.providers_ = { base::MakeRefCounted<FakeAutocompleteProviderDelayed>( AutocompleteProvider::Type::TYPE_BOOKMARK, &task_environment_, true), @@ -472,7 +478,7 @@ } } -TEST_F(AutocompleteControllerMetricsTest, +TEST_P(AutocompleteControllerMetricsTest, Provider_1ProviderWithMultipleUpdates) { // Sync update without completion. controller_.GetFakeProvider().done_ = true; @@ -489,7 +495,7 @@ ExpectNoProviderMetrics(controller_.GetFakeProvider().GetName()); } -TEST_F(AutocompleteControllerMetricsTest, Provider_Interrupted) { +TEST_P(AutocompleteControllerMetricsTest, Provider_Interrupted) { controller_.providers_ = { base::MakeRefCounted<FakeAutocompleteProviderDelayed>( AutocompleteProvider::Type::TYPE_BOOKMARK, &task_environment_), @@ -514,7 +520,7 @@ ExpectSingleCountSuggestionFinalizationMetrics(3, 0, 0, false); } -TEST_F(AutocompleteControllerMetricsTest, MatchStability) { +TEST_P(AutocompleteControllerMetricsTest, MatchStability) { auto create_result = [&](std::vector<int> ids) { std::vector<AutocompleteMatch> matches; std::ranges::transform(ids, std::back_inserter(matches), [&](int id) { @@ -656,3 +662,7 @@ testing::ElementsAre(base::Bucket(0, 1))); ResetHistogramTester(); } + +INSTANTIATE_TEST_SUITE_P(All, + AutocompleteControllerMetricsTest, + ::testing::Values(false, true));
diff --git a/components/omnibox/browser/clipboard_provider.h b/components/omnibox/browser/clipboard_provider.h index 9daf7ebf..2458b07 100644 --- a/components/omnibox/browser/clipboard_provider.h +++ b/components/omnibox/browser/clipboard_provider.h
@@ -9,6 +9,7 @@ #include "base/memory/raw_ptr.h" #include "components/omnibox/browser/autocomplete_enums.h" #include "components/omnibox/browser/autocomplete_provider.h" +#include "ui/gfx/image/image.h" class AutocompleteProviderClient; class AutocompleteProviderListener;
diff --git a/components/omnibox/common/omnibox_feature_configs.cc b/components/omnibox/common/omnibox_feature_configs.cc index 2f212475..befcf448 100644 --- a/components/omnibox/common/omnibox_feature_configs.cc +++ b/components/omnibox/common/omnibox_feature_configs.cc
@@ -20,6 +20,16 @@ base::FEATURE_ENABLED_BY_DEFAULT; #endif +BASE_FEATURE(AutocompleteControllerMetricsOptimization:: + kAutocompleteControllerMetricsOptimization, + "AutocompleteControlMetricsOptimization", + base::FEATURE_ENABLED_BY_DEFAULT); +AutocompleteControllerMetricsOptimization:: + AutocompleteControllerMetricsOptimization() { + enabled = + base::FeatureList::IsEnabled(kAutocompleteControllerMetricsOptimization); +} + // TODO(manukh): Enabled by default in m120. Clean up 12/5 when after m121 // branch cut. // static
diff --git a/components/omnibox/common/omnibox_feature_configs.h b/components/omnibox/common/omnibox_feature_configs.h index fb3b3777..b8c3569 100644 --- a/components/omnibox/common/omnibox_feature_configs.h +++ b/components/omnibox/common/omnibox_feature_configs.h
@@ -104,6 +104,14 @@ // Add new configs below, ordered alphabetically. +// If enabled, use more efficient codepaths when capturing autocomplete metrics. +struct AutocompleteControllerMetricsOptimization + : Config<AutocompleteControllerMetricsOptimization> { + DECLARE_FEATURE(kAutocompleteControllerMetricsOptimization); + AutocompleteControllerMetricsOptimization(); + bool enabled; +}; + // If enabled, adds recent calc suggestions. struct CalcProvider : Config<CalcProvider> { DECLARE_FEATURE(kCalcProvider);
diff --git a/components/optimization_guide/core/BUILD.gn b/components/optimization_guide/core/BUILD.gn index e7ddf75..3e61523 100644 --- a/components/optimization_guide/core/BUILD.gn +++ b/components/optimization_guide/core/BUILD.gn
@@ -473,6 +473,8 @@ "model_execution/test/request_builder.h", "model_execution/test/response_holder.cc", "model_execution/test/response_holder.h", + "model_execution/test/substitution_builder.cc", + "model_execution/test/substitution_builder.h", "model_execution/test/test_on_device_model_component_state_manager.cc", "model_execution/test/test_on_device_model_component_state_manager.h", ]
diff --git a/components/optimization_guide/core/model_execution/multimodal_message.cc b/components/optimization_guide/core/model_execution/multimodal_message.cc index 7cf9c89..b173c8b 100644 --- a/components/optimization_guide/core/model_execution/multimodal_message.cc +++ b/components/optimization_guide/core/model_execution/multimodal_message.cc
@@ -51,6 +51,14 @@ MultimodalMessageEditView::~MultimodalMessageEditView() = default; +void MultimodalMessageEditView::MarkPending(int tag, bool pending) { + if (pending) { + overlay_->pending.insert(tag); + } else { + overlay_->pending.erase(tag); + } +} + void MultimodalMessageEditView::Set(int tag, const std::string& v) { ProtoStatus status = SetProtoField(&message_.get(), tag, v); CHECK_EQ(status, ProtoStatus::kOk); @@ -97,6 +105,28 @@ MultimodalMessageReadView::~MultimodalMessageReadView() = default; +bool MultimodalMessageReadView::IsPending( + const proto::ProtoField& proto_field) const { + std::optional<MultimodalMessageReadView> parent = *this; + for (int i = 0; i < proto_field.proto_descriptors_size() - 1; i++) { + if (!parent->overlay_) { + return false; + } + int32_t tag = proto_field.proto_descriptors(i).tag_number(); + if (parent->overlay_->pending.contains(tag)) { + return true; + } + parent = parent->GetNested(proto_field.proto_descriptors(i).tag_number()); + if (!parent) { + return false; + } + } + int32_t tag = + proto_field.proto_descriptors(proto_field.proto_descriptors_size() - 1) + .tag_number(); + return parent->overlay_ && parent->overlay_->pending.contains(tag); +} + // Get the type of multimodal content for a field. MultimodalType MultimodalMessageReadView::GetMultimodalType( const proto::ProtoField& proto_field) const { @@ -255,6 +285,10 @@ return MultimodalMessageEditView(*message, overlay_->overlays[n]); } +void RepeatedMultimodalMessageEditView::MarkIncomplete(bool incomplete) { + overlay_->incomplete = incomplete; +} + RepeatedMultimodalMessageReadView::RepeatedMultimodalMessageReadView( const MessageLite& parent, int32_t tag, @@ -280,6 +314,10 @@ return MultimodalMessageReadView(*message, &overlay_->overlays[n]); } +bool RepeatedMultimodalMessageReadView::IsIncomplete() const { + return overlay_ && overlay_->incomplete; +} + MultimodalMessage::MultimodalMessage() = default; MultimodalMessage::MultimodalMessage(const MessageLite& initial_message) : message_(initial_message.New()) {
diff --git a/components/optimization_guide/core/model_execution/multimodal_message.h b/components/optimization_guide/core/model_execution/multimodal_message.h index 4a56337..2af2320 100644 --- a/components/optimization_guide/core/model_execution/multimodal_message.h +++ b/components/optimization_guide/core/model_execution/multimodal_message.h
@@ -27,6 +27,7 @@ #include <cstddef> #include <map> #include <optional> +#include <set> #include <vector> #include "base/memory/raw_ptr.h" @@ -56,6 +57,9 @@ MultimodalMessageData& operator=(MultimodalMessageData&&); ~MultimodalMessageData(); + // Which fields are currently marked pending. + std::set<int> pending; + // Images stored for fields of the message. std::map<int, SkBitmap> images; @@ -89,6 +93,9 @@ // effectively have no overlay, and default initialized values can be created // for them lazily. std::vector<MultimodalMessageData> overlays; + + // If this field is expected to have more data added later. + bool incomplete = false; }; // A mutable view of a MultimodalMessage (or a submessage of it). @@ -101,6 +108,10 @@ MultimodalMessageData& overlay); ~MultimodalMessageEditView(); + // Marks a field as pending (or clears it if pending = false). + // Substitutions will truncate where they would depend on this field. + void MarkPending(int tag, bool pending = true); + // Sets a string field value. void Set(int tag, const std::string& v); @@ -150,6 +161,9 @@ return std::string(message_->GetTypeName()); } + // Returns true iff the field or any parent has been marked pending. + bool IsPending(const proto::ProtoField& proto_field) const; + // Get the type of multimodal content for a field. MultimodalType GetMultimodalType(const proto::ProtoField& proto_field) const; @@ -199,6 +213,11 @@ // Get a previously added message. MultimodalMessageEditView Get(int n); + // Indicate that more content may be appended to this field later. + // Substitutions will truncate where they would depend on whether more + // messages are present. + void MarkIncomplete(bool incomplete = true); + private: // The underlying message that owns the repeated field. const raw_ref<google::protobuf::MessageLite> parent_; @@ -227,6 +246,9 @@ // Will crash on out of bounds access. MultimodalMessageReadView Get(int n) const; + // Return whether this list was marked incomplete. + bool IsIncomplete() const; + private: // The underlying message that owns the repeated field. const raw_ref<const google::protobuf::MessageLite> parent_;
diff --git a/components/optimization_guide/core/model_execution/substitution.cc b/components/optimization_guide/core/model_execution/substitution.cc index 4d4a743..b12907b 100644 --- a/components/optimization_guide/core/model_execution/substitution.cc +++ b/components/optimization_guide/core/model_execution/substitution.cc
@@ -45,51 +45,64 @@ int offset = 0; }; +enum class ConditionResult { kFalse, kTrue, kStop }; + +ConditionResult AsResult(bool value) { + return value ? ConditionResult::kTrue : ConditionResult::kFalse; +} + // Returns whether `condition` applies based on `message`. -bool EvaluateCondition(const ResolutionContext& ctx, - const proto::Condition& condition) { +ConditionResult EvaluateCondition(const ResolutionContext& ctx, + const proto::Condition& condition) { + if (ctx.view.IsPending(condition.proto_field())) { + return ConditionResult::kStop; + } std::optional<proto::Value> proto_value = ctx.view.GetValue(condition.proto_field()); if (!proto_value) { - return false; + return AsResult(false); } switch (condition.operator_type()) { case proto::OPERATOR_TYPE_EQUAL_TO: - return AreValuesEqual(*proto_value, condition.value()); + return AsResult(AreValuesEqual(*proto_value, condition.value())); case proto::OPERATOR_TYPE_NOT_EQUAL_TO: - return !AreValuesEqual(*proto_value, condition.value()); + return AsResult(!AreValuesEqual(*proto_value, condition.value())); default: base::debug::DumpWithoutCrashing(); - return false; + return AsResult(false); } } -bool AndConditions(const ResolutionContext& ctx, - const RepeatedPtrField<proto::Condition>& conditions) { +ConditionResult AndConditions( + const ResolutionContext& ctx, + const RepeatedPtrField<proto::Condition>& conditions) { for (const auto& condition : conditions) { - if (!EvaluateCondition(ctx, condition)) { - return false; + ConditionResult result = EvaluateCondition(ctx, condition); + if (result != ConditionResult::kTrue) { + return result; } } - return true; + return ConditionResult::kTrue; } -bool OrConditions(const ResolutionContext& ctx, - const RepeatedPtrField<proto::Condition>& conditions) { +ConditionResult OrConditions( + const ResolutionContext& ctx, + const RepeatedPtrField<proto::Condition>& conditions) { for (const auto& condition : conditions) { - if (EvaluateCondition(ctx, condition)) { - return true; + ConditionResult result = EvaluateCondition(ctx, condition); + if (result != ConditionResult::kFalse) { + return result; } } - return false; + return ConditionResult::kFalse; } // Returns whether `conditions` apply based on `message`. -bool DoConditionsApply(const ResolutionContext& ctx, - const proto::ConditionList& conditions) { +ConditionResult DoConditionsApply(const ResolutionContext& ctx, + const proto::ConditionList& conditions) { if (conditions.conditions_size() == 0) { - return true; + return ConditionResult::kTrue; } switch (conditions.condition_evaluation_type()) { @@ -99,7 +112,7 @@ return AndConditions(ctx, conditions.conditions()); default: base::debug::DumpWithoutCrashing(); - return false; + return ConditionResult::kFalse; } } @@ -109,8 +122,9 @@ class InputBuilder final { public: enum class Error { - OK = 0, - FAILED = 1, + kOk = 0, + kFailed = 1, // The config is not valid over this input. + kStop = 2, // Terminate early due to a pending field. }; InputBuilder() : out_(Input::New()) {} Error ResolveSubstitutedString(const ResolutionContext& ctx, @@ -155,40 +169,49 @@ InputBuilder::Error InputBuilder::ResolveProtoField( const ResolutionContext& ctx, const proto::ProtoField& field) { + if (ctx.view.IsPending(field)) { + return Error::kStop; + } std::optional<proto::Value> value = ctx.view.GetValue(field); if (!value) { DVLOG(1) << "Invalid proto field of " << ctx.view.GetTypeName(); - return Error::FAILED; + return Error::kFailed; } AddString(GetStringFromValue(*value)); - return Error::OK; + return Error::kOk; } InputBuilder::Error InputBuilder::ResolveRangeExpr( const ResolutionContext& ctx, const proto::RangeExpr& expr) { + if (ctx.view.IsPending(expr.proto_field())) { + return Error::kStop; + } auto repeated = ctx.view.GetRepeated(expr.proto_field()); if (!repeated) { DVLOG(1) << "Invalid proto field for RangeExpr over " << ctx.view.GetTypeName(); - return Error::FAILED; + return Error::kFailed; } int repeated_size = repeated->Size(); for (int i = 0; i < repeated_size; i++) { Error error = ResolveSubstitutedString( ResolutionContext{repeated->Get(i), i}, expr.expr()); - if (error != Error::OK) { + if (error != Error::kOk) { return error; } } - return Error::OK; + if (repeated->IsIncomplete()) { + return Error::kStop; + } + return Error::kOk; } InputBuilder::Error InputBuilder::ResolveIndexExpr( const ResolutionContext& ctx, const proto::IndexExpr& expr) { AddString(base::NumberToString(ctx.offset + expr.one_based())); - return Error::OK; + return Error::kOk; } InputBuilder::Error InputBuilder::ResolveControlToken( @@ -208,24 +231,27 @@ AddToken(ml::Token::kEnd); break; default: - return Error::FAILED; + return Error::kFailed; } - return Error::OK; + return Error::kOk; } InputBuilder::Error InputBuilder::ResolveMediaField( const ResolutionContext& ctx, proto::MediaField field) { + if (ctx.view.IsPending(field.proto_field())) { + return Error::kStop; + } MultimodalType mtype = ctx.view.GetMultimodalType(field.proto_field()); switch (mtype) { case MultimodalType::kAudio: out_->pieces.emplace_back(*ctx.view.GetAudio(field.proto_field())); - return Error::OK; + return Error::kOk; case MultimodalType::kImage: out_->pieces.emplace_back(*ctx.view.GetImage(field.proto_field())); - return Error::OK; + return Error::kOk; case MultimodalType::kNone: - return Error::OK; + return Error::kOk; } } @@ -235,7 +261,7 @@ switch (candidate.arg_case()) { case proto::StringArg::kRawString: AddString(candidate.raw_string()); - return Error::OK; + return Error::kOk; case proto::StringArg::kProtoField: return ResolveProtoField(ctx, candidate.proto_field()); case proto::StringArg::kRangeExpr: @@ -248,7 +274,7 @@ return ResolveMediaField(ctx, candidate.media_field()); case proto::StringArg::ARG_NOT_SET: DVLOG(1) << "StringArg is incomplete."; - return Error::FAILED; + return Error::kFailed; } } @@ -256,18 +282,28 @@ const ResolutionContext& ctx, const proto::StringSubstitution& arg) { for (const auto& candidate : arg.candidates()) { - if (DoConditionsApply(ctx, candidate.conditions())) { - return ResolveStringArg(ctx, candidate); + switch (DoConditionsApply(ctx, candidate.conditions())) { + case ConditionResult::kFalse: + continue; + case ConditionResult::kStop: + return Error::kStop; + case ConditionResult::kTrue: + return ResolveStringArg(ctx, candidate); } } - return Error::OK; + return Error::kOk; } InputBuilder::Error InputBuilder::ResolveSubstitutedString( const ResolutionContext& ctx, const proto::SubstitutedString& substitution) { - if (!DoConditionsApply(ctx, substitution.conditions())) { - return Error::OK; + switch (DoConditionsApply(ctx, substitution.conditions())) { + case ConditionResult::kFalse: + return Error::kOk; + case ConditionResult::kStop: + return Error::kStop; + case ConditionResult::kTrue: + break; } if (substitution.should_ignore_input_context()) { should_ignore_input_context_ = true; @@ -286,15 +322,15 @@ } if (token != "%s") { DVLOG(1) << "Invalid Token"; - return Error::FAILED; // Invalid token + return Error::kFailed; // Invalid token } if (substitution_idx >= substitution.substitutions_size()) { DVLOG(1) << "Too many substitutions"; - return Error::FAILED; + return Error::kFailed; } Error error = ResolveSubstitution(ctx, substitution.substitutions(substitution_idx)); - if (error != Error::OK) { + if (error != Error::kOk) { return error; } ++substitution_idx; @@ -302,9 +338,9 @@ AddString(templ.substr(template_idx, std::string_view::npos)); if (substitution_idx != substitution.substitutions_size()) { DVLOG(1) << "Missing substitutions"; - return Error::FAILED; + return Error::kFailed; } - return Error::OK; + return Error::kOk; } // Placeholder strings for a control token in MQLS logs / display. @@ -359,7 +395,10 @@ for (const auto& substitution : config_substitutions) { auto error = builder.ResolveSubstitutedString(ResolutionContext{request, 0}, substitution); - if (error != InputBuilder::Error::OK) { + if (error == InputBuilder::Error::kStop) { + break; + } + if (error != InputBuilder::Error::kOk) { return std::nullopt; } }
diff --git a/components/optimization_guide/core/model_execution/substitution_unittest.cc b/components/optimization_guide/core/model_execution/substitution_unittest.cc index d3c937a..e455b0a9 100644 --- a/components/optimization_guide/core/model_execution/substitution_unittest.cc +++ b/components/optimization_guide/core/model_execution/substitution_unittest.cc
@@ -14,6 +14,7 @@ #include "components/optimization_guide/core/model_execution/on_device_model_execution_proto_descriptors.h" #include "components/optimization_guide/core/model_execution/test/feature_config_builder.h" #include "components/optimization_guide/core/model_execution/test/request_builder.h" +#include "components/optimization_guide/core/model_execution/test/substitution_builder.h" #include "components/optimization_guide/proto/descriptors.pb.h" #include "components/optimization_guide/proto/features/compose.pb.h" #include "components/optimization_guide/proto/features/example_for_testing.pb.h" @@ -584,6 +585,140 @@ EXPECT_EQ(result->input->pieces.size(), 0u); } +Substitutions BlockCheck(proto::StringSubstitution sub) { + Substitutions result; + auto* expr1 = result.Add(); + expr1->set_string_template("%s for...%s...%s"); + expr1->add_substitutions()->add_candidates()->set_raw_string("waiting"); + *expr1->add_substitutions() = std::move(sub); + expr1->add_substitutions()->add_candidates()->set_raw_string("complete"); + return result; +} + +TEST_F(SubstitutionTest, BlockOnPendingField) { + using RequestProto = ::optimization_guide::proto::ExampleForTestingRequest; + + Substitutions substitutions = BlockCheck( + Always(StringArg(ProtoField({RequestProto::kStringValueFieldNumber})))); + + MultimodalMessage request{proto::ExampleForTestingRequest()}; + request.edit().MarkPending(RequestProto::kStringValueFieldNumber); + + std::optional<SubstitutionResult> result = + CreateSubstitutions(request.read(), substitutions); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->ToString(), "waiting for..."); +} + +TEST_F(SubstitutionTest, BlockOnPendingMediaField) { + using RequestProto = ::optimization_guide::proto::ExampleForTestingRequest; + using Msg = ::optimization_guide::proto::ExampleForTestingMessage; + + Substitutions substitutions = BlockCheck(Always(MediaFieldArg( + {RequestProto::kNested1FieldNumber, Msg::kMediaFieldNumber}))); + + MultimodalMessage request{proto::ExampleForTestingRequest()}; + request.edit().MarkPending(RequestProto::kNested1FieldNumber); + + std::optional<SubstitutionResult> result = + CreateSubstitutions(request.read(), substitutions); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->ToString(), "waiting for..."); +} + +TEST_F(SubstitutionTest, BlockOnPendingFieldInCondition) { + using RequestProto = ::optimization_guide::proto::ExampleForTestingRequest; + using Msg = ::optimization_guide::proto::ExampleForTestingMessage; + + Substitutions substitutions = BlockCheck( + Candidates({EnumCase(ProtoField({RequestProto::kEnumValueFieldNumber}), + Msg::VALUE0, StringArg("maybe_value")), + StringArg("fallback_value")})); + + MultimodalMessage request{proto::ExampleForTestingRequest()}; + request.edit().MarkPending(RequestProto::kEnumValueFieldNumber); + + std::optional<SubstitutionResult> result = + CreateSubstitutions(request.read(), substitutions); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->ToString(), "waiting for..."); +} + +Substitutions BlockCheckRepeatedSubstitution() { + using RequestProto = ::optimization_guide::proto::ExampleForTestingRequest; + using Msg = ::optimization_guide::proto::ExampleForTestingMessage; + return BlockCheck(Always(RangeExprArg( + ProtoField({RequestProto::kRepeatedFieldFieldNumber}), + Just(StringArg(ProtoField({Msg::kStringValueFieldNumber})))))); +} + +MultimodalMessage RepeatedMessage() { + using RequestProto = ::optimization_guide::proto::ExampleForTestingRequest; + using Msg = ::optimization_guide::proto::ExampleForTestingMessage; + MultimodalMessage msg{proto::ExampleForTestingRequest()}; + msg.edit() + .MutableRepeatedField(RequestProto::kRepeatedFieldFieldNumber) + .Add() + .Set(Msg::kStringValueFieldNumber, "A"); + msg.edit() + .MutableRepeatedField(RequestProto::kRepeatedFieldFieldNumber) + .Add() + .Set(Msg::kStringValueFieldNumber, "B"); + msg.edit() + .MutableRepeatedField(RequestProto::kRepeatedFieldFieldNumber) + .Add() + .Set(Msg::kStringValueFieldNumber, "C"); + return msg; +} + +TEST_F(SubstitutionTest, BlockOnPendingRepeated) { + using RequestProto = ::optimization_guide::proto::ExampleForTestingRequest; + + Substitutions substitutions = BlockCheckRepeatedSubstitution(); + + MultimodalMessage request = RepeatedMessage(); + request.edit().MarkPending(RequestProto::kRepeatedFieldFieldNumber); + + std::optional<SubstitutionResult> result = + CreateSubstitutions(request.read(), substitutions); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->ToString(), "waiting for..."); +} + +TEST_F(SubstitutionTest, BlockOnPendingFieldInRepeated) { + using RequestProto = ::optimization_guide::proto::ExampleForTestingRequest; + using Msg = ::optimization_guide::proto::ExampleForTestingMessage; + + Substitutions substitutions = BlockCheckRepeatedSubstitution(); + + MultimodalMessage request = RepeatedMessage(); + request.edit() + .MutableRepeatedField(RequestProto::kRepeatedFieldFieldNumber) + .Get(1) + .MarkPending(Msg::kStringValueFieldNumber); + + std::optional<SubstitutionResult> result = + CreateSubstitutions(request.read(), substitutions); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->ToString(), "waiting for...A"); +} + +TEST_F(SubstitutionTest, BlockOnIncompleteRepeated) { + using RequestProto = ::optimization_guide::proto::ExampleForTestingRequest; + + Substitutions substitutions = BlockCheckRepeatedSubstitution(); + + MultimodalMessage request = RepeatedMessage(); + request.edit() + .MutableRepeatedField(RequestProto::kRepeatedFieldFieldNumber) + .MarkIncomplete(true); + + std::optional<SubstitutionResult> result = + CreateSubstitutions(request.read(), substitutions); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->ToString(), "waiting for...ABC"); +} + } // namespace } // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/test/feature_config_builder.cc b/components/optimization_guide/core/model_execution/test/feature_config_builder.cc index 401c6b7..d35fa2d 100644 --- a/components/optimization_guide/core/model_execution/test/feature_config_builder.cc +++ b/components/optimization_guide/core/model_execution/test/feature_config_builder.cc
@@ -8,6 +8,7 @@ #include "base/strings/string_util.h" #include "components/optimization_guide/core/model_execution/feature_keys.h" +#include "components/optimization_guide/core/model_execution/test/substitution_builder.h" #include "components/optimization_guide/proto/descriptors.pb.h" #include "components/optimization_guide/proto/features/compose.pb.h" #include "components/optimization_guide/proto/features/example_for_testing.pb.h" @@ -32,14 +33,6 @@ return result; } -proto::ProtoField ProtoField(std::initializer_list<int32_t> tags) { - proto::ProtoField f; - for (int32_t tag : tags) { - f.add_proto_descriptors()->set_tag_number(tag); - } - return f; -} - proto::ProtoField PageUrlField() { return ProtoField({3, 1}); } @@ -60,32 +53,6 @@ return ProtoField({1}); } -proto::RangeExpr RangeExpr(proto::ProtoField repeated_field, - proto::SubstitutedString expr) { - proto::RangeExpr result; - *result.mutable_proto_field() = std::move(repeated_field); - *result.mutable_expr() = std::move(expr); - return result; -} - -proto::SubstitutedString FieldSubstitution(const std::string& tmpl, - proto::ProtoField field) { - proto::SubstitutedString result; - result.set_string_template(tmpl); - *result.add_substitutions()->add_candidates()->mutable_proto_field() = - std::move(field); - return result; -} - -proto::SubstitutedString ForEachSubstitution(proto::ProtoField repeated_field, - proto::SubstitutedString expr) { - proto::SubstitutedString result; - result.set_string_template("%s"); - *result.add_substitutions()->add_candidates()->mutable_range_expr() = - RangeExpr(std::move(repeated_field), std::move(expr)); - return result; -} - proto::SubstitutedString PageUrlSubstitution() { return FieldSubstitution("url: %s", PageUrlField()); }
diff --git a/components/optimization_guide/core/model_execution/test/feature_config_builder.h b/components/optimization_guide/core/model_execution/test/feature_config_builder.h index a86a430..fc0bba98 100644 --- a/components/optimization_guide/core/model_execution/test/feature_config_builder.h +++ b/components/optimization_guide/core/model_execution/test/feature_config_builder.h
@@ -9,6 +9,7 @@ #include <optional> #include <string> +#include "components/optimization_guide/core/model_execution/test/substitution_builder.h" #include "components/optimization_guide/proto/descriptors.pb.h" #include "components/optimization_guide/proto/features/example_for_testing.pb.h" #include "components/optimization_guide/proto/on_device_model_execution_config.pb.h" @@ -26,9 +27,6 @@ // FakeOnDeviceModel::ClassifyTextSafety. proto::SafetyCategoryThreshold RequireReasonable(); -// Construct a ProtoField with the given tags. -proto::ProtoField ProtoField(std::initializer_list<int32_t> tags); - // Reference ComposeRequest::page_metadata.page_url proto::ProtoField PageUrlField(); @@ -44,18 +42,6 @@ // Reference StringValue::value proto::ProtoField StringValueField(); -// Construct a RangeExpr. -proto::RangeExpr RangeExpr(proto::ProtoField repeated_field, - proto::SubstitutedString expr); - -// Make Substitution putting 'field' in 'tmpl'. -proto::SubstitutedString FieldSubstitution(const std::string& tmpl, - proto::ProtoField field); - -// Make a Substitution that formats a repeated field. -proto::SubstitutedString ForEachSubstitution(proto::ProtoField repeated_field, - proto::SubstitutedString expr); - // Make a template for "url: {page_url}". proto::SubstitutedString PageUrlSubstitution(); @@ -110,18 +96,6 @@ return cfg; } -inline auto Int32Proto(int32_t value) { - proto::Value v; - v.set_int32_value(value); - return v; -} - -inline auto Int64Proto(int64_t value) { - proto::Value v; - v.set_int64_value(value); - return v; -} - // Construct an InputConfig that formats a proto::ExampleForTestingRequest. proto::OnDeviceModelExecutionInputConfig TestInputConfig( proto::SubstitutedString context_template,
diff --git a/components/optimization_guide/core/model_execution/test/substitution_builder.cc b/components/optimization_guide/core/model_execution/test/substitution_builder.cc new file mode 100644 index 0000000..3ed0bcd --- /dev/null +++ b/components/optimization_guide/core/model_execution/test/substitution_builder.cc
@@ -0,0 +1,64 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/optimization_guide/core/model_execution/test/substitution_builder.h" + +#include "components/optimization_guide/proto/substitution.pb.h" + +namespace optimization_guide { + +proto::ProtoField ProtoField(std::initializer_list<int32_t> tags) { + proto::ProtoField f; + for (int32_t tag : tags) { + f.add_proto_descriptors()->set_tag_number(tag); + } + return f; +} + +proto::StringSubstitution Candidates( + std::initializer_list<proto::StringArg> candidates) { + proto::StringSubstitution result; + for (auto& candidate : candidates) { + *result.add_candidates() = candidate; + } + return result; +} + +proto::ConditionList All(std::initializer_list<proto::Condition> conditions) { + proto::ConditionList result; + result.set_condition_evaluation_type(proto::CONDITION_EVALUATION_TYPE_AND); + for (auto& condition : conditions) { + *result.add_conditions() = condition; + } + return result; +} + +proto::ConditionList Any(std::initializer_list<proto::Condition> conditions) { + proto::ConditionList result; + result.set_condition_evaluation_type(proto::CONDITION_EVALUATION_TYPE_OR); + for (auto& condition : conditions) { + *result.add_conditions() = condition; + } + return result; +} + +proto::SubstitutedString FieldSubstitution(const std::string& tmpl, + proto::ProtoField field) { + proto::SubstitutedString result; + result.set_string_template(tmpl); + *result.add_substitutions()->add_candidates()->mutable_proto_field() = + std::move(field); + return result; +} + +proto::SubstitutedString ForEachSubstitution(proto::ProtoField repeated_field, + proto::SubstitutedString expr) { + proto::SubstitutedString result; + result.set_string_template("%s"); + *result.add_substitutions()->add_candidates() = + RangeExprArg(std::move(repeated_field), std::move(expr)); + return result; +} + +} // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/test/substitution_builder.h b/components/optimization_guide/core/model_execution/test/substitution_builder.h new file mode 100644 index 0000000..0ca40ec --- /dev/null +++ b/components/optimization_guide/core/model_execution/test/substitution_builder.h
@@ -0,0 +1,160 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_TEST_SUBSTITUTION_BUILDER_H_ +#define COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_TEST_SUBSTITUTION_BUILDER_H_ + +#include <initializer_list> +#include <optional> +#include <string> +#include <type_traits> + +#include "components/optimization_guide/proto/descriptors.pb.h" +#include "components/optimization_guide/proto/substitution.pb.h" + +namespace optimization_guide { + +// Construct a ProtoField with the given tags. +proto::ProtoField ProtoField(std::initializer_list<int32_t> tags); + +inline proto::Value StrProto(std::string value) { + proto::Value v; + v.set_string_value(std::move(value)); + return v; +} + +inline proto::Value Int32Proto(int32_t value) { + proto::Value v; + v.set_int32_value(value); + return v; +} + +inline proto::Value Int64Proto(int64_t value) { + proto::Value v; + v.set_int64_value(value); + return v; +} + +inline proto::Value BoolProto(bool value) { + proto::Value v; + v.set_boolean_value(value); + return v; +} + +inline proto::Condition Eq(proto::ProtoField field, proto::Value value) { + proto::Condition result; + *result.mutable_proto_field() = std::move(field); + result.set_operator_type(proto::OPERATOR_TYPE_EQUAL_TO); + *result.mutable_value() = std::move(value); + return result; +} + +inline proto::Condition Neq(proto::ProtoField field, proto::Value value) { + proto::Condition result; + *result.mutable_proto_field() = std::move(field); + result.set_operator_type(proto::OPERATOR_TYPE_NOT_EQUAL_TO); + *result.mutable_value() = std::move(value); + return result; +} + +// Construct AND-joined conditions. +proto::ConditionList All(std::initializer_list<proto::Condition>); + +// Construct OR-joined conditions. +proto::ConditionList Any(std::initializer_list<proto::Condition>); + +inline proto::StringArg StringArg(std::string raw_string) { + proto::StringArg result; + result.set_raw_string(std::move(raw_string)); + return result; +} + +inline proto::StringArg StringArg(proto::ProtoField field) { + proto::StringArg result; + *result.mutable_proto_field() = std::move(field); + return result; +} + +inline proto::StringArg StringArg(proto::ControlToken token) { + proto::StringArg result; + result.set_control_token(token); + return result; +} + +inline proto::StringArg RangeExprArg(proto::ProtoField repeated_field, + proto::SubstitutedString expr) { + proto::StringArg result; + *result.mutable_range_expr()->mutable_proto_field() = + std::move(repeated_field); + *result.mutable_range_expr()->mutable_expr() = std::move(expr); + return result; +} + +// Construct an IndexExpr in a StringArg. +inline proto::StringArg IndexExprArg(bool one_based) { + proto::StringArg result; + result.mutable_index_expr()->set_one_based(one_based); + return result; +} + +// Construct an MediaField in a StringArg. +inline proto::StringArg MediaFieldArg(std::initializer_list<int32_t> tags) { + proto::StringArg result; + *result.mutable_media_field()->mutable_proto_field() = ProtoField(tags); + return result; +} + +// Add a condition to a StringArg. +inline proto::StringArg If(proto::ConditionList conditions, + proto::StringArg arg) { + *arg.mutable_conditions() = std::move(conditions); + return arg; +} + +// If(field=value) -> Arg +template <typename Enum> + requires(std::is_enum_v<Enum>) +proto::StringArg EnumCase(proto::ProtoField field, + Enum value, + proto::StringArg arg) { + return If(All({Eq(std::move(field), Int32Proto(value))}), std::move(arg)); +} + +// A StringSubstitution with only one candidate. +inline proto::StringSubstitution Always(proto::StringArg arg) { + proto::StringSubstitution result; + *result.add_candidates() = arg; + return result; +} + +// A SubstitutedString that contains one element. +inline proto::SubstitutedString Just(proto::StringSubstitution sub) { + proto::SubstitutedString result; + result.set_string_template("%s"); + *result.add_substitutions() = std::move(sub); + return result; +} + +inline proto::SubstitutedString Just(proto::StringArg arg) { + return Just(Always(arg)); +} + +// Constructs a StringSubstitution object with provided candidates. +proto::StringSubstitution Candidates( + std::initializer_list<proto::StringArg> candidates); + +proto::SubstitutedString Concatenated( + std::initializer_list<proto::StringSubstitution> substitutions); + +// Make Substitution putting 'field' in 'tmpl'. +proto::SubstitutedString FieldSubstitution(const std::string& tmpl, + proto::ProtoField field); + +// Make a Substitution that formats a repeated field. +proto::SubstitutedString ForEachSubstitution(proto::ProtoField repeated_field, + proto::SubstitutedString expr); + +} // namespace optimization_guide + +#endif // COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_TEST_SUBSTITUTION_BUILDER_H_
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal index 6032e32..3633af32 160000 --- a/components/optimization_guide/internal +++ b/components/optimization_guide/internal
@@ -1 +1 @@ -Subproject commit 6032e32fa8b82048023e47709f1e5a22dd277791 +Subproject commit 3633af3203b28054ee2d0aafd69b7a687dce4d1c
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentApp.java b/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentApp.java index 5213b3482..f7ae74c 100644 --- a/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentApp.java +++ b/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentApp.java
@@ -209,8 +209,10 @@ Intent isReadyToPayIntent = WebPaymentIntentHelper.createIsReadyToPayIntent( - /* packageName= */ mPackageName, - /* serviceName= */ mIsReadyToPayServiceName, + /* callerPackageName= */ ContextUtils.getApplicationContext() + .getPackageName(), + /* paymentAppPackageName= */ mPackageName, + /* paymentAppServiceName= */ mIsReadyToPayServiceName, removeUrlScheme(origin), removeUrlScheme(iframeOrigin), certificateChain, @@ -389,7 +391,10 @@ new PaymentDetailsUpdateConnection( ContextUtils.getApplicationContext(), WebPaymentIntentHelper.createPaymentDetailsUpdateServiceIntent( - mPackageName, mPaymentDetailsUpdateServiceName), + /* callerPackageName= */ ContextUtils.getApplicationContext() + .getPackageName(), + mPackageName, + mPaymentDetailsUpdateServiceName), new PaymentDetailsUpdateService().getBinder()); mPaymentDetailsUpdateConnection.connectToService(); }
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/intent/WebPaymentIntentHelper.java b/components/payments/content/android/java/src/org/chromium/components/payments/intent/WebPaymentIntentHelper.java index b8171373..8f8dc46 100644 --- a/components/payments/content/android/java/src/org/chromium/components/payments/intent/WebPaymentIntentHelper.java +++ b/components/payments/content/android/java/src/org/chromium/components/payments/intent/WebPaymentIntentHelper.java
@@ -64,6 +64,7 @@ public static final String EXTRA_PAYMENT_OPTIONS_REQUEST_SHIPPING = "requestShipping"; public static final String EXTRA_PAYMENT_OPTIONS_SHIPPING_TYPE = "shippingType"; public static final String EXTRA_SHIPPING_OPTIONS = "shippingOptions"; + public static final String EXTRA_CALLER_PACKAGE_NAME = "packageName"; // Deprecated parameters sent to the payment app for backward compatibility. // TODO(crbug.com/40849135): Remove these parameters. @@ -238,36 +239,36 @@ * Create an intent to invoke a native payment app. This method throws IllegalArgumentException * for invalid arguments. * - * @param packageName The name of the package of the payment app. Only non-empty string is - * allowed. - * @param activityName The name of the payment activity in the payment app. Only non-empty - * string is allowed. + * @param paymentAppPackageName The name of the package of the payment app. Only non-empty + * string is allowed. + * @param paymentAppActivityName The name of the payment activity in the payment app. Only + * non-empty string is allowed. * @param id The unique identifier of the PaymentRequest. Only non-empty string is allowed. * @param merchantName The name of the merchant. Cannot be null.. * @param schemelessOrigin The schemeless origin of this merchant. Only non-empty string is - * allowed. + * allowed. * @param schemelessIframeOrigin The schemeless origin of the iframe that invoked - * PaymentRequest. Only non-empty string is allowed. + * PaymentRequest. Only non-empty string is allowed. * @param certificateChain The site certificate chain of the merchant. Can be null when - * ANDROID_PAYMENT_INTENTS_OMIT_DEPRECATED_PARAMETERS is enabled or for localhost or - * local file, which are secure contexts without SSL. Each byte array cannot be null. + * ANDROID_PAYMENT_INTENTS_OMIT_DEPRECATED_PARAMETERS is enabled or for localhost or local + * file, which are secure contexts without SSL. Each byte array cannot be null. * @param methodDataMap The payment-method specific data for all applicable payment methods, - * e.g., whether the app should be invoked in test or production, a merchant identifier, - * or a public key. The map and its values cannot be null. The map should have at - * least one entry. + * e.g., whether the app should be invoked in test or production, a merchant identifier, or + * a public key. The map and its values cannot be null. The map should have at least one + * entry. * @param total The total amount. Cannot be null.. * @param displayItems The shopping cart items. OK to be null. * @param modifiers The relevant payment details modifiers. OK to be null. * @param paymentOptions The relevant merchant requested payment options. OK to be null. * @param shippingOptions Merchant specified available shipping options. Should be non-empty - * when paymentOptions.requestShipping is true. + * when paymentOptions.requestShipping is true. * @param removeDeprecatedFields Whether the deprecated fields should be omitted from the - * intent. + * intent. * @return The intent to invoke the payment app. */ public static Intent createPayIntent( - String packageName, - String activityName, + String paymentAppPackageName, + String paymentAppActivityName, String id, String merchantName, String schemelessOrigin, @@ -281,9 +282,9 @@ @Nullable List<PaymentShippingOption> shippingOptions, boolean removeDeprecatedFields) { Intent payIntent = new Intent(); - checkStringNotEmpty(activityName, "activityName"); - checkStringNotEmpty(packageName, "packageName"); - payIntent.setClassName(packageName, activityName); + checkStringNotEmpty(paymentAppActivityName, "paymentAppActivityName"); + checkStringNotEmpty(paymentAppPackageName, "paymentAppPackageName"); + payIntent.setClassName(paymentAppPackageName, paymentAppActivityName); payIntent.setAction(ACTION_PAY); payIntent.putExtras( buildPayIntentExtras( @@ -306,27 +307,38 @@ * Create an intent for the service that dynamically updates the payment details (e.g., total * price) based on user's payment method, shipping address, or shipping option. * - * @param packageName The name of the package of the payment app. Only non-empty string is - * allowed. - * @param serviceName The name of the service. Only non-empty string is allowed. + * @param callerPackageName The name of the package of the calling code, e.g., + * "com.android.chrome". + * @param paymentAppPackageName The name of the package of the payment app. Only non-empty + * string is allowed. + * @param paymentAppServiceName The name of the service. Only non-empty string is allowed. * @return The intent to invoke the service. */ public static Intent createPaymentDetailsUpdateServiceIntent( - String packageName, String serviceName) { + String callerPackageName, String paymentAppPackageName, String paymentAppServiceName) { + checkStringNotEmpty(callerPackageName, "callerPackageName"); + checkStringNotEmpty(paymentAppPackageName, "paymentAppPackageName"); + checkStringNotEmpty(paymentAppServiceName, "paymentAppServiceName"); + Intent intent = new Intent(); - checkStringNotEmpty(packageName, "packageName"); - checkStringNotEmpty(serviceName, "serviceName"); - intent.setClassName(packageName, serviceName); + intent.setClassName(paymentAppPackageName, paymentAppServiceName); intent.setAction(ACTION_UPDATE_PAYMENT_DETAILS); + + Bundle extras = new Bundle(); + extras.putString(EXTRA_CALLER_PACKAGE_NAME, callerPackageName); + intent.putExtras(extras); + return intent; } /** * Create an intent to invoke a service that can answer "is ready to pay" query. * - * @param packageName The name of the package of the payment app. Only non-empty string is - * allowed. - * @param serviceName The name of the service. Only non-empty string is allowed. + * @param callerPackageName The name of the package of the calling code, e.g., + * "com.android.chrome". + * @param paymentAppPackageName The name of the package of the payment app. Only non-empty + * string is allowed. + * @param paymentAppServiceName The name of the service. Only non-empty string is allowed. * @param schemelessOrigin The schemeless origin of this merchant. Only non-empty string is * allowed. * @param schemelessIframeOrigin The schemeless origin of the iframe that invoked @@ -344,8 +356,9 @@ * @return The intent to invoke the service. */ public static Intent createIsReadyToPayIntent( - String packageName, - String serviceName, + String callerPackageName, + String paymentAppPackageName, + String paymentAppServiceName, String schemelessOrigin, String schemelessIframeOrigin, byte @Nullable [][] certificateChain, @@ -353,11 +366,13 @@ boolean clearIdFields, boolean removeDeprecatedFields) { Intent isReadyToPayIntent = new Intent(); - checkStringNotEmpty(serviceName, "serviceName"); - checkStringNotEmpty(packageName, "packageName"); - isReadyToPayIntent.setClassName(packageName, serviceName); + checkStringNotEmpty(callerPackageName, "callerPackageName"); + checkStringNotEmpty(paymentAppServiceName, "paymentAppServiceName"); + checkStringNotEmpty(paymentAppPackageName, "paymentAppPackageName"); + isReadyToPayIntent.setClassName(paymentAppPackageName, paymentAppServiceName); isReadyToPayIntent.setAction(ACTION_IS_READY_TO_PAY); Bundle extras = new Bundle(); + extras.putString(EXTRA_CALLER_PACKAGE_NAME, callerPackageName); if (!clearIdFields) { addCommonExtrasWithIdentity( schemelessOrigin, @@ -401,6 +416,7 @@ @Nullable List<PaymentShippingOption> shippingOptions, boolean removeDeprecatedFields) { Bundle extras = new Bundle(); + checkStringNotEmpty(id, "id"); extras.putString(EXTRA_PAYMENT_REQUEST_ID, id);
diff --git a/content/browser/indexed_db/instance/bucket_context.cc b/content/browser/indexed_db/instance/bucket_context.cc index e517d5c..92bc6ac 100644 --- a/content/browser/indexed_db/instance/bucket_context.cc +++ b/content/browser/indexed_db/instance/bucket_context.cc
@@ -26,6 +26,7 @@ #include "base/check_op.h" #include "base/containers/contains.h" #include "base/containers/map_util.h" +#include "base/feature_list.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/functional/bind.h" @@ -67,6 +68,7 @@ #include "content/browser/indexed_db/instance/database_callbacks.h" #include "content/browser/indexed_db/instance/leveldb/backing_store.h" #include "content/browser/indexed_db/instance/pending_connection.h" +#include "content/browser/indexed_db/instance/sqlite/backing_store_impl.h" #include "content/browser/indexed_db/status.h" #include "mojo/public/cpp/bindings/pending_associated_receiver.h" #include "mojo/public/cpp/bindings/pending_associated_remote.h" @@ -194,6 +196,11 @@ } // namespace +// TODO(crbug.com/40253999): Move to blink when needed there. +BASE_FEATURE(kSqliteBackingStore, + "IdbSqliteBackingStore", + base::FEATURE_DISABLED_BY_DEFAULT); + BucketContext::Delegate::Delegate() : on_ready_for_destruction(base::DoNothing()), on_receiver_bounced(base::DoNothing()), @@ -876,6 +883,11 @@ return sanitized_message; } +bool BucketContext::ShouldUseSqliteBackingStore() { + // Additional checks may be added subsequently. + return base::FeatureList::IsEnabled(kSqliteBackingStore); +} + void BucketContext::HandleBackingStoreCorruption( const std::string& error_message) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -962,9 +974,11 @@ for (int i = 0; i < kNumOpenTries; ++i) { const bool is_first_attempt = i == 0; std::tie(backing_store, status, data_loss_info, disk_full) = - level_db::BackingStore::OpenAndVerify( - *this, data_path_, database_path, blob_path, lock_manager.get(), - is_first_attempt, create_if_missing); + ShouldUseSqliteBackingStore() + ? sqlite::BackingStoreImpl::OpenAndVerify(data_path_) + : level_db::BackingStore::OpenAndVerify( + *this, data_path_, database_path, blob_path, + lock_manager.get(), is_first_attempt, create_if_missing); if (is_first_attempt) [[likely]] { first_try_status = status; }
diff --git a/content/browser/indexed_db/instance/bucket_context.h b/content/browser/indexed_db/instance/bucket_context.h index 15c5034..6586d31 100644 --- a/content/browser/indexed_db/instance/bucket_context.h +++ b/content/browser/indexed_db/instance/bucket_context.h
@@ -382,11 +382,7 @@ std::string SanitizeErrorMessage(const std::string& message); - // This only exists to ease the transition to a swappable backing store. It - // should be removed. - level_db::BackingStore* leveldb_backing_store() { - return reinterpret_cast<level_db::BackingStore*>(backing_store_.get()); - } + bool ShouldUseSqliteBackingStore(); SEQUENCE_CHECKER(sequence_checker_);
diff --git a/content/gpu/BUILD.gn b/content/gpu/BUILD.gn index 5a5b7c3e..c6a90ab 100644 --- a/content/gpu/BUILD.gn +++ b/content/gpu/BUILD.gn
@@ -99,6 +99,7 @@ deps += [ "//components/tracing:graphics_provider", "//media", + "//sandbox/linux:sandbox_services", ] }
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc index cadb96f..73be627 100644 --- a/content/gpu/gpu_main.cc +++ b/content/gpu/gpu_main.cc
@@ -12,6 +12,7 @@ #include "base/allocator/partition_alloc_support.h" #include "base/check.h" #include "base/command_line.h" +#include "base/files/scoped_file.h" #include "base/feature_list.h" #include "base/functional/bind.h" #include "base/memory/raw_ptr.h" @@ -85,8 +86,13 @@ #if BUILDFLAG(IS_ANDROID) #include "base/android/meminfo_dump_provider.h" +#include "base/posix/eintr_wrapper.h" #include "base/trace_event/memory_dump_manager.h" #include "components/tracing/common/graphics_memory_dump_provider_android.h" +#include "sandbox/linux/services/thread_helpers.h" // nogncheck +#include "sandbox/policy/features.h" +#include "sandbox/policy/linux/landlock_gpu_policy_android.h" +#include "sandbox/policy/sandbox_type.h" #endif #if BUILDFLAG(IS_WIN) @@ -123,6 +129,8 @@ bool StartSandboxLinux(gpu::GpuWatchdogThread*, const gpu::GPUInfo*, const gpu::GpuPreferences&); +#elif BUILDFLAG(IS_ANDROID) +bool StartSandboxAndroid(gpu::GpuWatchdogThread*); #elif BUILDFLAG(IS_WIN) bool StartSandboxWindows(const sandbox::SandboxInterfaceInfo*); #endif @@ -185,6 +193,12 @@ return StartSandboxWindows(sandbox_info_); #elif BUILDFLAG(IS_MAC) return sandbox::Seatbelt::IsSandboxed(); +#elif BUILDFLAG(IS_ANDROID) + if (base::FeatureList::IsEnabled( + sandbox::policy::features::kAndroidGpuSandbox)) { + return StartSandboxAndroid(watchdog_thread); + } + return false; #else return false; #endif @@ -523,6 +537,29 @@ } #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#if BUILDFLAG(IS_ANDROID) +bool StartSandboxAndroid(gpu::GpuWatchdogThread* watchdog_thread) { + if (watchdog_thread) { + // Stop the watchdog thread temporarily. + base::ScopedFD proc_fd( + HANDLE_EINTR(open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC))); + + sandbox::ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.get(), + watchdog_thread); + } + + bool res = sandbox::landlock::ApplyLandlock( + sandbox::policy::SandboxTypeFromCommandLine( + *base::CommandLine::ForCurrentProcess())); + + if (watchdog_thread) { + watchdog_thread->Start(); + } + + return res; +} +#endif // BUILDFLAG(IS_ANDROID) + #if BUILDFLAG(IS_WIN) bool StartSandboxWindows(const sandbox::SandboxInterfaceInfo* sandbox_info) { GPU_STARTUP_TRACE_EVENT("Lower token");
diff --git a/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt index e63345d..44764738 100644 --- a/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt
@@ -117,6 +117,10 @@ # Longstanding flaking test - page reported failure crbug.com/40877266 [ android android-shield-android-tv ] ExpectedColor_MediaRecorderFromVideoElement [ RetryOnFailure ] +# Flakes on M1 machines when upgrading to MacOS 15.4. +crbug.com/416712848 [ sequoia apple-angle-metal-renderer:-apple-m1 ] ExpectedColor_MediaRecorderFrom2DCanvas [ RetryOnFailure ] +crbug.com/416712848 [ sequoia apple-angle-metal-renderer:-apple-m1 ] ExpectedColor_maps [ RetryOnFailure ] + ####################################################################### # Automated Entries After This Point - Do Not Manually Add Below Here #
diff --git a/extensions/browser/service_worker/service_worker_task_queue.cc b/extensions/browser/service_worker/service_worker_task_queue.cc index 0c296f5b..a9aa97f2 100644 --- a/extensions/browser/service_worker/service_worker_task_queue.cc +++ b/extensions/browser/service_worker/service_worker_task_queue.cc
@@ -287,19 +287,8 @@ service_worker_version_id, thread_id}; WorkerState* worker_state = GetWorkerState(context_id); DCHECK(worker_state); - // If |worker_state| had a worker running previously, for which we didn't - // see DidStopServiceWorkerContext notification (typically happens on render - // process shutdown), then we'd preserve stale state in |renderer_state_|. - // - // This isn't a problem because the next browser process readiness - // (DidStartWorkerForScope) or the next renderer process readiness - // (DidStartServiceWorkerContext) will clear the state, whichever happens - // first. - // - // TODO(lazyboy): Update the renderer state in RenderProcessExited() and - // uncomment the following DCHECK: - // DCHECK_NE(RendererState::kActive, worker_state->renderer_state_) - // << "Worker already started"; + DCHECK_NE(RendererState::kActive, worker_state->renderer_state()) + << "Worker already started"; worker_state->SetWorkerId(worker_id, ProcessManager::Get(browser_context_)); worker_state->SetRendererState(RendererState::kActive); @@ -319,9 +308,7 @@ WorkerState* worker_state = GetWorkerState(context_id); // If the extension is still activated, worker state should still exist. CHECK(worker_state); - - worker_state->SetRendererState(RendererState::kNotActive); - worker_state->ResetWorkerId(); + worker_state->Reset(); } void ServiceWorkerTaskQueue::DidStopServiceWorkerContext( @@ -353,8 +340,7 @@ } DCHECK_NE(RendererState::kNotActive, worker_state->renderer_state()); - worker_state->SetRendererState(RendererState::kNotActive); - worker_state->ResetWorkerId(); + worker_state->Reset(); if (g_test_observer) { g_test_observer->DidStopServiceWorkerContext(extension_id); @@ -569,13 +555,7 @@ // registered for subscopes via `navigation.serviceWorker.register()` rather // than being declared in the manifest's background section are not allowed // to use extensions API, and should be ignored here. See crbug.com/395536907. - // NOTE: We may have already reset worker ID and renderer state, but not - // browser state, if `DidStopServiceWorkerContext()` was called first. - // In that case, we reset browser state here. - bool has_already_reset_renderer_state = - !worker_state->worker_id() && - worker_state->renderer_state() == RendererState::kNotActive; - if (has_already_reset_renderer_state || + if (worker_state->worker_id() && worker_state->worker_id()->version_id == version_id) { // Stop tracking the worker for extension API purposes. ProcessManager::Get(browser_context_) @@ -584,9 +564,7 @@ // stops, a new instance must start before the worker can be considered // ready to receive tasks/events again and the renderer stop notifications // are not 100% reliable. - worker_state->SetBrowserState(BrowserState::kInitial); - worker_state->SetRendererState(RendererState::kNotActive); - worker_state->ResetWorkerId(); + worker_state->Reset(); } if (g_test_observer) {
diff --git a/extensions/browser/service_worker/service_worker_task_queue.h b/extensions/browser/service_worker/service_worker_task_queue.h index a892a08..4ee0444 100644 --- a/extensions/browser/service_worker/service_worker_task_queue.h +++ b/extensions/browser/service_worker/service_worker_task_queue.h
@@ -185,13 +185,17 @@ void SetWorkerId(const WorkerId& worker_id, ProcessManager* process_manager); - void ResetWorkerId() { worker_id_.reset(); } void SetBrowserState(BrowserState browser_state) { browser_state_ = browser_state; } void SetRendererState(RendererState renderer_state) { renderer_state_ = renderer_state; } + void Reset() { + worker_id_.reset(); + browser_state_ = BrowserState::kInitial; + renderer_state_ = RendererState::kNotActive; + } bool ready() const;
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json index 1c53f7f1..0bb9cc6 100644 --- a/gpu/config/gpu_driver_bug_list.json +++ b/gpu/config/gpu_driver_bug_list.json
@@ -3445,6 +3445,21 @@ "features": [ "force_rgb10a2_overlay_support" ] + }, + { + "id": 443, + "cr_bugs": [385039726], + "description": "Disable DComp texture support on older Windows versions", + "os": { + "type": "win", + "version": { + "op": "<", + "value": "10.0.26100.3624" + } + }, + "features": [ + "disable_dcomp_texture" + ] } ] }
diff --git a/gpu/config/gpu_workaround_list.txt b/gpu/config/gpu_workaround_list.txt index 6ff6822..5b969607 100644 --- a/gpu/config/gpu_workaround_list.txt +++ b/gpu/config/gpu_workaround_list.txt
@@ -22,6 +22,7 @@ disable_d3d11_update_subresource1 disable_d3d11_video_decoder disable_d3d11_vp9_ksvc_decoding +disable_dcomp_texture disable_decode_swap_chain disable_depth_texture disable_direct_composition_sw_video_overlays
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc index 59f4341..d30729c 100644 --- a/gpu/ipc/service/gpu_init.cc +++ b/gpu/ipc/service/gpu_init.cc
@@ -139,7 +139,10 @@ gpu::FORCE_RGB10A2_OVERLAY_SUPPORT), .check_ycbcr_studio_g22_left_p709_for_nv12_support = gpu_feature_info.IsWorkaroundEnabled( - gpu::CHECK_YCBCR_STUDIO_G22_LEFT_P709_FOR_NV12_SUPPORT)}; + gpu::CHECK_YCBCR_STUDIO_G22_LEFT_P709_FOR_NV12_SUPPORT), + .disable_dcomp_texture = + gpu_feature_info.IsWorkaroundEnabled(gpu::DISABLE_DCOMP_TEXTURE), + }; SetDirectCompositionOverlayWorkarounds(workarounds); DCHECK(gpu_info);
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils.mm b/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils.mm index 3c7777cf..d37a2df 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin/signin_utils.mm
@@ -284,33 +284,13 @@ return false; } - if (IsFullscreenSigninPromoManagerMigrationEnabled()) { - feature_engagement::Tracker* tracker = - feature_engagement::TrackerFactory::GetForProfile(profile); - unsigned int interactions = 0; - std::vector<std::pair<feature_engagement::EventConfig, int>> events = - tracker->ListEvents( - feature_engagement::kIPHiOSPromoSigninFullscreenFeature); - for (const auto& event : events) { - if (event.first.name == - feature_engagement::events::kIOSSigninFullscreenPromoTrigger) { - interactions = event.second; - break; - } - } - - if (interactions <= 1) { - return true; - } - - } else { - // The sign-in promo should be shown twice, even if no account has been - // added. - NSInteger display_count = - [defaults integerForKey:kSigninPromoViewDisplayCountKey]; - if (display_count <= 1) { - return true; - } + // TODO(crbug.com/416634715): Replace user defaults interaction count with FET + // event count. The sign-in promo should be shown twice, even if no account + // has been added. + NSInteger display_count = + [defaults integerForKey:kSigninPromoViewDisplayCountKey]; + if (display_count <= 1) { + return true; } // Otherwise, it can be shown only if a new account has been added.
diff --git a/ios/chrome/browser/push_notification/model/push_notification_delegate.mm b/ios/chrome/browser/push_notification/model/push_notification_delegate.mm index 8d106ec..6ae19061 100644 --- a/ios/chrome/browser/push_notification/model/push_notification_delegate.mm +++ b/ios/chrome/browser/push_notification/model/push_notification_delegate.mm
@@ -1160,6 +1160,14 @@ CHECK(sceneState); Browser* browser = sceneState.browserProviderInterface.mainBrowserProvider.browser; + ProfileIOS* profile = browser->GetProfile(); + + // Get the clientID for the client that is associated with this notification. + PushNotificationClient* client = [self clientForNotification:notification + profile:profile]; + std::optional<PushNotificationClientId> clientID = + (client) ? std::make_optional(client->GetClientId()) : std::nullopt; + CommandDispatcher* dispatcher = browser->GetCommandDispatcher(); id<ApplicationCommands> applicationHandler = HandlerForProtocol(dispatcher, ApplicationCommands); @@ -1167,11 +1175,31 @@ HandlerForProtocol(dispatcher, SettingsCommands); __block base::OnceClosure completion2 = std::move(completion); [applicationHandler prepareToPresentModal:^{ - [settingsHandler showNotificationsSettings]; + [settingsHandler showNotificationsSettingsAndHighlightClient:clientID]; std::move(completion2).Run(); }]; } +// Returns the client to handle the given `notification`. The client can be +// either profile-scoped (and associated with the given `profile`), or +// app-scoped. +- (PushNotificationClient*)clientForNotification:(UNNotification*)notification + profile:(ProfileIOS*)profile { + if (notification == nil) { + return nullptr; + } + PushNotificationClient* client = nullptr; + if (IsMultiProfilePushNotificationHandlingEnabled()) { + PushNotificationClientManager* clientManager = + GetClientManagerForProfile(profile); + client = clientManager->GetClientForNotification(notification); + } + if (!client) { + client = self.appWideClientManager->GetClientForNotification(notification); + } + return client; +} + // Returns the `SceneState` matching the notification response's target scene, // if any. - (SceneState*)notificationTargetSceneStateForResponse:
diff --git a/ios/chrome/browser/settings/ui_bundled/notifications/notifications_banner_view_controller.mm b/ios/chrome/browser/settings/ui_bundled/notifications/notifications_banner_view_controller.mm index 596de1e0..f093d24b 100644 --- a/ios/chrome/browser/settings/ui_bundled/notifications/notifications_banner_view_controller.mm +++ b/ios/chrome/browser/settings/ui_bundled/notifications/notifications_banner_view_controller.mm
@@ -79,6 +79,8 @@ TableViewHeaderFooterItem* tipsNotificationsFooterItem; // All the items for the send tab notifications section received by mediator. @property(nonatomic, strong) TableViewSwitchItem* sendTabNotificationsItem; +// The item with this identifier will be visually highlighted. +@property(nonatomic, assign) NotificationsItemIdentifier highlightedItem; @end @@ -88,6 +90,7 @@ UITableViewDiffableDataSource<NSNumber*, NSNumber*>* _dataSource; NSDiffableDataSourceSnapshot* _snapshot; ChromeTableViewStyler* _tableViewStyler; + ChromeTableViewStyler* _highlightTableViewStyler; // The `viewWillLayoutSubviews` is invoked on creation, dismissal, and // backward navigation of the NotificationsBannerViewController. To prevent // the view controller styling aspects of the view that will be carried over @@ -190,7 +193,7 @@ if (cell) { TableViewCell* tableViewCell = base::apple::ObjCCastStrict<TableViewCell>(cell); - [item configureCell:tableViewCell withStyler:[self tableViewStyler]]; + [self configureCell:tableViewCell item:item identifier:itemIdentifier]; } } } @@ -328,6 +331,7 @@ return self.safetyCheckItem; case ItemIdentifierSendTab: return self.sendTabNotificationsItem; + case ItemIdentifierNone: case ItemIdentifierTipsNotificationsFooter: NOTREACHED(); } @@ -368,7 +372,7 @@ DequeueTableViewCell<TableViewSwitchCell>(tableView); TableViewSwitchItem* switchItem = base::apple::ObjCCastStrict<TableViewSwitchItem>(item); - [switchItem configureCell:cell withStyler:[self tableViewStyler]]; + [self configureCell:cell item:switchItem identifier:itemIdentifier]; cell.switchView.tag = itemIdentifier; [cell.switchView addTarget:self action:@selector(switchAction:) @@ -419,6 +423,16 @@ return _tableViewStyler; } +// Styler for highlighted cells. +- (ChromeTableViewStyler*)highlightTableViewStyler { + if (!_highlightTableViewStyler) { + _highlightTableViewStyler = [[ChromeTableViewStyler alloc] init]; + _highlightTableViewStyler.cellBackgroundColor = + [UIColor colorNamed:kBlueHaloColor]; + } + return _highlightTableViewStyler; +} + // Configures the banner based on the view's size. - (void)configureBanner { if (IsCompactHeight(self.traitCollection) || TooNarrowForBanner(self.view)) { @@ -438,4 +452,15 @@ self.shouldHideBanner ? nil : UIColor.whiteColor; } +// Configures the `cell` for the `item` with the given `identifier`. A styler +// is chosed depending on whether the item should be highlighted or not. +- (void)configureCell:(TableViewCell*)cell + item:(TableViewItem*)item + identifier:(NotificationsItemIdentifier)identifier { + ChromeTableViewStyler* styler = identifier == self.highlightedItem + ? [self highlightTableViewStyler] + : [self tableViewStyler]; + [item configureCell:cell withStyler:styler]; +} + @end
diff --git a/ios/chrome/browser/settings/ui_bundled/notifications/notifications_consumer.h b/ios/chrome/browser/settings/ui_bundled/notifications/notifications_consumer.h index 56bed502..c6ba132 100644 --- a/ios/chrome/browser/settings/ui_bundled/notifications/notifications_consumer.h +++ b/ios/chrome/browser/settings/ui_bundled/notifications/notifications_consumer.h
@@ -7,6 +7,7 @@ #import <UIKit/UIKit.h> +#import "ios/chrome/browser/settings/ui_bundled/notifications/notifications_item_identifier.h" #import "ios/chrome/browser/shared/ui/table_view/legacy_chrome_table_view_consumer.h" @class TableViewItem; @@ -34,6 +35,9 @@ // Initializes the send tab notifications item. - (void)setSendTabNotificationsItem:(TableViewItem*)sendTabNotificationsItem; +// Visually highlights the item with the given `identifier`. +- (void)setHighlightedItem:(NotificationsItemIdentifier)identifier; + // Called when an item is updated and needs to be reloaded. - (void)reloadData;
diff --git a/ios/chrome/browser/settings/ui_bundled/notifications/notifications_coordinator.h b/ios/chrome/browser/settings/ui_bundled/notifications/notifications_coordinator.h index 96a74c3..67be279 100644 --- a/ios/chrome/browser/settings/ui_bundled/notifications/notifications_coordinator.h +++ b/ios/chrome/browser/settings/ui_bundled/notifications/notifications_coordinator.h
@@ -38,6 +38,9 @@ // Show Price Trackinhg Notifications settings. - (void)showTrackingPrice; +// Visually highlights the table view row for the given `clientID`. +- (void)highlightClient:(PushNotificationClientId)clientID; + @end #endif // IOS_CHROME_BROWSER_SETTINGS_UI_BUNDLED_NOTIFICATIONS_NOTIFICATIONS_COORDINATOR_H_
diff --git a/ios/chrome/browser/settings/ui_bundled/notifications/notifications_coordinator.mm b/ios/chrome/browser/settings/ui_bundled/notifications/notifications_coordinator.mm index 5c8f08f2..87e2b60 100644 --- a/ios/chrome/browser/settings/ui_bundled/notifications/notifications_coordinator.mm +++ b/ios/chrome/browser/settings/ui_bundled/notifications/notifications_coordinator.mm
@@ -119,6 +119,30 @@ [_optInAlertCoordinator stop]; } +- (void)highlightClient:(PushNotificationClientId)clientID { + self.viewController.highlightedItem = [self itemForClient:clientID]; + [self.viewController reloadData]; +} + +// Returns the table view item identifier for the given `clientID`. +- (NotificationsItemIdentifier)itemForClient: + (PushNotificationClientId)clientID { + switch (clientID) { + case PushNotificationClientId::kCommerce: + return ItemIdentifierPriceTracking; + case PushNotificationClientId::kContent: + case PushNotificationClientId::kSports: + return ItemIdentifierContent; + case PushNotificationClientId::kTips: + return ItemIdentifierTips; + case PushNotificationClientId::kSafetyCheck: + return ItemIdentifierSafetyCheck; + case PushNotificationClientId::kSendTab: + case PushNotificationClientId::kReminders: + return ItemIdentifierSendTab; + } +} + #pragma mark - NotificationsAlertPresenter - (void)presentPushNotificationPermissionAlert {
diff --git a/ios/chrome/browser/settings/ui_bundled/notifications/notifications_item_identifier.h b/ios/chrome/browser/settings/ui_bundled/notifications/notifications_item_identifier.h index 2bf0094de..faacd13 100644 --- a/ios/chrome/browser/settings/ui_bundled/notifications/notifications_item_identifier.h +++ b/ios/chrome/browser/settings/ui_bundled/notifications/notifications_item_identifier.h
@@ -10,7 +10,8 @@ // Enum representing the different types of notifications in the notifications // settings page. enum NotificationsItemIdentifier { - ItemIdentifierContent = kItemTypeEnumZero, + ItemIdentifierNone = kItemTypeEnumZero, + ItemIdentifierContent, ItemIdentifierTips, ItemIdentifierTipsNotificationsFooter, ItemIdentifierPriceTracking,
diff --git a/ios/chrome/browser/settings/ui_bundled/settings_navigation_controller.h b/ios/chrome/browser/settings/ui_bundled/settings_navigation_controller.h index cf77aa9..12e7a3d0 100644 --- a/ios/chrome/browser/settings/ui_bundled/settings_navigation_controller.h +++ b/ios/chrome/browser/settings/ui_bundled/settings_navigation_controller.h
@@ -251,6 +251,9 @@ // `delegate` may be nil. + (instancetype) notificationsSettingsControllerForBrowser:(Browser*)browser + client:(std::optional< + PushNotificationClientId>) + clientID delegate: (id<SettingsNavigationControllerDelegate>) delegate;
diff --git a/ios/chrome/browser/settings/ui_bundled/settings_navigation_controller.mm b/ios/chrome/browser/settings/ui_bundled/settings_navigation_controller.mm index a34e168..7d85d32 100644 --- a/ios/chrome/browser/settings/ui_bundled/settings_navigation_controller.mm +++ b/ios/chrome/browser/settings/ui_bundled/settings_navigation_controller.mm
@@ -578,6 +578,9 @@ + (instancetype) notificationsSettingsControllerForBrowser:(Browser*)browser + client:(std::optional< + PushNotificationClientId>) + clientID delegate: (id<SettingsNavigationControllerDelegate>) delegate { @@ -586,7 +589,7 @@ initWithRootViewController:nil browser:browser delegate:delegate]; - [navigationController showNotificationsSettings]; + [navigationController showNotificationsSettingsAndHighlightClient:clientID]; return navigationController; } @@ -1254,6 +1257,14 @@ [self.notificationsCoordinator start]; } +- (void)showNotificationsSettingsAndHighlightClient: + (std::optional<PushNotificationClientId>)clientID { + [self showNotificationsSettings]; + if (clientID.has_value()) { + [self.notificationsCoordinator highlightClient:clientID.value()]; + } +} + - (void)showPriceNotificationsSettings { [self stopNotificationsCoordinator]; self.notificationsCoordinator = [[NotificationsCoordinator alloc]
diff --git a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm index 2c426194..7612806 100644 --- a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm +++ b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
@@ -2826,15 +2826,22 @@ } - (void)showNotificationsSettings { + [self showNotificationsSettingsAndHighlightClient:std::nullopt]; +} + +- (void)showNotificationsSettingsAndHighlightClient: + (std::optional<PushNotificationClientId>)clientID { UIViewController* baseViewController = self.currentInterface.viewController; if (self.settingsNavigationController) { - [self.settingsNavigationController showNotificationsSettings]; + [self.settingsNavigationController + showNotificationsSettingsAndHighlightClient:clientID]; return; } Browser* browser = self.mainInterface.browser; self.settingsNavigationController = [SettingsNavigationController notificationsSettingsControllerForBrowser:browser + client:clientID delegate:self]; [baseViewController presentViewController:self.settingsNavigationController animated:YES
diff --git a/ios/chrome/browser/shared/public/commands/settings_commands.h b/ios/chrome/browser/shared/public/commands/settings_commands.h index 9a8bff1f..51002de 100644 --- a/ios/chrome/browser/shared/public/commands/settings_commands.h +++ b/ios/chrome/browser/shared/public/commands/settings_commands.h
@@ -7,6 +7,8 @@ #import <UIKit/UIKit.h> +#import <optional> + namespace autofill { class AutofillProfile; class CreditCard; @@ -16,6 +18,7 @@ struct CredentialUIEntry; enum class PasswordCheckReferrer; } // namespace password_manager +enum class PushNotificationClientId; @protocol SettingsCommands @@ -108,6 +111,11 @@ // Shows the Notifications Settings page in the settings. - (void)showNotificationsSettings; +// Shows the Notification Settings page and highlights the row for the push +// notification client with the given `clientID`. +- (void)showNotificationsSettingsAndHighlightClient: + (std::optional<PushNotificationClientId>)clientID; + @end #endif // IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_SETTINGS_COMMANDS_H_
diff --git a/pdf/pdf_ink_module.cc b/pdf/pdf_ink_module.cc index 0b80ebcf..77b5caa 100644 --- a/pdf/pdf_ink_module.cc +++ b/pdf/pdf_ink_module.cc
@@ -78,14 +78,13 @@ bool is_ink, std::vector<uint8_t> image_data, const gfx::Size& thumbnail_size) { - base::Value::Dict message; - message.Set("type", "updateInk2Thumbnail"); - message.Set("pageNumber", page_index + 1); - message.Set("isInk", is_ink); - message.Set("imageData", std::move(image_data)); - message.Set("width", thumbnail_size.width()); - message.Set("height", thumbnail_size.height()); - return message; + return base::Value::Dict() + .Set("type", "updateInk2Thumbnail") + .Set("pageNumber", page_index + 1) + .Set("isInk", is_ink) + .Set("imageData", std::move(image_data)) + .Set("width", thumbnail_size.width()) + .Set("height", thumbnail_size.height()); } ink::StrokeInput::ToolType GetToolTypeFromTouchEvent( @@ -304,9 +303,7 @@ } void PdfInkModule::SendContentFocusedMessage() { - base::Value::Dict message; - message.Set("type", "contentFocused"); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict().Set("type", "contentFocused")); } PdfInkModule::PageInkStrokeIterator PdfInkModule::GetVisibleStrokesIterator() { @@ -1203,11 +1200,10 @@ data.Set("size", ink_brush.GetSize()); SkColor color = GetSkColorFromInkBrush(ink_brush); - base::Value::Dict color_reply; - color_reply.Set("r", static_cast<int>(SkColorGetR(color))); - color_reply.Set("g", static_cast<int>(SkColorGetG(color))); - color_reply.Set("b", static_cast<int>(SkColorGetB(color))); - data.Set("color", std::move(color_reply)); + data.Set("color", base::Value::Dict() + .Set("r", static_cast<int>(SkColorGetR(color))) + .Set("g", static_cast<int>(SkColorGetG(color))) + .Set("b", static_cast<int>(SkColorGetB(color)))); reply.Set("data", std::move(data)); client_->PostMessage(std::move(reply));
diff --git a/pdf/pdf_ink_module_unittest.cc b/pdf/pdf_ink_module_unittest.cc index 67e30cc..1670ca6f 100644 --- a/pdf/pdf_ink_module_unittest.cc +++ b/pdf/pdf_ink_module_unittest.cc
@@ -228,9 +228,9 @@ base::Value::Dict CreateGetAnnotationBrushMessageForTesting( const std::string& brush_type) { - base::Value::Dict message; - message.Set("type", "getAnnotationBrush"); - message.Set("messageId", "foo"); + auto message = base::Value::Dict() + .Set("type", "getAnnotationBrush") + .Set("messageId", "foo"); if (!brush_type.empty()) { message.Set("brushType", brush_type); } @@ -415,6 +415,21 @@ std::vector<gfx::Rect> invalidations_; }; +class PdfInkModuleMetricsTestBase { + protected: + static constexpr char kHighlighterColorMetric[] = + "PDF.Ink2StrokeHighlighterColor"; + static constexpr char kHighlighterSizeMetric[] = + "PDF.Ink2StrokeHighlighterSize"; + static constexpr char kInputDeviceMetric[] = "PDF.Ink2StrokeInputDeviceType"; + static constexpr char kTypeMetric[] = "PDF.Ink2StrokeBrushType"; + + base::HistogramTester& histograms() { return histograms_; } + + private: + base::HistogramTester histograms_; +}; + class PdfInkModuleTest : public testing::TestWithParam<InkTestVariation> { public: void SetUp() override { @@ -451,9 +466,8 @@ } // namespace TEST_P(PdfInkModuleTest, UnknownMessage) { - base::Value::Dict message; - message.Set("type", "nonInkMessage"); - EXPECT_FALSE(ink_module().OnMessage(message)); + EXPECT_FALSE( + ink_module().OnMessage(base::Value::Dict().Set("type", "nonInkMessage"))); } // Verify that a get eraser message gets the eraser parameters. @@ -2779,137 +2793,129 @@ {expected_page1_horz_line_input_batch.value()})))); } -class PdfInkModuleMetricsTest : public PdfInkModuleUndoRedoTest { +class PdfInkModuleMetricsTest : public PdfInkModuleMetricsTestBase, + public PdfInkModuleUndoRedoTest { protected: static constexpr char kPenColorMetric[] = "PDF.Ink2StrokePenColor"; - static constexpr char kHighlighterColorMetric[] = - "PDF.Ink2StrokeHighlighterColor"; - static constexpr char kInputDeviceMetric[] = "PDF.Ink2StrokeInputDeviceType"; static constexpr char kPenSizeMetric[] = "PDF.Ink2StrokePenSize"; - static constexpr char kHighlighterSizeMetric[] = - "PDF.Ink2StrokeHighlighterSize"; - static constexpr char kTypeMetric[] = "PDF.Ink2StrokeBrushType"; }; TEST_P(PdfInkModuleMetricsTest, StrokeUndoRedoDoesNotAffectMetrics) { InitializeSimpleSinglePageBasicLayout(); - base::HistogramTester histograms; // Draw a pen stroke. RunStrokeCheckTest(/*annotation_mode_enabled=*/true); - histograms.ExpectUniqueSample(kTypeMetric, StrokeMetricBrushType::kPen, 1); - histograms.ExpectUniqueSample(kInputDeviceMetric, - StrokeMetricInputDeviceType::kMouse, 1); - histograms.ExpectUniqueSample(kPenSizeMetric, StrokeMetricBrushSize::kMedium, - 1); - histograms.ExpectUniqueSample(kPenColorMetric, StrokeMetricPenColor::kBlack, - 1); + histograms().ExpectUniqueSample(kTypeMetric, StrokeMetricBrushType::kPen, 1); + histograms().ExpectUniqueSample(kInputDeviceMetric, + StrokeMetricInputDeviceType::kMouse, 1); + histograms().ExpectUniqueSample(kPenSizeMetric, + StrokeMetricBrushSize::kMedium, 1); + histograms().ExpectUniqueSample(kPenColorMetric, StrokeMetricPenColor::kBlack, + 1); // Undo and redo. PerformUndo(); PerformRedo(); // The metrics should stay the same. - histograms.ExpectUniqueSample(kTypeMetric, StrokeMetricBrushType::kPen, 1); - histograms.ExpectUniqueSample(kInputDeviceMetric, - StrokeMetricInputDeviceType::kMouse, 1); - histograms.ExpectUniqueSample(kPenSizeMetric, StrokeMetricBrushSize::kMedium, - 1); - histograms.ExpectUniqueSample(kPenColorMetric, StrokeMetricPenColor::kBlack, - 1); + histograms().ExpectUniqueSample(kTypeMetric, StrokeMetricBrushType::kPen, 1); + histograms().ExpectUniqueSample(kInputDeviceMetric, + StrokeMetricInputDeviceType::kMouse, 1); + histograms().ExpectUniqueSample(kPenSizeMetric, + StrokeMetricBrushSize::kMedium, 1); + histograms().ExpectUniqueSample(kPenColorMetric, StrokeMetricPenColor::kBlack, + 1); } TEST_P(PdfInkModuleMetricsTest, StrokeBrushColorPen) { InitializeSimpleSinglePageBasicLayout(); - base::HistogramTester histograms; RunStrokeCheckTest(/*annotation_mode_enabled=*/false); - histograms.ExpectTotalCount(kPenColorMetric, 0); + histograms().ExpectTotalCount(kPenColorMetric, 0); // Draw a stroke with the default black color. RunStrokeCheckTest(/*annotation_mode_enabled=*/true); - histograms.ExpectUniqueSample(kPenColorMetric, StrokeMetricPenColor::kBlack, - 1); + histograms().ExpectUniqueSample(kPenColorMetric, StrokeMetricPenColor::kBlack, + 1); // Draw a stroke with "Red 1" color. TestAnnotationBrushMessageParams params = kRedBrushParams; SelectBrushTool(PdfInkBrush::Type::kPen, params); ApplyStrokeWithMouseAtMouseDownPoint(); - histograms.ExpectBucketCount(kPenColorMetric, StrokeMetricPenColor::kRed1, 1); - histograms.ExpectTotalCount(kPenColorMetric, 2); + histograms().ExpectBucketCount(kPenColorMetric, StrokeMetricPenColor::kRed1, + 1); + histograms().ExpectTotalCount(kPenColorMetric, 2); // Draw a stroke with "Tan 3" color. params.color = SkColorSetRGB(0x88, 0x59, 0x45); SelectBrushTool(PdfInkBrush::Type::kPen, params); ApplyStrokeWithMouseAtMouseDownPoint(); - histograms.ExpectBucketCount(kPenColorMetric, StrokeMetricPenColor::kTan3, 1); - histograms.ExpectTotalCount(kPenColorMetric, 3); - histograms.ExpectTotalCount(kHighlighterColorMetric, 0); + histograms().ExpectBucketCount(kPenColorMetric, StrokeMetricPenColor::kTan3, + 1); + histograms().ExpectTotalCount(kPenColorMetric, 3); + histograms().ExpectTotalCount(kHighlighterColorMetric, 0); } TEST_P(PdfInkModuleMetricsTest, StrokeBrushColorHighlighter) { EnableAnnotationMode(); InitializeSimpleSinglePageBasicLayout(); - base::HistogramTester histograms; // Draw a stroke with "Light Red" color. TestAnnotationBrushMessageParams params = kRedBrushParams; SelectBrushTool(PdfInkBrush::Type::kHighlighter, params); ApplyStrokeWithMouseAtMouseDownPoint(); - histograms.ExpectBucketCount(kHighlighterColorMetric, - StrokeMetricHighlighterColor::kLightRed, 1); - histograms.ExpectTotalCount(kHighlighterColorMetric, 1); + histograms().ExpectBucketCount(kHighlighterColorMetric, + StrokeMetricHighlighterColor::kLightRed, 1); + histograms().ExpectTotalCount(kHighlighterColorMetric, 1); // Draw a stroke with "Orange" color. params.color = SkColorSetRGB(0xFF, 0x63, 0x0C); SelectBrushTool(PdfInkBrush::Type::kHighlighter, params); ApplyStrokeWithMouseAtMouseDownPoint(); - histograms.ExpectBucketCount(kHighlighterColorMetric, - StrokeMetricHighlighterColor::kOrange, 1); - histograms.ExpectTotalCount(kHighlighterColorMetric, 2); - histograms.ExpectTotalCount(kPenColorMetric, 0); + histograms().ExpectBucketCount(kHighlighterColorMetric, + StrokeMetricHighlighterColor::kOrange, 1); + histograms().ExpectTotalCount(kHighlighterColorMetric, 2); + histograms().ExpectTotalCount(kPenColorMetric, 0); } TEST_P(PdfInkModuleMetricsTest, StrokeBrushSizePen) { InitializeSimpleSinglePageBasicLayout(); - base::HistogramTester histograms; // Draw a stroke. RunStrokeCheckTest(/*annotation_mode_enabled=*/true); - histograms.ExpectUniqueSample(kPenSizeMetric, StrokeMetricBrushSize::kMedium, - 1); + histograms().ExpectUniqueSample(kPenSizeMetric, + StrokeMetricBrushSize::kMedium, 1); TestAnnotationBrushMessageParams params = {SkColorSetRGB(0xF2, 0x8B, 0x82), /*size=*/1.0}; SelectBrushTool(PdfInkBrush::Type::kPen, params); ApplyStrokeWithMouseAtMouseDownPoint(); - histograms.ExpectBucketCount(kPenSizeMetric, - StrokeMetricBrushSize::kExtraThin, 1); - histograms.ExpectTotalCount(kPenSizeMetric, 2); + histograms().ExpectBucketCount(kPenSizeMetric, + StrokeMetricBrushSize::kExtraThin, 1); + histograms().ExpectTotalCount(kPenSizeMetric, 2); params.size = 8.0f; SelectBrushTool(PdfInkBrush::Type::kPen, params); ApplyStrokeWithMouseAtMouseDownPoint(); - histograms.ExpectBucketCount(kPenSizeMetric, - StrokeMetricBrushSize::kExtraThick, 1); - histograms.ExpectTotalCount(kPenSizeMetric, 3); - histograms.ExpectTotalCount(kHighlighterSizeMetric, 0); + histograms().ExpectBucketCount(kPenSizeMetric, + StrokeMetricBrushSize::kExtraThick, 1); + histograms().ExpectTotalCount(kPenSizeMetric, 3); + histograms().ExpectTotalCount(kHighlighterSizeMetric, 0); } TEST_P(PdfInkModuleMetricsTest, StrokeBrushSizeHighlighter) { EnableAnnotationMode(); InitializeSimpleSinglePageBasicLayout(); - base::HistogramTester histograms; // Draw a stroke with medium size. TestAnnotationBrushMessageParams params = {SkColorSetRGB(0xF2, 0x8B, 0x82), @@ -2917,116 +2923,115 @@ SelectBrushTool(PdfInkBrush::Type::kHighlighter, params); ApplyStrokeWithMouseAtMouseDownPoint(); - histograms.ExpectUniqueSample(kHighlighterSizeMetric, - StrokeMetricBrushSize::kMedium, 1); + histograms().ExpectUniqueSample(kHighlighterSizeMetric, + StrokeMetricBrushSize::kMedium, 1); // Draw a stroke with extra thin size. params.size = 4.0f; SelectBrushTool(PdfInkBrush::Type::kHighlighter, params); ApplyStrokeWithMouseAtMouseDownPoint(); - histograms.ExpectBucketCount(kHighlighterSizeMetric, - StrokeMetricBrushSize::kExtraThin, 1); - histograms.ExpectTotalCount(kHighlighterSizeMetric, 2); + histograms().ExpectBucketCount(kHighlighterSizeMetric, + StrokeMetricBrushSize::kExtraThin, 1); + histograms().ExpectTotalCount(kHighlighterSizeMetric, 2); // Draw a stroke with extra thick size. params.size = 16.0f; SelectBrushTool(PdfInkBrush::Type::kHighlighter, params); ApplyStrokeWithMouseAtMouseDownPoint(); - histograms.ExpectBucketCount(kHighlighterSizeMetric, - StrokeMetricBrushSize::kExtraThick, 1); - histograms.ExpectTotalCount(kPenSizeMetric, 0); - histograms.ExpectTotalCount(kHighlighterSizeMetric, 3); + histograms().ExpectBucketCount(kHighlighterSizeMetric, + StrokeMetricBrushSize::kExtraThick, 1); + histograms().ExpectTotalCount(kPenSizeMetric, 0); + histograms().ExpectTotalCount(kHighlighterSizeMetric, 3); } TEST_P(PdfInkModuleMetricsTest, StrokeBrushType) { InitializeSimpleSinglePageBasicLayout(); - base::HistogramTester histograms; RunStrokeCheckTest(/*annotation_mode_enabled=*/false); - histograms.ExpectTotalCount(kTypeMetric, 0); + histograms().ExpectTotalCount(kTypeMetric, 0); // Draw a pen stroke. RunStrokeCheckTest(/*annotation_mode_enabled=*/true); - histograms.ExpectUniqueSample(kTypeMetric, StrokeMetricBrushType::kPen, 1); + histograms().ExpectUniqueSample(kTypeMetric, StrokeMetricBrushType::kPen, 1); // Draw a highlighter stroke. TestAnnotationBrushMessageParams params = kRedBrushParams; SelectBrushTool(PdfInkBrush::Type::kHighlighter, params); ApplyStrokeWithMouseAtMouseDownPoint(); - histograms.ExpectBucketCount(kTypeMetric, StrokeMetricBrushType::kHighlighter, - 1); - histograms.ExpectTotalCount(kTypeMetric, 2); + histograms().ExpectBucketCount(kTypeMetric, + StrokeMetricBrushType::kHighlighter, 1); + histograms().ExpectTotalCount(kTypeMetric, 2); // Draw an eraser stroke. SelectEraserTool(); ApplyStrokeWithMouseAtMouseDownPoint(); - histograms.ExpectBucketCount(kTypeMetric, StrokeMetricBrushType::kEraser, 1); - histograms.ExpectTotalCount(kTypeMetric, 3); + histograms().ExpectBucketCount(kTypeMetric, StrokeMetricBrushType::kEraser, + 1); + histograms().ExpectTotalCount(kTypeMetric, 3); // Draw an eraser stroke at a different point that does not erase any other // strokes. The metric should stay the same. ApplyStrokeWithMouseAtPoints( kMouseUpPoint, base::span_from_ref(kMouseUpPoint), kMouseUpPoint); - histograms.ExpectBucketCount(kTypeMetric, StrokeMetricBrushType::kEraser, 1); - histograms.ExpectTotalCount(kTypeMetric, 3); + histograms().ExpectBucketCount(kTypeMetric, StrokeMetricBrushType::kEraser, + 1); + histograms().ExpectTotalCount(kTypeMetric, 3); // Draw another pen stroke. params.size = 3.0f; SelectBrushTool(PdfInkBrush::Type::kPen, params); ApplyStrokeWithMouseAtMouseDownPoint(); - histograms.ExpectBucketCount(kTypeMetric, StrokeMetricBrushType::kPen, 2); - histograms.ExpectTotalCount(kTypeMetric, 4); + histograms().ExpectBucketCount(kTypeMetric, StrokeMetricBrushType::kPen, 2); + histograms().ExpectTotalCount(kTypeMetric, 4); } TEST_P(PdfInkModuleMetricsTest, StrokeInputDeviceMouse) { InitializeSimpleSinglePageBasicLayout(); - base::HistogramTester histograms; RunStrokeCheckTest(/*annotation_mode_enabled=*/false); - histograms.ExpectTotalCount(kInputDeviceMetric, 0); + histograms().ExpectTotalCount(kInputDeviceMetric, 0); // Draw a stroke with a mouse. RunStrokeCheckTest(/*annotation_mode_enabled=*/true); - histograms.ExpectUniqueSample(kInputDeviceMetric, - StrokeMetricInputDeviceType::kMouse, 1); + histograms().ExpectUniqueSample(kInputDeviceMetric, + StrokeMetricInputDeviceType::kMouse, 1); // Draw an eraser stroke with a mouse that erases the first stroke. SelectEraserTool(); ApplyStrokeWithMouseAtMouseDownPoint(); - histograms.ExpectUniqueSample(kInputDeviceMetric, - StrokeMetricInputDeviceType::kMouse, 2); + histograms().ExpectUniqueSample(kInputDeviceMetric, + StrokeMetricInputDeviceType::kMouse, 2); // Draw another eraser stroke with a mouse that erases nothing. ApplyStrokeWithMouseAtMouseDownPoint(); - histograms.ExpectUniqueSample(kInputDeviceMetric, - StrokeMetricInputDeviceType::kMouse, 2); + histograms().ExpectUniqueSample(kInputDeviceMetric, + StrokeMetricInputDeviceType::kMouse, 2); } TEST_P(PdfInkModuleMetricsTest, StrokeInputDeviceTouch) { InitializeSimpleSinglePageBasicLayout(); - base::HistogramTester histograms; RunStrokeTouchCheckTest(/*annotation_mode_enabled=*/false); - histograms.ExpectTotalCount(kInputDeviceMetric, 0); + histograms().ExpectTotalCount(kInputDeviceMetric, 0); // Draw a stroke with touch. RunStrokeTouchCheckTest(/*annotation_mode_enabled=*/true); - histograms.ExpectUniqueSample(kInputDeviceMetric, - StrokeMetricInputDeviceType::kTouch, 1); + histograms().ExpectUniqueSample(kInputDeviceMetric, + StrokeMetricInputDeviceType::kTouch, 1); // Draw an eraser stroke with touch that erases the first stroke. SelectEraserTool(); @@ -3036,30 +3041,29 @@ ApplyStrokeWithTouchAtPoints(base::span_from_ref(kMouseDownPoint), move_point, base::span_from_ref(kMouseDownPoint)); - histograms.ExpectUniqueSample(kInputDeviceMetric, - StrokeMetricInputDeviceType::kTouch, 2); + histograms().ExpectUniqueSample(kInputDeviceMetric, + StrokeMetricInputDeviceType::kTouch, 2); // Draw another eraser stroke with touch that erases nothing. ApplyStrokeWithTouchAtPoints(base::span_from_ref(kMouseDownPoint), move_point, base::span_from_ref(kMouseDownPoint)); - histograms.ExpectUniqueSample(kInputDeviceMetric, - StrokeMetricInputDeviceType::kTouch, 2); + histograms().ExpectUniqueSample(kInputDeviceMetric, + StrokeMetricInputDeviceType::kTouch, 2); } TEST_P(PdfInkModuleMetricsTest, StrokeInputDevicePen) { InitializeSimpleSinglePageBasicLayout(); - base::HistogramTester histograms; RunStrokePenCheckTest(/*annotation_mode_enabled=*/false); - histograms.ExpectTotalCount(kInputDeviceMetric, 0); + histograms().ExpectTotalCount(kInputDeviceMetric, 0); // Draw a stroke with a pen. RunStrokePenCheckTest(/*annotation_mode_enabled=*/true); - histograms.ExpectUniqueSample(kInputDeviceMetric, - StrokeMetricInputDeviceType::kPen, 1); + histograms().ExpectUniqueSample(kInputDeviceMetric, + StrokeMetricInputDeviceType::kPen, 1); // Draw an eraser stroke with a pen that erases the first stroke. SelectEraserTool(); @@ -3069,15 +3073,15 @@ ApplyStrokeWithPenAtPoints(base::span_from_ref(kMouseDownPoint), move_point, base::span_from_ref(kMouseDownPoint)); - histograms.ExpectUniqueSample(kInputDeviceMetric, - StrokeMetricInputDeviceType::kPen, 2); + histograms().ExpectUniqueSample(kInputDeviceMetric, + StrokeMetricInputDeviceType::kPen, 2); // Draw another eraser stroke with a pen that erases nothing. ApplyStrokeWithPenAtPoints(base::span_from_ref(kMouseDownPoint), move_point, base::span_from_ref(kMouseDownPoint)); - histograms.ExpectUniqueSample(kInputDeviceMetric, - StrokeMetricInputDeviceType::kPen, 2); + histograms().ExpectUniqueSample(kInputDeviceMetric, + StrokeMetricInputDeviceType::kPen, 2); } class PdfInkModuleTextHighlightTest : public PdfInkModuleStrokeTest {
diff --git a/pdf/pdf_view_web_plugin.cc b/pdf/pdf_view_web_plugin.cc index a51357c..d221559 100644 --- a/pdf/pdf_view_web_plugin.cc +++ b/pdf/pdf_view_web_plugin.cc
@@ -232,12 +232,11 @@ }; base::Value::Dict DictFromRect(const gfx::Rect& rect) { - base::Value::Dict dict; - dict.Set("x", rect.x()); - dict.Set("y", rect.y()); - dict.Set("width", rect.width()); - dict.Set("height", rect.height()); - return dict; + return base::Value::Dict() + .Set("x", rect.x()) + .Set("y", rect.y()) + .Set("width", rect.width()) + .Set("height", rect.height()); } bool IsPrintPreviewUrl(std::string_view url) { @@ -273,12 +272,11 @@ base::Value::Dict CreateSaveDataBlockMessage( const std::string& token, PdfViewWebPlugin::SaveDataBlock data) { - base::Value::Dict message; - message.Set("type", "saveDataBlock"); - message.Set("token", token); - message.Set("dataToSave", std::move(data.block)); - message.Set("totalFileSize", base::checked_cast<int>(data.total_file_size)); - return message; + return base::Value::Dict() + .Set("type", "saveDataBlock") + .Set("token", token) + .Set("dataToSave", std::move(data.block)) + .Set("totalFileSize", base::checked_cast<int>(data.total_file_size)); } } // namespace @@ -404,9 +402,8 @@ } void StrokeFinished() override { - base::Value::Dict message; - message.Set("type", "finishInkStroke"); - plugin_->client_->PostMessage(std::move(message)); + plugin_->client_->PostMessage( + base::Value::Dict().Set("type", "finishInkStroke")); plugin_->SetPluginCanSave(true); } @@ -571,11 +568,11 @@ } void PdfViewWebPlugin::SendSetSmoothScrolling() { - base::Value::Dict message; - message.Set("type", "setSmoothScrolling"); - message.Set("smoothScrolling", - blink::Platform::Current()->IsScrollAnimatorEnabled()); - client_->PostMessage(std::move(message)); + client_->PostMessage( + base::Value::Dict() + .Set("type", "setSmoothScrolling") + .Set("smoothScrolling", + blink::Platform::Current()->IsScrollAnimatorEnabled())); } void PdfViewWebPlugin::DidOpen(std::unique_ptr<UrlLoader> loader, @@ -1024,16 +1021,17 @@ } void PdfViewWebPlugin::ProposeDocumentLayout(const DocumentLayout& layout) { - base::Value::Dict message; - message.Set("type", "documentDimensions"); - message.Set("width", layout.size().width()); - message.Set("height", layout.size().height()); - message.Set("layoutOptions", layout.options().ToValue()); base::Value::List page_dimensions; - for (size_t i = 0; i < layout.page_count(); ++i) + page_dimensions.reserve(layout.page_count()); + for (size_t i = 0; i < layout.page_count(); ++i) { page_dimensions.Append(DictFromRect(layout.page_rect(i))); - message.Set("pageDimensions", std::move(page_dimensions)); - client_->PostMessage(std::move(message)); + } + client_->PostMessage(base::Value::Dict() + .Set("type", "documentDimensions") + .Set("width", layout.size().width()) + .Set("height", layout.size().height()) + .Set("layoutOptions", layout.options().ToValue()) + .Set("pageDimensions", std::move(page_dimensions))); // Reload the accessibility tree on layout changes because the relative page // bounds are no longer valid. @@ -1059,64 +1057,61 @@ void PdfViewWebPlugin::ScrollToX(int x_screen_coords) { const float x_scroll_pos = x_screen_coords / device_scale_; - base::Value::Dict message; - message.Set("type", "setScrollPosition"); - message.Set("x", static_cast<double>(x_scroll_pos)); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict() + .Set("type", "setScrollPosition") + .Set("x", static_cast<double>(x_scroll_pos))); } void PdfViewWebPlugin::ScrollToY(int y_screen_coords) { const float y_scroll_pos = y_screen_coords / device_scale_; - base::Value::Dict message; - message.Set("type", "setScrollPosition"); - message.Set("y", static_cast<double>(y_scroll_pos)); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict() + .Set("type", "setScrollPosition") + .Set("y", static_cast<double>(y_scroll_pos))); } void PdfViewWebPlugin::ScrollBy(const gfx::Vector2d& delta) { const float x_delta = delta.x() / device_scale_; const float y_delta = delta.y() / device_scale_; - base::Value::Dict message; - message.Set("type", "scrollBy"); - message.Set("x", static_cast<double>(x_delta)); - message.Set("y", static_cast<double>(y_delta)); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict() + .Set("type", "scrollBy") + .Set("x", static_cast<double>(x_delta)) + .Set("y", static_cast<double>(y_delta))); } void PdfViewWebPlugin::ScrollToPage(int page) { if (!engine_ || engine_->GetNumberOfPages() == 0) return; - base::Value::Dict message; - message.Set("type", "goToPage"); - message.Set("page", page); - client_->PostMessage(std::move(message)); + client_->PostMessage( + base::Value::Dict().Set("type", "goToPage").Set("page", page)); } void PdfViewWebPlugin::NavigateTo(const std::string& url, WindowOpenDisposition disposition) { - base::Value::Dict message; - message.Set("type", "navigate"); - message.Set("url", url); - message.Set("disposition", static_cast<int>(disposition)); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict() + .Set("type", "navigate") + .Set("url", url) + .Set("disposition", static_cast<int>(disposition))); } void PdfViewWebPlugin::NavigateToDestination(int page, const float* x, const float* y, const float* zoom) { - base::Value::Dict message; - message.Set("type", "navigateToDestination"); - message.Set("page", page); - if (x) + auto message = base::Value::Dict() + .Set("type", "navigateToDestination") + .Set("page", page); + if (x) { message.Set("x", static_cast<double>(*x)); - if (y) + } + if (y) { message.Set("y", static_cast<double>(*y)); - if (zoom) + } + if (zoom) { message.Set("zoom", static_cast<double>(*zoom)); + } client_->PostMessage(std::move(message)); } @@ -1176,9 +1171,8 @@ } void PdfViewWebPlugin::NotifyTouchSelectionOccurred() { - base::Value::Dict message; - message.Set("type", "touchSelectionOccurred"); - client_->PostMessage(std::move(message)); + client_->PostMessage( + base::Value::Dict().Set("type", "touchSelectionOccurred")); } void PdfViewWebPlugin::CaretChanged(const gfx::Rect& caret_rect) { @@ -1190,15 +1184,11 @@ DCHECK(password_callback_.is_null()); password_callback_ = std::move(callback); - base::Value::Dict message; - message.Set("type", "getPassword"); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict().Set("type", "getPassword")); } void PdfViewWebPlugin::Beep() { - base::Value::Dict message; - message.Set("type", "beep"); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict().Set("type", "beep")); } void PdfViewWebPlugin::Alert(const std::string& message) { @@ -1238,14 +1228,14 @@ const std::string& bcc, const std::string& subject, const std::string& body) { - base::Value::Dict message; - message.Set("type", "email"); - message.Set("to", base::EscapeUrlEncodedData(to, false)); - message.Set("cc", base::EscapeUrlEncodedData(cc, false)); - message.Set("bcc", base::EscapeUrlEncodedData(bcc, false)); - message.Set("subject", base::EscapeUrlEncodedData(subject, false)); - message.Set("body", base::EscapeUrlEncodedData(body, false)); - client_->PostMessage(std::move(message)); + client_->PostMessage( + base::Value::Dict() + .Set("type", "email") + .Set("to", base::EscapeUrlEncodedData(to, false)) + .Set("cc", base::EscapeUrlEncodedData(cc, false)) + .Set("bcc", base::EscapeUrlEncodedData(bcc, false)) + .Set("subject", base::EscapeUrlEncodedData(subject, false)) + .Set("body", base::EscapeUrlEncodedData(body, false))); } void PdfViewWebPlugin::Print() { @@ -1446,8 +1436,6 @@ void PdfViewWebPlugin::FormFieldFocusChange( PDFiumEngineClient::FocusFieldType type) { - base::Value::Dict message; - message.Set("type", "formFocusChange"); std::string field_type; // LINT.IfChange(FocusFieldTypes) switch (type) { @@ -1462,8 +1450,10 @@ break; } // LINT.ThenChange(//chrome/browser/resources/pdf/constants.ts:FocusFieldTypes) - message.Set("focused", field_type); - client_->PostMessage(std::move(message)); + + client_->PostMessage(base::Value::Dict() + .Set("type", "formFocusChange") + .Set("focused", std::move(field_type))); text_input_type_ = type == PDFiumEngineClient::FocusFieldType::kText ? blink::WebTextInputType::kWebTextInputTypeText @@ -1508,16 +1498,13 @@ edit_mode_ = true; SetPluginCanSave(true); - base::Value::Dict message; - message.Set("type", "setIsEditing"); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict().Set("type", "setIsEditing")); } void PdfViewWebPlugin::DocumentFocusChanged(bool document_has_focus) { - base::Value::Dict message; - message.Set("type", "documentFocusChanged"); - message.Set("hasFocus", document_has_focus); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict() + .Set("type", "documentFocusChanged") + .Set("hasFocus", document_has_focus)); } void PdfViewWebPlugin::SetSelectedText(const std::string& selected_text) { @@ -1591,16 +1578,14 @@ return; } - base::Value::Dict message; - message.Set("type", "showSearchifyInProgress"); - message.Set("show", show); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict() + .Set("type", "showSearchifyInProgress") + .Set("show", show)); } void PdfViewWebPlugin::OnHasSearchifyText() { - base::Value::Dict message; - message.Set("type", "setHasSearchifyText"); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict().Set("type", "setHasSearchifyText")); + pdf_accessibility_data_handler_->OnHasSearchifyText(); if (chrome_pdf::features::IsPdfSearchifySaveEnabled()) { SetPluginCanSave(true); @@ -1772,8 +1757,6 @@ void PdfViewWebPlugin::HandleGetPageBoundingBoxMessage( const base::Value::Dict& message) { const int page_index = message.FindInt("page").value(); - base::Value::Dict reply = PrepareReplyMessage(message); - PDFiumPage* page = engine_->GetPage(page_index); CHECK(page); gfx::RectF bounding_box = page->GetBoundingBox(); @@ -1782,12 +1765,12 @@ // Flip the origin from bottom-left to top-left. bounding_box.set_y(static_cast<float>(page_bounds.height()) - bounding_box.bottom()); - reply.Set("x", bounding_box.x()); - reply.Set("y", bounding_box.y()); - reply.Set("width", bounding_box.width()); - reply.Set("height", bounding_box.height()); - client_->PostMessage(std::move(reply)); + client_->PostMessage(PrepareReplyMessage(message) + .Set("x", bounding_box.x()) + .Set("y", bounding_box.y()) + .Set("width", bounding_box.width()) + .Set("height", bounding_box.height())); } void PdfViewWebPlugin::HandleGetPasswordCompleteMessage( @@ -1802,9 +1785,8 @@ std::string selected_text; base::RemoveChars(engine_->GetSelectedText(), "\r", &selected_text); - base::Value::Dict reply = PrepareReplyMessage(message); - reply.Set("selectedText", selected_text); - client_->PostMessage(std::move(reply)); + client_->PostMessage(PrepareReplyMessage(message).Set( + "selectedText", std::move(selected_text))); } void PdfViewWebPlugin::HandleGetSaveDataBlockMessage( @@ -1822,9 +1804,8 @@ void PdfViewWebPlugin::HandleGetSuggestedFileName( const base::Value::Dict& message) { - base::Value::Dict reply = PrepareReplyMessage(message); - reply.Set("fileName", GetFileNameForSaveFromUrl(url_)); - client_->PostMessage(std::move(reply)); + client_->PostMessage(PrepareReplyMessage(message).Set( + "fileName", GetFileNameForSaveFromUrl(url_))); } void PdfViewWebPlugin::HandleGetThumbnailMessage( @@ -1879,9 +1860,8 @@ base::Value data_to_save( IsSaveDataSizeValid(data.size()) ? data : std::vector<uint8_t>()); - base::Value::Dict reply = PrepareReplyMessage(message); - reply.Set("dataToSave", std::move(data_to_save)); - client_->PostMessage(std::move(reply)); + client_->PostMessage( + PrepareReplyMessage(message).Set("dataToSave", std::move(data_to_save))); } void PdfViewWebPlugin::HandleSaveMessage(const base::Value::Dict& message) { @@ -2096,10 +2076,10 @@ engine_->KillFormFocus(); - base::Value::Dict message; - message.Set("type", "saveData"); - message.Set("token", token); - message.Set("fileName", GetFileNameForSaveFromUrl(url_)); + auto message = base::Value::Dict() + .Set("type", "saveData") + .Set("token", token) + .Set("fileName", GetFileNameForSaveFromUrl(url_)); // Expose `edit_mode_` state for integration testing. message.Set("editModeForTesting", edit_mode_); @@ -2208,10 +2188,8 @@ void PdfViewWebPlugin::SaveToFile(const std::string& token) { engine_->KillFormFocus(); - base::Value::Dict message; - message.Set("type", "consumeSaveToken"); - message.Set("token", token); - client_->PostMessage(std::move(message)); + client_->PostMessage( + base::Value::Dict().Set("type", "consumeSaveToken").Set("token", token)); pdf_host_->SaveUrlAs(GURL(url_), network::mojom::ReferrerPolicy::kDefault); } @@ -2694,6 +2672,7 @@ return; base::Value::List attachments; + attachments.reserve(attachment_infos.size()); for (const DocumentAttachmentInfo& attachment_info : attachment_infos) { // Send `size` as -1 to indicate that the attachment is too large to be // downloaded. @@ -2701,41 +2680,36 @@ ? static_cast<int>(attachment_info.size_bytes) : -1; - base::Value::Dict attachment; - attachment.Set("name", attachment_info.name); - attachment.Set("size", size); - attachment.Set("readable", attachment_info.is_readable); - attachments.Append(std::move(attachment)); + attachments.Append(base::Value::Dict() + .Set("name", attachment_info.name) + .Set("size", size) + .Set("readable", attachment_info.is_readable)); } - base::Value::Dict message; - message.Set("type", "attachments"); - message.Set("attachmentsData", std::move(attachments)); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict() + .Set("type", "attachments") + .Set("attachmentsData", std::move(attachments))); } void PdfViewWebPlugin::SendBookmarks() { base::Value::List bookmarks = engine_->GetBookmarks(); - if (bookmarks.empty()) + if (bookmarks.empty()) { return; + } - base::Value::Dict message; - message.Set("type", "bookmarks"); - message.Set("bookmarksData", std::move(bookmarks)); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict() + .Set("type", "bookmarks") + .Set("bookmarksData", std::move(bookmarks))); } void PdfViewWebPlugin::SendExecutedEditCommand(std::string_view edit_command) { - base::Value::Dict message; - message.Set("type", "executedEditCommand"); - message.Set("editCommand", edit_command); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict() + .Set("type", "executedEditCommand") + .Set("editCommand", edit_command)); } void PdfViewWebPlugin::SendStartedFindInPage() { - base::Value::Dict message; - message.Set("type", "startedFindInPage"); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict().Set("type", "startedFindInPage")); } void PdfViewWebPlugin::SendMetadata() { @@ -2783,20 +2757,18 @@ metadata.Set("canSerializeDocument", IsSaveDataSizeValid(engine_->GetLoadedByteSize())); - base::Value::Dict message; - message.Set("type", "metadata"); - message.Set("metadataData", std::move(metadata)); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict() + .Set("type", "metadata") + .Set("metadataData", std::move(metadata))); } void PdfViewWebPlugin::SendLoadingProgress(double percentage) { DCHECK(percentage == -1 || (percentage >= 0 && percentage <= 100)); last_progress_sent_ = percentage; - base::Value::Dict message; - message.Set("type", "loadProgress"); - message.Set("progress", percentage); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict() + .Set("type", "loadProgress") + .Set("progress", percentage)); } void PdfViewWebPlugin::HandleReleaseSaveInBlockBuffers( @@ -2944,9 +2916,7 @@ } void PdfViewWebPlugin::SendPrintPreviewLoadedNotification() { - base::Value::Dict message; - message.Set("type", "printPreviewLoaded"); - client_->PostMessage(std::move(message)); + client_->PostMessage(base::Value::Dict().Set("type", "printPreviewLoaded")); } void PdfViewWebPlugin::SendThumbnailForTesting(base::Value::Dict reply,
diff --git a/pdf/pdf_view_web_plugin_unittest.cc b/pdf/pdf_view_web_plugin_unittest.cc index 767da03..29d8049 100644 --- a/pdf/pdf_view_web_plugin_unittest.cc +++ b/pdf/pdf_view_web_plugin_unittest.cc
@@ -199,10 +199,9 @@ } base::Value::Dict GenerateShowSearchifyInProgressMessage(bool show) { - base::Value::Dict message; - message.Set("type", "showSearchifyInProgress"); - message.Set("show", show); - return message; + return base::Value::Dict() + .Set("type", "showSearchifyInProgress") + .Set("show", show); } class MockHeaderVisitor : public blink::WebHTTPHeaderVisitor { @@ -1499,10 +1498,9 @@ TEST_F(PdfViewWebPluginTest, HandleSetBackgroundColorMessage) { ASSERT_NE(SK_ColorGREEN, plugin_->GetBackgroundColor()); - base::Value::Dict message; - message.Set("type", "setBackgroundColor"); - message.Set("color", static_cast<double>(SK_ColorGREEN)); - plugin_->OnMessage(message); + plugin_->OnMessage(base::Value::Dict() + .Set("type", "setBackgroundColor") + .Set("color", static_cast<double>(SK_ColorGREEN))); EXPECT_EQ(SK_ColorGREEN, plugin_->GetBackgroundColor()); } @@ -1511,9 +1509,9 @@ EXPECT_FALSE(engine_ptr_->IsReadOnly()); plugin_->set_cursor_type_for_testing(ui::mojom::CursorType::kIBeam); - base::Value::Dict message; - message.Set("type", "setPresentationMode"); - message.Set("enablePresentationMode", true); + auto message = base::Value::Dict() + .Set("type", "setPresentationMode") + .Set("enablePresentationMode", true); plugin_->OnMessage(message); // After entering presentation mode, PDFiumEngine is read-only and the cursor @@ -1837,15 +1835,14 @@ } TEST_F(PdfViewWebPluginTest, OnDocumentLoadComplete) { - base::Value::Dict metadata; - metadata.Set("fileSize", "0 B"); - metadata.Set("linearized", false); - metadata.Set("pageSize", "Varies"); - metadata.Set("canSerializeDocument", true); - - base::Value::Dict message; - message.Set("type", "metadata"); - message.Set("metadataData", std::move(metadata)); + auto message = + base::Value::Dict() + .Set("type", "metadata") + .Set("metadataData", base::Value::Dict() + .Set("fileSize", "0 B") + .Set("linearized", false) + .Set("pageSize", "Varies") + .Set("canSerializeDocument", true)); EXPECT_CALL(*client_ptr_, PostMessage); EXPECT_CALL(*client_ptr_, PostMessage(Eq(std::ref(message)))); @@ -1935,25 +1932,21 @@ } TEST_F(PdfViewWebPluginTest, OnHasSearchifyText) { - base::Value::Dict message; - message.Set("type", "setHasSearchifyText"); + auto message = base::Value::Dict().Set("type", "setHasSearchifyText"); EXPECT_CALL(*client_ptr_, PostMessage(Eq(std::ref(message)))); plugin_->OnHasSearchifyText(); } TEST_F(PdfViewWebPluginTest, HighlightTextFragments) { - base::Value::List fragments; - fragments.Append("hello-,world"); - fragments.Append("world,-hello"); - - base::Value::Dict message; - message.Set("type", "highlightTextFragments"); - message.Set("textFragments", std::move(fragments)); - EXPECT_CALL(*engine_ptr_, HighlightTextFragments( ElementsAre("hello-,world", "world,-hello"))); - plugin_->OnMessage(message); + + plugin_->OnMessage(base::Value::Dict() + .Set("type", "highlightTextFragments") + .Set("textFragments", base::Value::List() + .Append("hello-,world") + .Append("world,-hello"))); } class PdfViewWebPluginWithDocInfoTest @@ -1979,29 +1972,25 @@ } base::Value::List GetBookmarks() override { - // Create `bookmark1` which navigates to an in-doc position. This bookmark - // will be in the top-level bookmark list. - base::Value::Dict bookmark1; - bookmark1.Set("title", "Bookmark 1"); - bookmark1.Set("page", 2); - bookmark1.Set("x", 10); - bookmark1.Set("y", 20); - bookmark1.Set("zoom", 2.0); - // Create `bookmark2` which navigates to a web page. This bookmark will be // a child of `bookmark1`. - base::Value::Dict bookmark2; - bookmark2.Set("title", "Bookmark 2"); - bookmark2.Set("uri", "test.com"); + auto bookmark2 = + base::Value::Dict().Set("title", "Bookmark 2").Set("uri", "test.com"); - base::Value::List children_of_bookmark1; - children_of_bookmark1.Append(std::move(bookmark2)); - bookmark1.Set("children", std::move(children_of_bookmark1)); + // Create `bookmark1` which navigates to an in-doc position. This bookmark + // will be in the top-level bookmark list. + auto bookmark1 = + base::Value::Dict() + .Set("title", "Bookmark 1") + .Set("page", 2) + .Set("x", 10) + .Set("y", 20) + .Set("zoom", 2.0) + .Set("children", + base::Value::List().Append(std::move(bookmark2))); // Create the top-level bookmark list. - base::Value::List bookmarks; - bookmarks.Append(std::move(bookmark1)); - return bookmarks; + return base::Value::List().Append(std::move(bookmark1)); } std::optional<gfx::Size> GetUniformPageSizePoints() override { @@ -2051,68 +2040,51 @@ }; static base::Value::Dict CreateExpectedAttachmentsResponse() { - base::Value::List attachments; - { - base::Value::Dict attachment; - attachment.Set("name", "attachment1.txt"); - attachment.Set("size", 13); - attachment.Set("readable", true); - attachments.Append(std::move(attachment)); - } - { - base::Value::Dict attachment; - attachment.Set("name", "attachment2.pdf"); - attachment.Set("size", 0); - attachment.Set("readable", false); - attachments.Append(std::move(attachment)); - } - { - base::Value::Dict attachment; - attachment.Set("name", "attachment3.mov"); - attachment.Set("size", -1); - attachment.Set("readable", true); - attachments.Append(std::move(attachment)); - } - - base::Value::Dict message; - message.Set("type", "attachments"); - message.Set("attachmentsData", std::move(attachments)); - return message; + return base::Value::Dict() + .Set("type", "attachments") + .Set("attachmentsData", base::Value::List() + .Append(base::Value::Dict() + .Set("name", "attachment1.txt") + .Set("size", 13) + .Set("readable", true)) + .Append(base::Value::Dict() + .Set("name", "attachment2.pdf") + .Set("size", 0) + .Set("readable", false)) + .Append(base::Value::Dict() + .Set("name", "attachment3.mov") + .Set("size", -1) + .Set("readable", true))); } static base::Value::Dict CreateExpectedBookmarksResponse( base::Value::List bookmarks) { - base::Value::Dict message; - message.Set("type", "bookmarks"); - message.Set("bookmarksData", std::move(bookmarks)); - return message; + return base::Value::Dict() + .Set("type", "bookmarks") + .Set("bookmarksData", std::move(bookmarks)); } static base::Value::Dict CreateExpectedMetadataResponse() { - base::Value::Dict metadata; - metadata.Set("version", "1.7"); - metadata.Set("fileSize", "13 B"); - metadata.Set("linearized", true); - - metadata.Set("title", "Title"); - metadata.Set("author", "Author"); - metadata.Set("subject", "Subject"); - metadata.Set("keywords", "Keywords"); - metadata.Set("creator", "Creator"); - metadata.Set("producer", "Producer"); - metadata.Set("creationDate", - "5/4/21, 4:12:13\xE2\x80\xAF" - "AM"); - metadata.Set("modDate", - "6/4/21, 8:16:17\xE2\x80\xAF" - "AM"); - metadata.Set("pageSize", "13.89 × 16.67 in (portrait)"); - metadata.Set("canSerializeDocument", true); - - base::Value::Dict message; - message.Set("type", "metadata"); - message.Set("metadataData", std::move(metadata)); - return message; + return base::Value::Dict() + .Set("type", "metadata") + .Set("metadataData", base::Value::Dict() + .Set("version", "1.7") + .Set("fileSize", "13 B") + .Set("linearized", true) + .Set("title", "Title") + .Set("author", "Author") + .Set("subject", "Subject") + .Set("keywords", "Keywords") + .Set("creator", "Creator") + .Set("producer", "Producer") + .Set("creationDate", + "5/4/21, 4:12:13\xE2\x80\xAF" + "AM") + .Set("modDate", + "6/4/21, 8:16:17\xE2\x80\xAF" + "AM") + .Set("pageSize", "13.89 × 16.67 in (portrait)") + .Set("canSerializeDocument", true)); } void SetUpClient() override { @@ -2308,27 +2280,27 @@ uint32_t offset, uint32_t block_size, std::string token) { - base::Value::Dict dict; - dict.Set("type", "getSaveDataBlock"); - dict.Set("saveRequestType", static_cast<int>(request_type)); - dict.Set("offset", static_cast<int>(offset)); - dict.Set("blockSize", static_cast<int>(block_size)); - dict.Set("token", token); - return dict; + return base::Value::Dict() + .Set("type", "getSaveDataBlock") + .Set("saveRequestType", static_cast<int>(request_type)) + .Set("offset", static_cast<int>(offset)) + .Set("blockSize", static_cast<int>(block_size)) + .Set("token", token); } void ExpectResponse(base::span<const uint8_t> data, uint32_t offset, uint32_t block_size, std::string token) { - base::Value value(base::Value::Type::DICT); - value.GetDict().Set("type", "saveDataBlock"); - value.GetDict().Set("token", token); - value.GetDict().Set("dataToSave", - base::Value(data.subspan(offset, block_size))); - value.GetDict().Set("totalFileSize", - base::Value(static_cast<int>(data.size()))); - EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(value))); + auto data_to_save = data.subspan(offset, block_size); + base::BlobStorage data_to_save_blob(data_to_save.begin(), + data_to_save.end()); + auto dict = base::Value::Dict() + .Set("type", "saveDataBlock") + .Set("token", std::move(token)) + .Set("dataToSave", std::move(data_to_save_blob)) + .Set("totalFileSize", static_cast<int>(data.size())); + EXPECT_CALL(*client_ptr_, PostMessage(base::test::IsJson(dict))); } void SetUpClient() override { @@ -2422,9 +2394,8 @@ 0, 0, "token-1")); EXPECT_FALSE(plugin_->IsSaveDataBufferEmptyForTesting()); - base::Value::Dict message; - message.Set("type", "releaseSaveInBlockBuffers"); - plugin_->OnMessage(message); + plugin_->OnMessage( + base::Value::Dict().Set("type", "releaseSaveInBlockBuffers")); EXPECT_TRUE(plugin_->IsSaveDataBufferEmptyForTesting()); pdf_receiver_.FlushForTesting(); @@ -2839,9 +2810,9 @@ } void SendThumbnail(std::string_view message_id, const gfx::SizeF& page_size) { - base::Value::Dict reply; - reply.Set("type", "getThumbnailReply"); - reply.Set("messageId", message_id); + auto reply = base::Value::Dict() + .Set("type", "getThumbnailReply") + .Set("messageId", message_id); plugin_->SendThumbnailForTesting( std::move(reply), /*page_index=*/0, Thumbnail(page_size, /*device_pixel_ratio=*/1));
diff --git a/pdf/test/pdf_ink_test_helpers.cc b/pdf/test/pdf_ink_test_helpers.cc index 3971287..d16b7fe 100644 --- a/pdf/test/pdf_ink_test_helpers.cc +++ b/pdf/test/pdf_ink_test_helpers.cc
@@ -58,9 +58,6 @@ base::Value::Dict CreateSetAnnotationBrushMessageForTesting( std::string_view type, const TestAnnotationBrushMessageParams* params) { - base::Value::Dict message; - message.Set("type", "setAnnotationBrush"); - base::Value::Dict data; data.Set("type", type); if (params) { @@ -71,15 +68,16 @@ .Set("b", static_cast<int>(SkColorGetB(params->color)))); data.Set("size", params->size); } - message.Set("data", std::move(data)); - return message; + + return base::Value::Dict() + .Set("type", "setAnnotationBrush") + .Set("data", std::move(data)); } base::Value::Dict CreateSetAnnotationModeMessageForTesting(bool enable) { - base::Value::Dict message; - message.Set("type", "setAnnotationMode"); - message.Set("mode", enable ? "draw" : "off"); - return message; + return base::Value::Dict() + .Set("type", "setAnnotationMode") + .Set("mode", enable ? "draw" : "off"); } base::Value::Dict CreateSetAnnotationUndoRedoMessageForTesting( @@ -87,11 +85,9 @@ base::Value::Dict message; switch (type) { case TestAnnotationUndoRedoMessageType::kUndo: - message.Set("type", "annotationUndo"); - return message; + return base::Value::Dict().Set("type", "annotationUndo"); case TestAnnotationUndoRedoMessageType::kRedo: - message.Set("type", "annotationRedo"); - return message; + return base::Value::Dict().Set("type", "annotationRedo"); } NOTREACHED(); }
diff --git a/sandbox/linux/BUILD.gn b/sandbox/linux/BUILD.gn index d642c7d..e56c1423 100644 --- a/sandbox/linux/BUILD.gn +++ b/sandbox/linux/BUILD.gn
@@ -377,9 +377,9 @@ "system_headers/arm_linux_syscalls.h", "system_headers/arm_linux_ucontext.h", "system_headers/i386_linux_ucontext.h", - "system_headers/landlock.h", "system_headers/linux_filter.h", "system_headers/linux_futex.h", + "system_headers/linux_landlock.h", "system_headers/linux_prctl.h", "system_headers/linux_ptrace.h", "system_headers/linux_seccomp.h",
diff --git a/sandbox/linux/services/syscall_wrappers.cc b/sandbox/linux/services/syscall_wrappers.cc index df4a991..4d12e51 100644 --- a/sandbox/linux/services/syscall_wrappers.cc +++ b/sandbox/linux/services/syscall_wrappers.cc
@@ -215,4 +215,16 @@ return syscall(__NR_landlock_create_ruleset, attr, size, flags); } +int landlock_add_rule(const int ruleset_fd, + const int rule_type, + const void* const rule_attr, + const uint32_t flags) { + return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr, + flags); +} + +int landlock_restrict_self(const int ruleset_fd, const uint32_t flags) { + return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); +} + } // namespace sandbox
diff --git a/sandbox/linux/services/syscall_wrappers.h b/sandbox/linux/services/syscall_wrappers.h index a6b22eb..d591dbc 100644 --- a/sandbox/linux/services/syscall_wrappers.h +++ b/sandbox/linux/services/syscall_wrappers.h
@@ -105,6 +105,12 @@ const struct landlock_ruleset_attr* const attr, const size_t size, const uint32_t flags); +SANDBOX_EXPORT int landlock_add_rule(const int ruleset_fd, + const int rule_type, + const void* const rule_attr, + const uint32_t flags); +SANDBOX_EXPORT int landlock_restrict_self(const int ruleset_fd, + const uint32_t flags); } // namespace sandbox
diff --git a/sandbox/linux/system_headers/landlock.h b/sandbox/linux/system_headers/landlock.h deleted file mode 100644 index 76c24b4..0000000 --- a/sandbox/linux/system_headers/landlock.h +++ /dev/null
@@ -1,51 +0,0 @@ -/* Copyright 2024 The Chromium Authors - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/* - * Landlock system definitions. - * - * These definitions are based on <linux/landlock.h>. However, because we - * can't guarantee that header will be available on all systems, they are - * extracted here. We only include definitions needed for checking the Landlock - * version, as we just need to determine if the system supports Landlock. - */ - -#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_LANDLOCK_H_ -#define SANDBOX_LINUX_SYSTEM_HEADERS_LANDLOCK_H_ - -#include <stddef.h> -#include <stdint.h> - -/** - * struct landlock_ruleset_attr - Ruleset definition - * - * Argument of sys_landlock_create_ruleset(). - */ -struct landlock_ruleset_attr { - /** - * @handled_access_fs: Bitmask of actions (cf. `Filesystem flags`_) - * that is handled by this ruleset and should then be forbidden if no - * rule explicitly allow them. This is needed for backward - * compatibility reasons. - */ - uint64_t handled_access_fs; -}; - -/* - * sys_landlock_create_ruleset() flags: - * - * - %LANDLOCK_CREATE_RULESET_VERSION: Get the highest supported Landlock ABI - * version. - */ -#ifndef LANDLOCK_CREATE_RULESET_VERSION -#define LANDLOCK_CREATE_RULESET_VERSION (1U << 0) -#endif - -// Syscall number for landlock_create_ruleset taken from <asm-generic/unistd.h>. -#ifndef __NR_landlock_create_ruleset -#define __NR_landlock_create_ruleset 444 -#endif - -#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LANDLOCK_H_
diff --git a/sandbox/linux/system_headers/linux_landlock.h b/sandbox/linux/system_headers/linux_landlock.h new file mode 100644 index 0000000..32ef65e --- /dev/null +++ b/sandbox/linux/system_headers/linux_landlock.h
@@ -0,0 +1,113 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Landlock system definitions. +// +// These definitions are based on <linux/landlock.h>. However, because we +// can't guarantee that header will be available on all systems, they are +// extracted here. We only include definitions needed for checking the Landlock +// version, as we just need to determine if the system supports Landlock. + +#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_LANDLOCK_H_ +#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_LANDLOCK_H_ + +#include <stddef.h> +#include <stdint.h> + +// struct landlock_ruleset_attr - Ruleset definition +// +// Argument of sys_landlock_create_ruleset(). +struct landlock_ruleset_attr { + // @handled_access_fs: Bitmask of actions (cf. `Filesystem flags`_) + // that is handled by this ruleset and should then be forbidden if no + // rule explicitly allow them. This is needed for backward + // compatibility reasons. + uint64_t handled_access_fs; +}; + +// sys_landlock_create_ruleset() flags: +// +// - %LANDLOCK_CREATE_RULESET_VERSION: Get the highest supported Landlock ABI +// version. +#ifndef LANDLOCK_CREATE_RULESET_VERSION +#define LANDLOCK_CREATE_RULESET_VERSION (1U << 0) +#endif + +#ifndef LANDLOCK_RULE_PATH_BENEATH +#define LANDLOCK_RULE_PATH_BENEATH 1 +#endif + +// struct landlock_path_beneath_attr - Path hierarchy definition +// +// Argument of sys_landlock_add_rule(). +struct landlock_path_beneath_attr { + // @allowed_access: Bitmask of allowed actions for this file hierarchy + // (cf. `Filesystem flags`_). + uint64_t allowed_access; + // @parent_fd: File descriptor, open with ``O_PATH``, which identifies + // the parent directory of a file hierarchy, or just a file. + uint32_t parent_fd; +}; + +#ifndef LANDLOCK_ACCESS_FS_EXECUTE +#define LANDLOCK_ACCESS_FS_EXECUTE (1ULL << 0) +#endif + +#ifndef LANDLOCK_ACCESS_FS_WRITE_FILE +#define LANDLOCK_ACCESS_FS_WRITE_FILE (1ULL << 1) +#endif + +#ifndef LANDLOCK_ACCESS_FS_READ_FILE +#define LANDLOCK_ACCESS_FS_READ_FILE (1ULL << 2) +#endif + +#ifndef LANDLOCK_ACCESS_FS_READ_DIR +#define LANDLOCK_ACCESS_FS_READ_DIR (1ULL << 3) +#endif + +#ifndef LANDLOCK_ACCESS_FS_REMOVE_DIR +#define LANDLOCK_ACCESS_FS_REMOVE_DIR (1ULL << 4) +#endif + +#ifndef LANDLOCK_ACCESS_FS_REMOVE_FILE +#define LANDLOCK_ACCESS_FS_REMOVE_FILE (1ULL << 5) +#endif + +#ifndef LANDLOCK_ACCESS_FS_MAKE_CHAR +#define LANDLOCK_ACCESS_FS_MAKE_CHAR (1ULL << 6) +#endif + +#ifndef LANDLOCK_ACCESS_FS_MAKE_DIR +#define LANDLOCK_ACCESS_FS_MAKE_DIR (1ULL << 7) +#endif + +#ifndef LANDLOCK_ACCESS_FS_MAKE_REG +#define LANDLOCK_ACCESS_FS_MAKE_REG (1ULL << 8) +#endif + +#ifndef LANDLOCK_ACCESS_FS_MAKE_SOCK +#define LANDLOCK_ACCESS_FS_MAKE_SOCK (1ULL << 9) +#endif + +#ifndef LANDLOCK_ACCESS_FS_MAKE_FIFO +#define LANDLOCK_ACCESS_FS_MAKE_FIFO (1ULL << 10) +#endif + +#ifndef LANDLOCK_ACCESS_FS_MAKE_BLOCK +#define LANDLOCK_ACCESS_FS_MAKE_BLOCK (1ULL << 11) +#endif + +#ifndef LANDLOCK_ACCESS_FS_MAKE_SYM +#define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12) +#endif + +#ifndef LANDLOCK_ACCESS_FS_REFER +#define LANDLOCK_ACCESS_FS_REFER (1ULL << 13) +#endif + +#ifndef LANDLOCK_ACCESS_FS_TRUNCATE +#define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14) +#endif + +#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_LANDLOCK_H_
diff --git a/sandbox/policy/BUILD.gn b/sandbox/policy/BUILD.gn index d8b04f5..7d206b8 100644 --- a/sandbox/policy/BUILD.gn +++ b/sandbox/policy/BUILD.gn
@@ -36,6 +36,8 @@ "linux/bpf_base_policy_linux.h", "linux/bpf_renderer_policy_linux.cc", "linux/bpf_renderer_policy_linux.h", + "linux/landlock_gpu_policy_android.cc", + "linux/landlock_gpu_policy_android.h", ] deps += [ "//sandbox:sandbox_buildflags",
diff --git a/sandbox/policy/features.cc b/sandbox/policy/features.cc index 579745a..b2e59a2 100644 --- a/sandbox/policy/features.cc +++ b/sandbox/policy/features.cc
@@ -156,6 +156,10 @@ #endif // BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_ANDROID) +// Enables the experimental Android GPU sandbox using Landlock. +BASE_FEATURE(kAndroidGpuSandbox, + "AndroidGpuSandbox", + base::FEATURE_DISABLED_BY_DEFAULT); // Enables the renderer on Android to use a separate seccomp policy. BASE_FEATURE(kUseRendererProcessPolicy, "UseRendererProcessPolicy",
diff --git a/sandbox/policy/features.h b/sandbox/policy/features.h index 1cc13f0..905de65f 100644 --- a/sandbox/policy/features.h +++ b/sandbox/policy/features.h
@@ -53,6 +53,7 @@ #endif // BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_ANDROID) +SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kAndroidGpuSandbox); SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kUseRendererProcessPolicy); SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kRestrictRendererPoliciesInBaseline); SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kRestrictCloneParameters);
diff --git a/sandbox/policy/linux/landlock_gpu_policy_android.cc b/sandbox/policy/linux/landlock_gpu_policy_android.cc new file mode 100644 index 0000000..75d921c8 --- /dev/null +++ b/sandbox/policy/linux/landlock_gpu_policy_android.cc
@@ -0,0 +1,114 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/policy/linux/landlock_gpu_policy_android.h" + +#include <string> +#include <vector> + +#include "base/logging.h" +#include "base/files/scoped_file.h" +#include "base/threading/thread_id_name_manager.h" +#include "build/build_config.h" +#include "sandbox/linux/services/thread_helpers.h" + +#if BUILDFLAG(IS_ANDROID) +#include <fcntl.h> +#include <sys/prctl.h> +#include <unistd.h> + +namespace sandbox::landlock { + +bool AddRulesToPolicy(int ruleset_fd, + const std::vector<std::string>& paths, + uint64_t allowed_access) { + bool success = true; + for (const auto& path : paths) { + base::ScopedFD parent_fd(open(path.c_str(), O_PATH | O_CLOEXEC)); + if (!parent_fd.is_valid()) { + PLOG(ERROR) << "open failed for " << path; + continue; + } + struct landlock_path_beneath_attr path_beneath = { + .allowed_access = allowed_access, + .parent_fd = static_cast<uint32_t>(parent_fd.get()), + }; + + if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, + 0)) { + PLOG(ERROR) << "landlock_add_rule() failed for " << path; + success = false; + } + } + return success; +} +#endif + +bool ApplyLandlock(sandbox::mojom::Sandbox sandbox_type) { +#if BUILDFLAG(IS_ANDROID) + if (sandbox_type != sandbox::mojom::Sandbox::kGpu) { + LOG(ERROR) << "Sandbox type not GPU, skipping Landlock"; + return false; + } + + if (!sandbox::ThreadHelpers::IsSingleThreaded()) { + LOG(ERROR) << "Not single threaded, skipping Landlock"; + for (const auto& id : base::ThreadIdNameManager::GetInstance()->GetIds()) { + LOG(ERROR) << "ThreadId=" << id << " name:" + << base::ThreadIdNameManager::GetInstance()->GetName(id); + } + return false; + } + + struct landlock_ruleset_attr ruleset_attr = { + .handled_access_fs = LANDLOCK_HANDLED_ACCESS_TYPES}; + base::ScopedFD ruleset_fd( + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0)); + if (!ruleset_fd.is_valid()) { + PLOG(ERROR) << "Ruleset creation failed"; + return false; + } + + std::vector<std::string> allowed_ro_paths = { + // RO access to /data because there may be sub-directories that don't + // exist yet at policy creation. + "/data", "/proc", "/sys", "/var"}; + uint64_t ro_access = + LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR; + if (!AddRulesToPolicy(ruleset_fd.get(), allowed_ro_paths, ro_access)) { + LOG(ERROR) << "Adding Landlock RO rules failed"; + } + + std::vector<std::string> allowed_rw_paths = { + "/data/cache/com.android.chrome", + // We need to allowlist /dev, because ashmem creates dynamically named + // directories, such as /dev/ashmemc4072f6e-da0f-447e-98d4-b6497e5f57af. + // TODO(crbug.com/40215931): Move away from ashmem and remove this. + "/dev", + }; + uint64_t rw_access = + LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR | + LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_REMOVE_FILE; + if (!AddRulesToPolicy(ruleset_fd.get(), allowed_rw_paths, rw_access)) { + LOG(ERROR) << "Adding Landlock RW rules failed"; + } + // Landlock requires no_new_privs. + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { + PLOG(ERROR) << "SET_NO_NEW_PRIVS failed"; + return false; + } + int result = landlock_restrict_self(ruleset_fd.get(), 0); + if (result != 0) { + PLOG(ERROR) << "landlock_restrict_self() failed"; + return false; + } + + return true; +#else // !BUILDFLAG(IS_ANDROID) + // Landlock is not applicable on non-Android platforms. + return false; +#endif +} + +} // namespace sandbox::landlock
diff --git a/sandbox/policy/linux/landlock_gpu_policy_android.h b/sandbox/policy/linux/landlock_gpu_policy_android.h new file mode 100644 index 0000000..16ad33ec --- /dev/null +++ b/sandbox/policy/linux/landlock_gpu_policy_android.h
@@ -0,0 +1,55 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Landlock functions and constants. + +#ifndef SANDBOX_POLICY_LINUX_LANDLOCK_GPU_POLICY_ANDROID_H_ +#define SANDBOX_POLICY_LINUX_LANDLOCK_GPU_POLICY_ANDROID_H_ + +#include "sandbox/linux/services/syscall_wrappers.h" +#include "sandbox/linux/system_headers/linux_landlock.h" +#include "sandbox/policy/export.h" +#include "sandbox/policy/mojom/sandbox.mojom.h" + +namespace sandbox::landlock { + +#define LANDLOCK_ACCESS_FS_ROUGHLY_READ \ + (LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR) + +#define LANDLOCK_ACCESS_FS_ROUGHLY_READ_EXECUTE \ + (LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_READ_FILE | \ + LANDLOCK_ACCESS_FS_READ_DIR) + +#define LANDLOCK_ACCESS_FS_ROUGHLY_BASIC_WRITE \ + (LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_REMOVE_DIR | \ + LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_MAKE_DIR | \ + LANDLOCK_ACCESS_FS_MAKE_REG) + +#define LANDLOCK_ACCESS_FS_ROUGHLY_EDIT \ + (LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_REMOVE_DIR | \ + LANDLOCK_ACCESS_FS_REMOVE_FILE) + +#define LANDLOCK_ACCESS_FS_ROUGHLY_FULL_WRITE \ + (LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_REMOVE_DIR | \ + LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_MAKE_CHAR | \ + LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_MAKE_REG | \ + LANDLOCK_ACCESS_FS_MAKE_SOCK | LANDLOCK_ACCESS_FS_MAKE_FIFO | \ + LANDLOCK_ACCESS_FS_MAKE_BLOCK | LANDLOCK_ACCESS_FS_MAKE_SYM) + +#define LANDLOCK_ACCESS_FILE \ + (LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_WRITE_FILE | \ + LANDLOCK_ACCESS_FS_READ_FILE) + +#define LANDLOCK_HANDLED_ACCESS_TYPES \ + (LANDLOCK_ACCESS_FS_ROUGHLY_READ_EXECUTE | \ + LANDLOCK_ACCESS_FS_ROUGHLY_FULL_WRITE) + +// Applies a basic Landlock sandbox policy to the current process. +// Returns true if the policy was applied successfully, false otherwise. +// This function is a no-op and returns false on non-Android platforms. +SANDBOX_POLICY_EXPORT bool ApplyLandlock(mojom::Sandbox sandbox_type); + +} // namespace sandbox::landlock + +#endif // SANDBOX_POLICY_LINUX_LANDLOCK_GPU_POLICY_ANDROID_H_
diff --git a/sandbox/policy/linux/sandbox_linux.cc b/sandbox/policy/linux/sandbox_linux.cc index 427604b2..ac0c77b 100644 --- a/sandbox/policy/linux/sandbox_linux.cc +++ b/sandbox/policy/linux/sandbox_linux.cc
@@ -47,7 +47,7 @@ #include "sandbox/linux/syscall_broker/broker_client.h" #include "sandbox/linux/syscall_broker/broker_command.h" #include "sandbox/linux/syscall_broker/broker_process.h" -#include "sandbox/linux/system_headers/landlock.h" +#include "sandbox/linux/system_headers/linux_landlock.h" #include "sandbox/linux/system_headers/linux_stat.h" #include "sandbox/policy/features.h" #include "sandbox/policy/linux/bpf_broker_policy_linux.h"
diff --git a/services/on_device_model/ml/on_device_model_executor.cc b/services/on_device_model/ml/on_device_model_executor.cc index 9ccd457..421beb7 100644 --- a/services/on_device_model/ml/on_device_model_executor.cc +++ b/services/on_device_model/ml/on_device_model_executor.cc
@@ -410,17 +410,17 @@ DISABLE_CFI_DLSYM on_device_model::Capabilities OnDeviceModelExecutor::GetCapabilities( const ChromeML& chrome_ml, - on_device_model::ModelAssets assets) { + on_device_model::ModelFile model_file) { on_device_model::Capabilities result; if (!chrome_ml.api().GetCapabilities) { return result; } PlatformFile platform_file; - if (assets.weights.IsValid()) { - platform_file = assets.weights.TakePlatformFile(); + if (model_file.IsFile()) { + platform_file = model_file.file().TakePlatformFile(); } else { - base::File file(assets.weights_path, + base::File file(model_file.path(), base::File::FLAG_OPEN | base::File::FLAG_READ); platform_file = file.TakePlatformFile(); } @@ -531,12 +531,13 @@ max_tokens_ = std::max(params->max_tokens, kReserveTokensForSafety); ChromeMLModelData data; - std::string weights_path_str = assets.weights_path.AsUTF8Unsafe(); + std::string weights_path_str; std::string sp_model_path_str = assets.sp_model_path.AsUTF8Unsafe(); // Prefer to load the model from a file descriptor if possible. - if (assets.weights.IsValid()) { - data.weights_file = assets.weights.TakePlatformFile(); + if (assets.weights.IsFile()) { + data.weights_file = assets.weights.file().TakePlatformFile(); } else { + weights_path_str = assets.weights.path().AsUTF8Unsafe(); data.model_path = weights_path_str.data(); data.sentencepiece_model_path = sp_model_path_str.data(); }
diff --git a/services/on_device_model/ml/on_device_model_executor.h b/services/on_device_model/ml/on_device_model_executor.h index d06638b..94b251e5 100644 --- a/services/on_device_model/ml/on_device_model_executor.h +++ b/services/on_device_model/ml/on_device_model_executor.h
@@ -105,7 +105,7 @@ static on_device_model::Capabilities GetCapabilities( const ChromeML& chrome_ml, - on_device_model::ModelAssets assets); + on_device_model::ModelFile model_file); std::unique_ptr<SessionImpl> CreateSession( const ScopedAdaptation* adaptation,
diff --git a/services/on_device_model/on_device_model_service.cc b/services/on_device_model/on_device_model_service.cc index 2d1044b..d91b04c 100644 --- a/services/on_device_model/on_device_model_service.cc +++ b/services/on_device_model/on_device_model_service.cc
@@ -445,10 +445,10 @@ std::move(callback).Run(mojom::LoadModelResult::kSuccess); } -void OnDeviceModelService::GetCapabilities(ModelAssets assets, +void OnDeviceModelService::GetCapabilities(ModelFile model_file, GetCapabilitiesCallback callback) { std::move(callback).Run(ml::OnDeviceModelExecutor::GetCapabilities( - *chrome_ml_, std::move(assets))); + *chrome_ml_, std::move(model_file))); } void OnDeviceModelService::GetEstimatedPerformanceClass(
diff --git a/services/on_device_model/on_device_model_service.h b/services/on_device_model/on_device_model_service.h index 0fbc32ce..68afc21 100644 --- a/services/on_device_model/on_device_model_service.h +++ b/services/on_device_model/on_device_model_service.h
@@ -71,7 +71,7 @@ void LoadModel(mojom::LoadModelParamsPtr params, mojo::PendingReceiver<mojom::OnDeviceModel> model, LoadModelCallback callback) override; - void GetCapabilities(ModelAssets assets, + void GetCapabilities(ModelFile model_file, GetCapabilitiesCallback callback) override; void LoadTextSafetyModel( mojom::TextSafetyModelParamsPtr params,
diff --git a/services/on_device_model/on_device_model_service_unittest.cc b/services/on_device_model/on_device_model_service_unittest.cc index bd8a90c8..5c38a1549 100644 --- a/services/on_device_model/on_device_model_service_unittest.cc +++ b/services/on_device_model/on_device_model_service_unittest.cc
@@ -92,6 +92,7 @@ params->backend_type = backend_type; params->performance_hint = performance_hint; params->max_tokens = 8000; + params->assets = ModelAssets::FromPath(base::FilePath()); base::test::TestFuture<mojom::LoadModelResult> future; service()->LoadModel(std::move(params), remote.BindNewPipeAndPassReceiver(), future.GetCallback()); @@ -647,10 +648,9 @@ auto expect_capabilities = [&](const std::string& data, const Capabilities& expected) { FakeFile file(data); - ModelAssets assets; - assets.weights = file.Open(); + ModelFile model_file(file.Open()); base::test::TestFuture<const Capabilities&> future; - service()->GetCapabilities(std::move(assets), future.GetCallback()); + service()->GetCapabilities(std::move(model_file), future.GetCallback()); EXPECT_EQ(expected, future.Take()); }; expect_capabilities("none", {}); @@ -664,10 +664,9 @@ auto expect_capabilities = [&](const std::string& data, const Capabilities& expected) { FakeFile file(data); - ModelAssets assets; - assets.weights_path = file.Path(); + ModelFile model_file(file.Path()); base::test::TestFuture<const Capabilities&> future; - service()->GetCapabilities(std::move(assets), future.GetCallback()); + service()->GetCapabilities(std::move(model_file), future.GetCallback()); EXPECT_EQ(expected, future.Take()); }; expect_capabilities("none", {});
diff --git a/services/on_device_model/public/cpp/BUILD.gn b/services/on_device_model/public/cpp/BUILD.gn index 62f5dda..c8b5eb4f 100644 --- a/services/on_device_model/public/cpp/BUILD.gn +++ b/services/on_device_model/public/cpp/BUILD.gn
@@ -33,6 +33,8 @@ "model_assets.h", "model_assets_mojom_traits.cc", "model_assets_mojom_traits.h", + "model_file_mojom_traits.cc", + "model_file_mojom_traits.h", ] public_deps = [ "//base",
diff --git a/services/on_device_model/public/cpp/model_assets.cc b/services/on_device_model/public/cpp/model_assets.cc index 5f25f7d..eb3ed72c 100644 --- a/services/on_device_model/public/cpp/model_assets.cc +++ b/services/on_device_model/public/cpp/model_assets.cc
@@ -5,13 +5,21 @@ #include "services/on_device_model/public/cpp/model_assets.h" #include <cstdint> -#include <string_view> +#include <optional> +#include <utility> +#include <variant> +#include "base/check.h" #include "base/feature_list.h" #include "base/files/file.h" +#include "base/files/file_path.h" #include "base/files/file_util.h" -#include "base/task/thread_pool.h" #include "build/build_config.h" +#include "mojo/public/cpp/bindings/default_construct_tag.h" + +#if BUILDFLAG(IS_WIN) +#include "base/task/thread_pool.h" +#endif namespace on_device_model { namespace { @@ -67,20 +75,70 @@ ModelAssetPaths::ModelAssetPaths(const ModelAssetPaths&) = default; ModelAssetPaths::~ModelAssetPaths() = default; -ModelAssets::ModelAssets() = default; +ModelFile::ModelFile(base::File file) : file_(std::move(file)) {} -ModelAssets::ModelAssets(const ModelAssets& other) - : weights(other.weights.Duplicate()), - weights_path(other.weights_path), - sp_model_path(other.sp_model_path) {} +ModelFile::ModelFile(base::FilePath path) : file_(std::move(path)) {} -ModelAssets& ModelAssets::operator=(const ModelAssets& other) { - weights = other.weights.Duplicate(); - weights_path = other.weights_path; - sp_model_path = other.sp_model_path; +ModelFile::ModelFile(mojo::DefaultConstruct::Tag) {} + +ModelFile::ModelFile(const ModelFile& other) { + if (other.IsFile()) { + file_ = other.file().Duplicate(); + } else { + file_ = other.path(); + } +} + +ModelFile& ModelFile::operator=(const ModelFile& other) { + if (other.IsFile()) { + file_ = other.file().Duplicate(); + } else { + file_ = other.path(); + } return *this; } +ModelFile::ModelFile(ModelFile&&) = default; +ModelFile& ModelFile::operator=(ModelFile&&) = default; +ModelFile::~ModelFile() = default; + +base::File& ModelFile::file() { + CHECK(std::holds_alternative<base::File>(file_)); + return std::get<base::File>(file_); +} + +const base::File& ModelFile::file() const { + CHECK(std::holds_alternative<base::File>(file_)); + return std::get<base::File>(file_); +} + +const base::FilePath& ModelFile::path() const { + CHECK(std::holds_alternative<base::FilePath>(file_)); + return std::get<base::FilePath>(file_); +} + +bool ModelFile::IsFile() const { + return std::holds_alternative<base::File>(file_); +} + +// static +ModelAssets ModelAssets::FromFile(base::File file) { + return ModelAssets(ModelFile(std::move(file))); +} + +// static +ModelAssets ModelAssets::FromPath(base::FilePath path) { + return ModelAssets(ModelFile(std::move(path))); +} + +ModelAssets::ModelAssets(ModelFile weights) : weights(std::move(weights)) {} + +ModelAssets::ModelAssets(mojo::DefaultConstruct::Tag tag) : weights(tag) {} + +ModelAssets::ModelAssets(const ModelAssets& other) = default; + +ModelAssets& ModelAssets::operator=(const ModelAssets& other) = default; + ModelAssets::ModelAssets(ModelAssets&&) = default; ModelAssets& ModelAssets::operator=(ModelAssets&&) = default; ModelAssets::~ModelAssets() = default; @@ -90,16 +148,13 @@ PrefetchFile(paths.weights); } - ModelAssets assets; - if (!paths.weights.empty()) { - if (base::FeatureList::IsEnabled( - kForceLoadOnDeviceModelFromFilePathForTesting)) { - assets.weights_path = paths.weights; - } else { - assets.weights = base::File(paths.weights, kWeightsFlags); - } + if (paths.weights.empty() || + base::FeatureList::IsEnabled( + kForceLoadOnDeviceModelFromFilePathForTesting)) { + return ModelAssets::FromPath(std::move(paths.weights)); } - return assets; + + return ModelAssets::FromFile(base::File(paths.weights, kWeightsFlags)); } AdaptationAssetPaths::AdaptationAssetPaths() = default;
diff --git a/services/on_device_model/public/cpp/model_assets.h b/services/on_device_model/public/cpp/model_assets.h index 4ef85fb4..71f2788 100644 --- a/services/on_device_model/public/cpp/model_assets.h +++ b/services/on_device_model/public/cpp/model_assets.h
@@ -5,9 +5,15 @@ #ifndef SERVICES_ON_DEVICE_MODEL_PUBLIC_CPP_MODEL_ASSETS_H_ #define SERVICES_ON_DEVICE_MODEL_PUBLIC_CPP_MODEL_ASSETS_H_ +#include <optional> +#include <variant> + #include "base/component_export.h" #include "base/files/file.h" #include "base/files/file_path.h" +#include "mojo/public/cpp/bindings/default_construct_tag.h" +#include "mojo/public/cpp/bindings/union_traits.h" +#include "services/on_device_model/public/mojom/on_device_model_service.mojom-data-view.h" namespace on_device_model { @@ -21,18 +27,48 @@ base::FilePath sp_model; }; +class COMPONENT_EXPORT(ON_DEVICE_MODEL_CPP) ModelFile { + public: + explicit ModelFile(base::File file); + explicit ModelFile(base::FilePath path); + explicit ModelFile(mojo::DefaultConstruct::Tag); + ModelFile(const ModelFile&); + ModelFile& operator=(const ModelFile&); + ModelFile(ModelFile&&); + ModelFile& operator=(ModelFile&&); + ~ModelFile(); + + base::File& file(); + const base::File& file() const; + + const base::FilePath& path() const; + + bool IsFile() const; + + private: + friend struct mojo::UnionTraits<on_device_model::mojom::ModelFileDataView, + on_device_model::ModelFile>; + + std::variant<base::File, base::FilePath> file_; +}; + // A bundle of opened file assets comprising model description to use for // execution. struct COMPONENT_EXPORT(ON_DEVICE_MODEL_CPP) ModelAssets { - ModelAssets(); + // Convenience methods which construct `weights` from the given arg. + static ModelAssets FromFile(base::File file); + static ModelAssets FromPath(base::FilePath path); + + explicit ModelAssets(ModelFile weights); + + explicit ModelAssets(mojo::DefaultConstruct::Tag); ModelAssets(const ModelAssets&); ModelAssets& operator=(const ModelAssets&); ModelAssets(ModelAssets&&); ModelAssets& operator=(ModelAssets&&); ~ModelAssets(); - base::File weights; - base::FilePath weights_path; + ModelFile weights; base::FilePath sp_model_path; }; @@ -63,6 +99,7 @@ AdaptationAssets& operator=(AdaptationAssets&&); ~AdaptationAssets(); + // TODO(crbug.com/401011041): Use a ModelFile to represent these members. base::File weights; base::FilePath weights_path; };
diff --git a/services/on_device_model/public/cpp/model_assets_mojom_traits.cc b/services/on_device_model/public/cpp/model_assets_mojom_traits.cc index aa47d4f5..48537d9f 100644 --- a/services/on_device_model/public/cpp/model_assets_mojom_traits.cc +++ b/services/on_device_model/public/cpp/model_assets_mojom_traits.cc
@@ -4,6 +4,11 @@ #include "services/on_device_model/public/cpp/model_assets_mojom_traits.h" +#include <optional> + +#include "base/files/file_path.h" +#include "mojo/public/cpp/base/file_path_mojom_traits.h" +#include "services/on_device_model/public/cpp/model_assets.h" #include "services/on_device_model/public/mojom/on_device_model_service.mojom-shared.h" namespace mojo { @@ -15,16 +20,12 @@ on_device_model::ModelAssets* assets) { // base::FilePath doesn't have nullable StructTraits, so we need to use // optional. - std::optional<base::FilePath> weights_path, sp_model_path; + std::optional<base::FilePath> sp_model_path; bool ok = data.ReadWeights(&assets->weights) && - data.ReadWeightsPath(&weights_path) && data.ReadSpModelPath(&sp_model_path); if (!ok) { return false; } - if (weights_path.has_value()) { - assets->weights_path = *weights_path; - } if (sp_model_path.has_value()) { assets->sp_model_path = *sp_model_path; }
diff --git a/services/on_device_model/public/cpp/model_assets_mojom_traits.h b/services/on_device_model/public/cpp/model_assets_mojom_traits.h index 142f1c8..2565feb0 100644 --- a/services/on_device_model/public/cpp/model_assets_mojom_traits.h +++ b/services/on_device_model/public/cpp/model_assets_mojom_traits.h
@@ -5,13 +5,13 @@ #ifndef SERVICES_ON_DEVICE_MODEL_PUBLIC_CPP_MODEL_ASSETS_MOJOM_TRAITS_H_ #define SERVICES_ON_DEVICE_MODEL_PUBLIC_CPP_MODEL_ASSETS_MOJOM_TRAITS_H_ +#include <utility> + #include "base/component_export.h" -#include "base/files/file.h" #include "base/files/file_path.h" -#include "mojo/public/cpp/base/file_mojom_traits.h" -#include "mojo/public/cpp/base/file_path_mojom_traits.h" -#include "mojo/public/cpp/base/read_only_file_mojom_traits.h" +#include "mojo/public/cpp/bindings/struct_traits.h" #include "services/on_device_model/public/cpp/model_assets.h" +#include "services/on_device_model/public/cpp/model_file_mojom_traits.h" #include "services/on_device_model/public/mojom/on_device_model_service.mojom-shared.h" namespace mojo { @@ -20,14 +20,11 @@ struct COMPONENT_EXPORT(ON_DEVICE_MODEL_CPP) StructTraits<on_device_model::mojom::ModelAssetsDataView, on_device_model::ModelAssets> { - static base::File weights(on_device_model::ModelAssets& assets) { + static on_device_model::ModelFile weights( + on_device_model::ModelAssets& assets) { return std::move(assets.weights); } - static base::FilePath weights_path(on_device_model::ModelAssets& assets) { - return std::move(assets.weights_path); - } - static base::FilePath sp_model_path(on_device_model::ModelAssets& assets) { return std::move(assets.sp_model_path); }
diff --git a/services/on_device_model/public/cpp/model_file_mojom_traits.cc b/services/on_device_model/public/cpp/model_file_mojom_traits.cc new file mode 100644 index 0000000..e2254c3 --- /dev/null +++ b/services/on_device_model/public/cpp/model_file_mojom_traits.cc
@@ -0,0 +1,82 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/on_device_model/public/cpp/model_file_mojom_traits.h" + +#include <optional> +#include <utility> +#include <variant> + +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/functional/overloaded.h" +#include "base/notreached.h" +#include "mojo/public/cpp/base/file_mojom_traits.h" +#include "mojo/public/cpp/base/file_path_mojom_traits.h" +#include "services/on_device_model/public/cpp/model_assets.h" +#include "services/on_device_model/public/mojom/on_device_model_service.mojom-shared.h" + +namespace mojo { + +// static +base::File UnionTraits< + on_device_model::mojom::ModelFileDataView, + on_device_model::ModelFile>::file(on_device_model::ModelFile& file) { + return std::get<base::File>(std::move(file.file_)); +} + +// static +base::FilePath UnionTraits< + on_device_model::mojom::ModelFileDataView, + on_device_model::ModelFile>::path(on_device_model::ModelFile& file) { + return std::get<base::FilePath>(std::move(file.file_)); +} + +// static +bool UnionTraits<on_device_model::mojom::ModelFileDataView, + on_device_model::ModelFile>:: + Read(on_device_model::mojom::ModelFileDataView data, + on_device_model::ModelFile* out) { + switch (data.tag()) { + case on_device_model::mojom::ModelFileDataView::Tag::kFile: { + base::File file; + if (!data.ReadFile(&file)) { + return false; + } + *out = on_device_model::ModelFile(std::move(file)); + return true; + } + case on_device_model::mojom::ModelFileDataView::Tag::kPath: { + // base::FilePath doesn't have nullable StructTraits, so we need to use + // optional. + std::optional<base::FilePath> path; + if (!data.ReadPath(&path)) { + return false; + } + *out = on_device_model::ModelFile( + std::move(path).value_or(base::FilePath())); + return true; + } + default: + return false; + } +} + +// static +on_device_model::mojom::ModelFileDataView::Tag UnionTraits< + on_device_model::mojom::ModelFileDataView, + on_device_model::ModelFile>::GetTag(const on_device_model::ModelFile& + file) { + return std::visit( + base::Overloaded{ + [](const base::File& file) { + return on_device_model::mojom::ModelFileDataView::Tag::kFile; + }, + [](const base::FilePath& path) { + return on_device_model::mojom::ModelFileDataView::Tag::kPath; + }}, + file.file_); +} + +} // namespace mojo
diff --git a/services/on_device_model/public/cpp/model_file_mojom_traits.h b/services/on_device_model/public/cpp/model_file_mojom_traits.h new file mode 100644 index 0000000..3d33f985 --- /dev/null +++ b/services/on_device_model/public/cpp/model_file_mojom_traits.h
@@ -0,0 +1,34 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_ON_DEVICE_MODEL_PUBLIC_CPP_MODEL_FILE_MOJOM_TRAITS_H_ +#define SERVICES_ON_DEVICE_MODEL_PUBLIC_CPP_MODEL_FILE_MOJOM_TRAITS_H_ + +#include "base/component_export.h" +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "mojo/public/cpp/bindings/union_traits.h" +#include "services/on_device_model/public/cpp/model_assets.h" +#include "services/on_device_model/public/mojom/on_device_model_service.mojom-shared.h" + +namespace mojo { + +template <> +class COMPONENT_EXPORT(ON_DEVICE_MODEL_CPP) + UnionTraits<on_device_model::mojom::ModelFileDataView, + on_device_model::ModelFile> { + public: + static base::File file(on_device_model::ModelFile& file); + static base::FilePath path(on_device_model::ModelFile& file); + + static bool Read(on_device_model::mojom::ModelFileDataView data, + on_device_model::ModelFile* weights); + + static on_device_model::mojom::ModelFileDataView::Tag GetTag( + const on_device_model::ModelFile& weights); +}; + +} // namespace mojo + +#endif // SERVICES_ON_DEVICE_MODEL_PUBLIC_CPP_MODEL_FILE_MOJOM_TRAITS_H_
diff --git a/services/on_device_model/public/cpp/test_support/fake_service.cc b/services/on_device_model/public/cpp/test_support/fake_service.cc index f64fd34..7b2ebeb 100644 --- a/services/on_device_model/public/cpp/test_support/fake_service.cc +++ b/services/on_device_model/public/cpp/test_support/fake_service.cc
@@ -388,7 +388,7 @@ return; } FakeOnDeviceModel::Data data; - data.base_weight = ReadFile(params->assets.weights); + data.base_weight = ReadFile(params->assets.weights.file()); auto test_model = std::make_unique<FakeOnDeviceModel>( settings_, std::move(data), params->performance_hint); model_receivers_.Add(std::move(test_model), std::move(model)); @@ -396,9 +396,9 @@ } void FakeOnDeviceModelService::GetCapabilities( - ModelAssets assets, + ModelFile model_file, GetCapabilitiesCallback callback) { - std::string contents = ReadFile(assets.weights); + std::string contents = ReadFile(model_file.file()); Capabilities capabilities; if (contents.find("image") != std::string::npos) { capabilities.Put(CapabilityFlags::kImageInput);
diff --git a/services/on_device_model/public/cpp/test_support/fake_service.h b/services/on_device_model/public/cpp/test_support/fake_service.h index 2496132..c797250f 100644 --- a/services/on_device_model/public/cpp/test_support/fake_service.h +++ b/services/on_device_model/public/cpp/test_support/fake_service.h
@@ -212,7 +212,7 @@ void LoadModel(mojom::LoadModelParamsPtr params, mojo::PendingReceiver<mojom::OnDeviceModel> model, LoadModelCallback callback) override; - void GetCapabilities(ModelAssets assets, + void GetCapabilities(ModelFile model_file, GetCapabilitiesCallback callback) override; void LoadTextSafetyModel( mojom::TextSafetyModelParamsPtr params,
diff --git a/services/on_device_model/public/mojom/BUILD.gn b/services/on_device_model/public/mojom/BUILD.gn index 7f61f2b..0d7be5f 100644 --- a/services/on_device_model/public/mojom/BUILD.gn +++ b/services/on_device_model/public/mojom/BUILD.gn
@@ -32,6 +32,13 @@ { mojom = "on_device_model.mojom.ModelAssets" cpp = "::on_device_model::ModelAssets" + default_constructible = false + move_only = true + }, + { + mojom = "on_device_model.mojom.ModelFile" + cpp = "::on_device_model::ModelFile" + default_constructible = false move_only = true }, { @@ -61,6 +68,7 @@ "//services/on_device_model/public/cpp/adaptation_assets_mojom_traits.h", "//services/on_device_model/public/cpp/capabilities_mojom_traits.h", "//services/on_device_model/public/cpp/model_assets_mojom_traits.h", + "//services/on_device_model/public/cpp/model_file_mojom_traits.h", ] traits_sources = [ "//services/on_device_model/ml/chrome_ml_types_traits.cc" ]
diff --git a/services/on_device_model/public/mojom/on_device_model_service.mojom b/services/on_device_model/public/mojom/on_device_model_service.mojom index 75961c59..7758a2ef 100644 --- a/services/on_device_model/public/mojom/on_device_model_service.mojom +++ b/services/on_device_model/public/mojom/on_device_model_service.mojom
@@ -11,17 +11,21 @@ import "sandbox/policy/mojom/sandbox.mojom"; import "services/on_device_model/public/mojom/on_device_model.mojom"; +// Model weights could be passed as an opened file or a file path. +union ModelFile { + // This should be used with the GPU backend. + // + // TODO(b/313919363): This should be a ReadOnlyFile. + mojo_base.mojom.File file; + + // This should be used with the APU backend. + mojo_base.mojom.FilePath path; +}; + // Opened file resources needed to define the model. struct ModelAssets { - // Model weights could be passed as an opened file or a file path. - // The backend type will decide which one should be used, or - // which one is preferred if both are passed. If both are unset, - // usually the operation should fail. - // APU backend: weights_path should be used. - // GPU backend: weights should be used. - // TODO(b/313919363): This should also be a ReadOnlyFile. - mojo_base.mojom.File? weights; - mojo_base.mojom.FilePath? weights_path; + // Model weights. The backend may prefer these to be passed a certain way. + ModelFile weights; // Currently the only usage of sp model will be passed by file path. mojo_base.mojom.FilePath? sp_model_path; @@ -128,9 +132,9 @@ LoadModel(LoadModelParams params, pending_receiver<OnDeviceModel> model) => (LoadModelResult result); - // Returns the capabilities for a model specified by `params`. Note that this + // Returns the capabilities for a model specified by `weights`. Note that this // will not load the model, so is much cheaper than calling LoadModel(). - GetCapabilities(ModelAssets assets) => (Capabilities capabilities); + GetCapabilities(ModelFile weights) => (Capabilities capabilities); // Initializes a new TextSafetyModel with the provided params. // The model is disconnected on any errors with it.
diff --git a/services/preferences/tracked/OWNERS b/services/preferences/tracked/OWNERS index f1e6849..8550559 100644 --- a/services/preferences/tracked/OWNERS +++ b/services/preferences/tracked/OWNERS
@@ -1,2 +1,3 @@ gab@chromium.org proberge@chromium.org +wfh@chromium.org
diff --git a/services/screen_ai/screen_ai_ocr_perf_test.cc b/services/screen_ai/screen_ai_ocr_perf_test.cc index 7ee0092..36c7443 100644 --- a/services/screen_ai/screen_ai_ocr_perf_test.cc +++ b/services/screen_ai/screen_ai_ocr_perf_test.cc
@@ -17,6 +17,7 @@ #include "base/json/json_file_value_serializer.h" #include "base/logging.h" #include "base/strings/string_split.h" +#include "base/system/sys_info.h" #include "base/test/bind.h" #include "base/time/time.h" #include "base/timer/lap_timer.h" @@ -113,8 +114,6 @@ library_->SetFileContentFunctions(&OcrTestEnvironment::GetDataSize, &OcrTestEnvironment::CopyData); - - InitOcr(); } void InitOcr() { @@ -151,12 +150,26 @@ void Benchmark(const std::string& metrics_name, base::RepeatingClosure target_ops) { + // Records the available memory before library initialization. + int base_mem = static_cast<int>( + (base::SysInfo::AmountOfAvailablePhysicalMemory() / (1024 * 1024))); + InitOcr(); + base::LapTimer lap_timer(kWarmUpIterationCount, base::Seconds(10), 1); do { target_ops.Run(); lap_timer.NextLap(); } while (lap_timer.NumLaps() < kActualIterationCount); + // Records the memory difference after `PerformOcr()`. + // TODO(crbug.com/415902702): Considers to put memory into a separate test. + int mem_diff = + base_mem - + static_cast<int>(base::SysInfo::AmountOfAvailablePhysicalMemory() / + (1024 * 1024)); + perf_values_.Set(metrics_name + "_mem", mem_diff); + LOG(INFO) << "Perf: " << metrics_name << "_mem => " << mem_diff << " mb"; + int avg_duration = lap_timer.TimePerLap().InMilliseconds(); perf_values_.Set(metrics_name, avg_duration); LOG(INFO) << "Perf: " << metrics_name << " => " << avg_duration << " ms";
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index e969e8d..4e8cd2c 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -440,22 +440,6 @@ ] } ], - "AndroidAutofillLazyFrameworkWrapper": [ - { - "platforms": [ - "android", - "android_webview" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "AndroidAutofillLazyFrameworkWrapper" - ] - } - ] - } - ], "AndroidBookmarkBar": [ { "platforms": [ @@ -10006,6 +9990,7 @@ "GlicDevelopmentCookies", "GlicFreURLConfig", "GlicKeyboardShortcutNewBadge", + "GlicLearnMoreURLConfig", "GlicURLConfig", "IPH_GlicPromo" ] @@ -10013,6 +9998,22 @@ ] } ], + "GlicTieredRollout": [ + { + "platforms": [ + "mac", + "windows" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "GlicTieredRollout" + ] + } + ] + } + ], "GlobalVaapiLock": [ { "platforms": [
diff --git a/third_party/androidx/build.gradle b/third_party/androidx/build.gradle index a71ddc0..17d7c9e6 100644 --- a/third_party/androidx/build.gradle +++ b/third_party/androidx/build.gradle
@@ -304,7 +304,7 @@ google() maven { // This URL is generated by the fetch_all_androidx.py script. - url 'https://androidx.dev/snapshots/builds/13471930/artifacts/repository' + url 'https://androidx.dev/snapshots/builds/13474078/artifacts/repository' } mavenCentral() }
diff --git a/third_party/angle b/third_party/angle index 214b7ce..db33baf 160000 --- a/third_party/angle +++ b/third_party/angle
@@ -1 +1 @@ -Subproject commit 214b7ce20bb6a308c8e929743cd8402f3f5204dd +Subproject commit db33baf4eb0d7954f0110cddc30acb9cdc12e2d4
diff --git a/third_party/blink/renderer/bindings/generated_in_core.gni b/third_party/blink/renderer/bindings/generated_in_core.gni index fb18143b..e0b3fe84 100644 --- a/third_party/blink/renderer/bindings/generated_in_core.gni +++ b/third_party/blink/renderer/bindings/generated_in_core.gni
@@ -637,6 +637,8 @@ "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_axis.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_behavior.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_behavior.h", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_container.cc", + "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_container.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_logical_position.cc", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_logical_position.h", "$root_gen_dir/third_party/blink/renderer/bindings/core/v8/v8_scroll_restoration.cc",
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index eb2604a..9168f578 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -46,6 +46,7 @@ #include "third_party/blink/renderer/bindings/core/v8/v8_aria_notification_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_check_visibility_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_pointer_lock_options.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_scroll_container.h" #include "third_party/blink/renderer/bindings/core/v8/v8_scroll_into_view_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_scroll_to_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_shadow_root_init.h" @@ -1856,8 +1857,13 @@ mojom::blink::ScrollIntoViewParamsPtr params = scroll_into_view_util::CreateScrollIntoViewParams(*options, *GetComputedStyle()); + Element* container = nullptr; + if (options->hasContainer() && + options->container() == V8ScrollContainer::Enum::kNearest) { + container = this; + } - ScrollIntoViewNoVisualUpdate(std::move(params)); + ScrollIntoViewNoVisualUpdate(std::move(params), container); } // TODO(crbug.com/385129957): This only searches up to the nearest scroll
diff --git a/third_party/blink/renderer/core/frame/scroll_into_view_options.idl b/third_party/blink/renderer/core/frame/scroll_into_view_options.idl index 117a7aeb..3ff462c1 100644 --- a/third_party/blink/renderer/core/frame/scroll_into_view_options.idl +++ b/third_party/blink/renderer/core/frame/scroll_into_view_options.idl
@@ -1,6 +1,8 @@ enum ScrollLogicalPosition { "start", "center", "end", "nearest" }; +enum ScrollContainer { "all", "nearest" }; dictionary ScrollIntoViewOptions : ScrollOptions { ScrollLogicalPosition block = "start"; [ImplementedAs=inlinePosition] ScrollLogicalPosition inline = "nearest"; -}; \ No newline at end of file + [RuntimeEnabled=ScrollIntoViewNearest] ScrollContainer container = "all"; +};
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_test.cc b/third_party/blink/renderer/core/frame/web_frame_widget_test.cc index 0976cac7..57ba4690 100644 --- a/third_party/blink/renderer/core/frame/web_frame_widget_test.cc +++ b/third_party/blink/renderer/core/frame/web_frame_widget_test.cc
@@ -23,6 +23,7 @@ #include "third_party/blink/renderer/core/dom/events/add_event_listener_options_resolved.h" #include "third_party/blink/renderer/core/dom/events/native_event_listener.h" #include "third_party/blink/renderer/core/editing/editing_utilities.h" +#include "third_party/blink/renderer/core/execution_context/agent.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/frame/web_frame_widget_impl.h" #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" @@ -32,6 +33,7 @@ #include "third_party/blink/renderer/core/html/html_div_element.h" #include "third_party/blink/renderer/core/html/html_image_element.h" #include "third_party/blink/renderer/core/input/event_handler.h" +#include "third_party/blink/renderer/core/layout/layout_image.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "third_party/blink/renderer/core/testing/sim/sim_request.h" #include "third_party/blink/renderer/core/testing/sim/sim_test.h" @@ -692,6 +694,41 @@ } } +TEST_F(WebFrameWidgetImplSimTest, SpeculativeImageDecodeBeforeLayout) { + // Check that a speculative decode can start as soon as an img element gets a + // src based on prior layout information, without waiting for a subsequent + // layout to happen. + base::test::ScopedFeatureList feature_list( + features::kSpeculativeImageDecodes); + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <html><body><img width=13 height=17/></body></html> + )HTML"); + Compositor().BeginFrame(); + HTMLImageElement* image = + To<HTMLImageElement>(GetDocument().QuerySelector(AtomicString("img"))); + LayoutImage* layout_image = To<LayoutImage>(image->GetLayoutObject()); + EXPECT_EQ(layout_image->CachedResourcePriority().visibility, + ResourcePriority::kVisible); + // Decode size should be based on layout size (note that this does not + // actually match the intrinsic size of the data URL below. + EXPECT_EQ(layout_image->CachedSpeculativeDecodeSize(), gfx::Size(13, 17)); + + image->setAttribute( + html_names::kSrcAttr, + AtomicString("data:image/" + "png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+" + "9AAAAAXNSR0IArs4c6QAAABhJREFUKFNjbGj48J+BCMA4qhBfKFE/" + "eACQKR1hvTllHQAAAABJRU5ErkJggg==")); + // The fetch is initiated synchronously from a microtask after src is set. For + // a data URL the load will also finish synchronously, and the speculative + // decode should have been triggered, based on pre-computed visibility. + EXPECT_CALL(*MockMainFrameWidget(), RequestDecode(_, _, true)).Times(1); + GetDocument().GetAgent().PerformMicrotaskCheckpoint(); +} + #if BUILDFLAG(IS_WIN) struct ProximateBoundsCollectionArgs final { base::RepeatingCallback<gfx::Rect(const Document&)>
diff --git a/third_party/blink/renderer/core/html/html_image_element.cc b/third_party/blink/renderer/core/html/html_image_element.cc index c742a6a7..11bab51 100644 --- a/third_party/blink/renderer/core/html/html_image_element.cc +++ b/third_party/blink/renderer/core/html/html_image_element.cc
@@ -520,6 +520,9 @@ LayoutImage* image = MakeGarbageCollected<LayoutImage>(this); image->SetImageResource(MakeGarbageCollected<LayoutImageResource>()); image->SetImageDevicePixelRatio(image_device_pixel_ratio_); + if (base::FeatureList::IsEnabled(features::kSpeculativeImageDecodes)) { + GetDocument().View()->RegisterForLifecycleNotifications(this); + } return image; } case LayoutDisposition::kCollapsed: // Falls through. @@ -760,9 +763,27 @@ is_ad_related_ = true; } +void HTMLImageElement::DidFinishLayout() { + if (base::FeatureList::IsEnabled(features::kSpeculativeImageDecodes)) { + if (LayoutImage* layout_image = DynamicTo<LayoutImage>(GetLayoutObject())) { + // Populate cached values for load priority and speculative decode + // parameters. + layout_image->ComputeResourcePriority(); + layout_image->ComputeSpeculativeDecodeSize(); + layout_image->ComputeSpeculativeDecodeQuality(); + // Once the image has a source ResourceFetcher will take over the updates. + if (!is_ad_related_ && GetImageLoader().GetContent()) { + GetDocument().View()->UnregisterFromLifecycleNotifications(this); + } + } + } +} + void HTMLImageElement::DidFinishLifecycleUpdate( const LocalFrameView& local_frame_view) { - DCHECK(is_ad_related_); + if (!is_ad_related_) { + return; + } // Scope to the outermost frame to avoid counting image ads that are (likely) // already in ad iframes.
diff --git a/third_party/blink/renderer/core/html/html_image_element.h b/third_party/blink/renderer/core/html/html_image_element.h index 7e6ed80..e8f5f43 100644 --- a/third_party/blink/renderer/core/html/html_image_element.h +++ b/third_party/blink/renderer/core/html/html_image_element.h
@@ -261,6 +261,7 @@ void CreateMediaQueryListIfDoesNotExist(); // LocalFrameView::LifecycleNotificationObserver + void DidFinishLayout() override; void DidFinishLifecycleUpdate(const LocalFrameView&) override; Member<HTMLImageLoader> image_loader_;
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h index 70bf1252..40c10b80 100644 --- a/third_party/blink/renderer/core/layout/layout_box.h +++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -854,7 +854,7 @@ PhysicalRect ClippingRect(const PhysicalOffset& location) const; void ImageChanged(WrappedImagePtr, CanDeferInvalidation) override; - ResourcePriority ComputeResourcePriority() const final; + ResourcePriority ComputeResourcePriority() const override; PositionWithAffinity PositionForPointInFragments(const PhysicalOffset&) const;
diff --git a/third_party/blink/renderer/core/layout/layout_image.cc b/third_party/blink/renderer/core/layout/layout_image.cc index 1a22f77..10eb7ce 100644 --- a/third_party/blink/renderer/core/layout/layout_image.cc +++ b/third_party/blink/renderer/core/layout/layout_image.cc
@@ -217,6 +217,36 @@ return !is_fixed_sized; } +ResourcePriority LayoutImage::ComputeResourcePriority() const { + speculative_decode_parameters_.cached_resource_priority = + LayoutReplaced::ComputeResourcePriority(); + return speculative_decode_parameters_.cached_resource_priority; +} + +ResourcePriority LayoutImage::CachedResourcePriority() const { + return speculative_decode_parameters_.cached_resource_priority; +} + +gfx::Size LayoutImage::ComputeSpeculativeDecodeSize() const { + speculative_decode_parameters_.cached_speculative_decode_size = + LayoutReplaced::ComputeSpeculativeDecodeSize(); + return speculative_decode_parameters_.cached_speculative_decode_size; +} + +gfx::Size LayoutImage::CachedSpeculativeDecodeSize() const { + return speculative_decode_parameters_.cached_speculative_decode_size; +} + +InterpolationQuality LayoutImage::ComputeSpeculativeDecodeQuality() const { + speculative_decode_parameters_.cached_speculative_decode_quality = + LayoutReplaced::ComputeSpeculativeDecodeQuality(); + return speculative_decode_parameters_.cached_speculative_decode_quality; +} + +InterpolationQuality LayoutImage::CachedSpeculativeDecodeQuality() const { + return speculative_decode_parameters_.cached_speculative_decode_quality; +} + bool LayoutImage::InvalidateLayoutOnNaturalSizeChange() { SetIntrinsicLogicalWidthsDirty();
diff --git a/third_party/blink/renderer/core/layout/layout_image.h b/third_party/blink/renderer/core/layout/layout_image.h index b0efc66a..ef6e86d 100644 --- a/third_party/blink/renderer/core/layout/layout_image.h +++ b/third_party/blink/renderer/core/layout/layout_image.h
@@ -101,6 +101,13 @@ ImageChanged(image_resource_->ImagePtr(), CanDeferInvalidation::kNo); } + ResourcePriority ComputeResourcePriority() const final; + ResourcePriority CachedResourcePriority() const final; + gfx::Size ComputeSpeculativeDecodeSize() const final; + gfx::Size CachedSpeculativeDecodeSize() const final; + InterpolationQuality ComputeSpeculativeDecodeQuality() const final; + InterpolationQuality CachedSpeculativeDecodeQuality() const final; + const char* GetName() const override { NOT_DESTROYED(); return "LayoutImage"; @@ -191,6 +198,12 @@ friend class MutableForPainting; PhysicalRect last_paint_rect_; + + mutable struct { + ResourcePriority cached_resource_priority; + gfx::Size cached_speculative_decode_size; + InterpolationQuality cached_speculative_decode_quality; + } speculative_decode_parameters_; }; template <>
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc index 7ebadec3..4b290c86 100644 --- a/third_party/blink/renderer/core/layout/layout_object.cc +++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -4408,7 +4408,7 @@ return true; } -InterpolationQuality LayoutObject::GetSpeculativeDecodeQuality() const { +InterpolationQuality LayoutObject::ComputeSpeculativeDecodeQuality() const { NOT_DESTROYED(); return StyleRef().GetInterpolationQuality(); }
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h index 6b637b7a..cd0e0a7 100644 --- a/third_party/blink/renderer/core/layout/layout_object.h +++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -2665,7 +2665,7 @@ void NotifyImageFullyRemoved(ImageResourceContent*) override; bool WillRenderImage() final; bool GetImageAnimationPolicy(mojom::blink::ImageAnimationPolicy&) final; - InterpolationQuality GetSpeculativeDecodeQuality() const final; + InterpolationQuality ComputeSpeculativeDecodeQuality() const override; void Remove() { NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/layout/layout_replaced.cc b/third_party/blink/renderer/core/layout/layout_replaced.cc index acaa092b..dcd39dca 100644 --- a/third_party/blink/renderer/core/layout/layout_replaced.cc +++ b/third_party/blink/renderer/core/layout/layout_replaced.cc
@@ -435,7 +435,7 @@ return LayoutBox::PositionForPoint(point); } -gfx::Size LayoutReplaced::GetSpeculativeDecodeSize() const { +gfx::Size LayoutReplaced::ComputeSpeculativeDecodeSize() const { NOT_DESTROYED(); return ReplacedContentRect().PixelSnappedSize(); }
diff --git a/third_party/blink/renderer/core/layout/layout_replaced.h b/third_party/blink/renderer/core/layout/layout_replaced.h index 7a0cc07..5f88674e 100644 --- a/third_party/blink/renderer/core/layout/layout_replaced.h +++ b/third_party/blink/renderer/core/layout/layout_replaced.h
@@ -159,7 +159,7 @@ } // ImageResourceObserver - gfx::Size GetSpeculativeDecodeSize() const override; + gfx::Size ComputeSpeculativeDecodeSize() const override; private: // Computes a rect, relative to the element's content's natural size, that
diff --git a/third_party/blink/renderer/core/loader/image_loader.cc b/third_party/blink/renderer/core/loader/image_loader.cc index 74dc54f..f072abe 100644 --- a/third_party/blink/renderer/core/loader/image_loader.cc +++ b/third_party/blink/renderer/core/loader/image_loader.cc
@@ -617,6 +617,7 @@ // dispatched. if (new_image_content) { new_image_content->AddObserver(this); + document.Fetcher()->MaybeStartSpeculativeImageDecode(); } if (old_image_content) { old_image_content->RemoveObserver(this);
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc index fa680b5..43aac37 100644 --- a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc +++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
@@ -151,6 +151,10 @@ ProhibitAddRemoveObserverInScope prohibit_add_remove_observer_in_scope( this); observers_.insert(observer); + ApplyPriorityAndSpeculativeDecodeParams( + observer->CachedResourcePriority(), + observer->CachedSpeculativeDecodeSize(), + observer->CachedSpeculativeDecodeQuality()); } if (info_->IsCacheValidator()) @@ -188,30 +192,6 @@ info_->DidRemoveClientOrObserver(); } -static void PriorityFromObserver( - const ImageResourceObserver* observer, - ResourcePriority& priority, - ResourcePriority& priority_excluding_image_loader) { - ResourcePriority next_priority = observer->ComputeResourcePriority(); - if (next_priority.is_lcp_resource) { - // Mark the resource as predicted LCP despite its visibility. - priority.is_lcp_resource = true; - priority_excluding_image_loader.is_lcp_resource = true; - } - - if (next_priority.visibility == ResourcePriority::kNotVisible) - return; - - priority.visibility = ResourcePriority::kVisible; - priority.intra_priority_value += next_priority.intra_priority_value; - - if (next_priority.source != ResourcePriority::Source::kImageLoader) { - priority_excluding_image_loader.visibility = ResourcePriority::kVisible; - priority_excluding_image_loader.intra_priority_value += - next_priority.intra_priority_value; - } -} - void ImageResourceContent::UpdateResourceInfoFromObservers() { ProhibitAddRemoveObserverInScope prohibit_add_remove_observer_in_scope(this); @@ -221,12 +201,10 @@ cached_info_.max_interpolation_quality_ = kInterpolationNone; auto update = [this](const ImageResourceObserver* observer) -> void { - PriorityFromObserver(observer, cached_info_.priority_, - cached_info_.priority_excluding_image_loader_); - cached_info_.max_size_.SetToMax(observer->GetSpeculativeDecodeSize()); - cached_info_.max_interpolation_quality_ = - std::max(cached_info_.max_interpolation_quality_, - observer->GetSpeculativeDecodeQuality()); + ApplyPriorityAndSpeculativeDecodeParams( + observer->ComputeResourcePriority(), + observer->ComputeSpeculativeDecodeSize(), + observer->ComputeSpeculativeDecodeQuality()); }; for (const auto& it : finished_observers_) { @@ -237,6 +215,35 @@ } } +void ImageResourceContent::ApplyPriorityAndSpeculativeDecodeParams( + const ResourcePriority& new_priority, + const gfx::Size& new_size, + InterpolationQuality new_quality) { + if (new_priority.is_lcp_resource) { + // Mark the resource as predicted LCP despite its visibility. + cached_info_.priority_.is_lcp_resource = true; + cached_info_.priority_excluding_image_loader_.is_lcp_resource = true; + } + + if (new_priority.visibility == ResourcePriority::kNotVisible) { + return; + } + + cached_info_.priority_.visibility = ResourcePriority::kVisible; + cached_info_.priority_.intra_priority_value += + new_priority.intra_priority_value; + + if (new_priority.source != ResourcePriority::Source::kImageLoader) { + cached_info_.priority_excluding_image_loader_.visibility = + ResourcePriority::kVisible; + cached_info_.priority_excluding_image_loader_.intra_priority_value += + new_priority.intra_priority_value; + } + cached_info_.max_size_.SetToMax(new_size); + cached_info_.max_interpolation_quality_ = + std::max(cached_info_.max_interpolation_quality_, new_quality); +} + bool ImageResourceContent::CanBeSpeculativelyDecoded() const { for (const auto& it : finished_observers_) { if (!it.key->CanBeSpeculativelyDecoded()) {
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.h b/third_party/blink/renderer/core/loader/resource/image_resource_content.h index b7e3ae8..23c67ac 100644 --- a/third_party/blink/renderer/core/loader/resource/image_resource_content.h +++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.h
@@ -252,6 +252,10 @@ void HandleObserverFinished(ImageResourceObserver*); void UpdateToLoadedContentStatus(ResourceStatus); void UpdateImageAnimationPolicy(); + void ApplyPriorityAndSpeculativeDecodeParams( + const ResourcePriority& new_priority, + const gfx::Size& new_size, + InterpolationQuality new_quality); class ProhibitAddRemoveObserverInScope : public base::AutoReset<bool> { public:
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_observer.h b/third_party/blink/renderer/core/loader/resource/image_resource_observer.h index 05b9b03..cf28f201 100644 --- a/third_party/blink/renderer/core/loader/resource/image_resource_observer.h +++ b/third_party/blink/renderer/core/loader/resource/image_resource_observer.h
@@ -82,14 +82,24 @@ return false; } - // Return the observer's requested resource priority. An implementation of - // this method is not allowed to add or remove ImageResource observers. + // Compute and return the observer's requested resource priority. An + // implementation of this method is not allowed to add or remove ImageResource + // observers. virtual ResourcePriority ComputeResourcePriority() const { return ResourcePriority(); } + // Return the last computed ResourcePriority, if available. + virtual ResourcePriority CachedResourcePriority() const { + return ResourcePriority(); + } + virtual bool CanBeSpeculativelyDecoded() const { return true; } - virtual gfx::Size GetSpeculativeDecodeSize() const { return gfx::Size(); } - virtual InterpolationQuality GetSpeculativeDecodeQuality() const { + virtual gfx::Size ComputeSpeculativeDecodeSize() const { return gfx::Size(); } + virtual gfx::Size CachedSpeculativeDecodeSize() const { return gfx::Size(); } + virtual InterpolationQuality ComputeSpeculativeDecodeQuality() const { + return kInterpolationNone; + } + virtual InterpolationQuality CachedSpeculativeDecodeQuality() const { return kInterpolationNone; }
diff --git a/third_party/blink/renderer/core/scroll/scroll_into_view_util.cc b/third_party/blink/renderer/core/scroll/scroll_into_view_util.cc index 342cbe48..20974abc 100644 --- a/third_party/blink/renderer/core/scroll/scroll_into_view_util.cc +++ b/third_party/blink/renderer/core/scroll/scroll_into_view_util.cc
@@ -282,10 +282,6 @@ break; } - if (stop_at.Contains(current_box)) { - break; - } - // If the scroll was stopped prior to reaching the local root, we cannot // return a rect since the caller cannot know which frame it's relative to. std::optional<LayoutBox*> next_box_opt = @@ -296,13 +292,21 @@ LayoutBox* next_box = *next_box_opt; - // TODO(https://crbug.com/391627364): for now, we should not leave the frame - // for scroll-marker-group containers, and `container` check indicates this - // case. But later we will support more cases where `container` is not - // nullptr. - if (container && next_box && - next_box->GetFrame() != current_box->GetFrame()) { - break; + if (container) { + // If we just found a scrolling container that is or contains the + // requested container, stop scrolling. + // Additionally stop scrolling if we would continue on to scroll a + // different frame. + // TODO(crbug.com/365913982): We shouldn't scroll the scroll container + // on which scrollIntoView was called, which would obviate the need + // for the check that area_to_scroll is not the target. + // TODO(crbug.com/416730010): Revisit this if we allow passing a + // container from a different document. + if ((area_to_scroll && area_to_scroll->GetLayoutBox() != &box && + stop_at.Contains(current_box)) || + (next_box && next_box->GetFrame() != current_box->GetFrame())) { + return std::nullopt; + } } AdjustRectAndParamsForParentFrame(*current_box, next_box,
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h index 6f329ce..37275b2e 100644 --- a/third_party/blink/renderer/core/style/computed_style.h +++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -585,9 +585,22 @@ // column-rule-width GapDataList<int> ColumnRuleWidth() const { - if (!BorderStyleIsVisible(ColumnRuleStyle())) { + // The legacy version of 'column-rule-width' behaved such that if + // 'column-rule-style' was not visible, we'd treat the width as 0. We will + // continue to apply this rule for 'column-rule-width' if a single value is + // provided for 'column-rule-width' and 'column-rule-style' for backwards + // compat. However, if one of the properties is a list of values, we will + // return the true computed value of the width as specified by the author + // (per CSSWG resolution [1]). + // + // [1]: https://github.com/w3c/csswg-drafts/issues/11494 + const GapDataList<EBorderStyle> rule_style = ColumnRuleStyle(); + if (rule_style.HasSingleValue() && + ColumnRuleWidthInternal().HasSingleValue() && + !BorderStyleIsVisible(rule_style)) { return GapDataList<int>(0); } + return ColumnRuleWidthInternal(); }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc index 3152c3b..7c3c219 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc +++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -1496,7 +1496,6 @@ resource->GetContentStatus() == ResourceStatus::kCached && base::FeatureList::IsEnabled(features::kSpeculativeImageDecodes)) { speculative_decode_candidate_images_.insert(resource); - MaybeStartSpeculativeImageDecode(); } break; } @@ -2538,7 +2537,6 @@ resource->GetContentStatus() == ResourceStatus::kCached && base::FeatureList::IsEnabled(features::kSpeculativeImageDecodes)) { speculative_decode_candidate_images_.insert(resource); - MaybeStartSpeculativeImageDecode(); } // Since this resource came from the network stack we only schedule a stale
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h index 26c2401..91136ce 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h +++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
@@ -379,6 +379,8 @@ // changed such that the load should no longer be deferred. void ReloadImagesIfNotDeferred(); + void MaybeStartSpeculativeImageDecode(); + // Populates the provided request's permissions policy. void PopulateResourceRequestPermissionsPolicy( network::ResourceRequest* request); @@ -488,7 +490,6 @@ void MaybeSaveResourceToStrongReference(Resource* resource); - void MaybeStartSpeculativeImageDecode(); void SpeculativeImageDecodeFinished(); enum class RevalidationPolicy {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index cf649153..3cfbfea9 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -4112,6 +4112,10 @@ name: "ScrollEndEvents", status: "stable", }, + { + name: "ScrollIntoViewNearest", + status: "test" + }, // TODO(355460994): Remove after M129. // scroll into root frame didn't take scrollbar and borders into account, // while this is fixed, to avoid any web compat issues we have this killswitch.
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials index 1ff99026..48efd7b 100644 --- a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials +++ b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
@@ -76,6 +76,7 @@ crbug.com/1209223 [ Linux ] external/wpt/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html [ Failure ] # ====== New tests from wpt-importer added here ====== +external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html?3-4 [ Crash Pass ] crbug.com/415690774 external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https.html?5-6 [ Crash ] crbug.com/415690774 external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html?5-6 [ Crash ] crbug.com/407618687 external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-same-origin.https.html?3-4 [ Crash ]
diff --git a/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility b/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility index 109d48e8..a24deeba 100644 --- a/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility +++ b/third_party/blink/web_tests/FlagExpectations/force-renderer-accessibility
@@ -59,6 +59,3 @@ # realistic scenario in the wild so we deem it safe to simply skip this test # when accessibility is enabled. fast/events/touch/gesture/gesture-tap-frame-move.html [ Skip ] - -# Undiagnosed failure: -crbug.com/1503980 fast/events/scroll-event-handler-count.html [ Skip ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json index 3eb77f29..58b296c 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -26480,6 +26480,19 @@ {} ] ], + "webkit-box-ignores-flex-wrap.tentative.html": [ + "98086f83570d4c5b4c6251f31093744442a907ca", + [ + null, + [ + [ + "/compat/green-ref.html", + "==" + ] + ], + {} + ] + ], "webkit-box-rtl-flex.html": [ "d20b3a0a9dbab237a799f62504b9b1ea7b90a564", [ @@ -387638,16 +387651,12 @@ [] ], "resources": { - "fetch-private-permission-denied.html": [ - "7368cf5fcb0b854d4f0eaaed95fa5c3169d572c7", - [] - ], "fetch-private.html": [ - "ede16badf338dc50defad0251f83f8b02f1d3fec", + "b96a207ec33a13e5dd4c53083ac3d73123b23cbb", [] ], "support.sub.js": [ - "ef343b956fe2007089acccc4c6bffe5876ad364a", + "774e34d0a6fe59bab19aca14cb71b6e520acb798", [] ], "target.py": [ @@ -411353,7 +411362,7 @@ [] ], "largest-contentful-paint.idl": [ - "872ba552b0d3b0398be533beb578fdedd260baea", + "d1630cc7daae802bf27107ce0ab07185d0746d7f", [] ], "layout-instability.idl": [ @@ -411677,7 +411686,7 @@ [] ], "service-workers.idl": [ - "d9ff2f651f8235968dd29bd6d7f86e9e8fdcb4cf", + "34af3372401eed53328b8bc5e7ea42b87ddd9b20", [] ], "shape-detection-api.idl": [ @@ -411829,7 +411838,7 @@ [] ], "webauthn.idl": [ - "a33c85e7bad86753211fa7aa9270abac18b1e54e", + "7fbe55e67652b3fd35079d1a06423bb441657bd5", [] ], "webcodecs-aac-codec-registration.idl": [ @@ -487480,6 +487489,13 @@ {} ] ], + "style-src-inline-style-with-csstext.html": [ + "5e812b4aee9d0d081673a0f333f8b29187619c3d", + [ + null, + {} + ] + ], "style-src-multiple-policies-multiple-hashing-algorithms.html": [ "027c61d8c632f2387408b8fb6869dee69bb8913d", [ @@ -529096,7 +529112,7 @@ ] ], "sibling-function-descriptors.tentative.html": [ - "4987eff9b8d4ce6b662cabd0b7729499d7b3e704", + "76d2ff8ee4db660e41ede9b188465a6c31843e3b", [ null, {} @@ -578355,7 +578371,7 @@ }, "local-network-access": { "fetch.tentative.https.html": [ - "649dd2309596fe38e650aa9dbaf661c845dc7f9c", + "9c591f309b75af6731c6d6be9c0907d3b8c22618", [ null, { @@ -665515,7 +665531,7 @@ ] ], "idlharness.html": [ - "5f5d286b356cf5dcffd76f2ed6f689804e8426ba", + "0dd006e711c6d7b02fcbcd4d31a5c11381fe8d69", [ null, {} @@ -711863,6 +711879,19 @@ } ] ], + "smoke": { + "tentative": { + "basic.html": [ + "9bfedf09b1934e00a85cd030388ed470f575f7b0", + [ + null, + { + "testdriver": true + } + ] + ] + } + }, "soft-navigation-detection-main-descendent.tentative.html": [ "96ff55260c3b9553ac5735cc5fe209ee7cfd5f37", [ @@ -714828,10 +714857,12 @@ ] ], "SpeechRecognition-installOnDevice.https.html": [ - "1d1dd35edc26d75419a49120d8a76c4ea7c84233", + "2f5b359c571d6d9cc97815ae6ba46e4249ec569e", [ null, - {} + { + "testdriver": true + } ] ], "SpeechRecognition-onerror.https.html": [ @@ -773239,7 +773270,7 @@ ] ], "qdq_subgraph.https.any.js": [ - "5ec9ff80a22ce74bb9492b5bca747ce717c1ba46", + "aa816cce7fb2cd3d0a3f81d0453c8c199bf052fe", [ "webnn/conformance_tests/qdq_subgraph.https.any.html?cpu", {
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/style-src/style-src-inline-style-with-csstext.html b/third_party/blink/web_tests/external/wpt/content-security-policy/style-src/style-src-inline-style-with-csstext.html new file mode 100644 index 0000000..5e812b4 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/content-security-policy/style-src/style-src-inline-style-with-csstext.html
@@ -0,0 +1,29 @@ +<!doctype html> +<html> +<head> + <meta http-equiv="Content-Security-Policy" content="style-src 'self';"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + + <script> + var t = async_test("Manipulating cssText should be allowed with 'self'"); + document.addEventListener("securitypolicyviolation", t.unreached_func("Should not trigger a security policy violation")); + </script> +</head> +<body> + <div id='log'></div> + + <div id="content">Lorem ipsum</div> + + <script> + t.step(function() { + var contentEl = document.getElementById("content"); + contentEl.style.cssText = 'margin-left: 2px;'; + var marginLeftVal = getComputedStyle(contentEl).getPropertyValue('margin-left'); + assert_equals(marginLeftVal, "2px"); + t.done(); + }); + </script> + +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-col-rule-width.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-col-rule-width.html new file mode 100644 index 0000000..db7b97e --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-col-rule-width.html
@@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Gap Decorations: Ensure getComputedStyle for column-rule-width is as specified with multiple values</title> +<link rel="help" href="https://drafts.csswg.org/css-gaps-1/#column-row-rule-width"> +<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com"> +<script src="/resources/testharness.js" type="text/javascript"></script> +<script src="/resources/testharnessreport.js" type="text/javascript"></script> +</head> +<body> +<div id="target1"></div> +<div id="target2"></div> +<div id="target3"></div> +<style> + #target1 { + column-rule-width: thin; + } + + #target2 { + column-rule-width: 5px 10px 15px; + } + + #target3 { + column-rule-width: repeat(auto, 5px); + } +</style> +<script> + test(function() { + const containerStyle = window.getComputedStyle(document.querySelector('#target1')); + const columnRuleWidth = containerStyle.getPropertyValue('column-rule-width'); + assert_equals(columnRuleWidth, '0px'); + + }, "`column-rule-width` should be `0px` when `column-rule-style` is `none` with single value"); + + test(function() { + const containerStyle = window.getComputedStyle(document.querySelector('#target2')); + const columnRuleWidth = containerStyle.getPropertyValue('column-rule-width'); + assert_equals(columnRuleWidth, '5px 10px 15px'); + + }, "`column-rule-width` should be as specified regardless of `column-rule-style` with multiple values"); + + test(function() { + const containerStyle = window.getComputedStyle(document.querySelector('#target3')); + const columnRuleWidth = containerStyle.getPropertyValue('column-rule-width'); + assert_equals(columnRuleWidth, 'repeat(auto, 5px)'); + + }, "`column-rule-width` should be as specified regardless of `column-rule-style` with multiple (repeat) values"); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/resources/scrollIntoView-frame.html b/third_party/blink/web_tests/external/wpt/css/cssom-view/resources/scrollIntoView-frame.html new file mode 100644 index 0000000..ee2be2f --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/resources/scrollIntoView-frame.html
@@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<meta name="viewport" content="initial-scale=1"> +<style> +body { + height: 600px; +} +#target { + position: absolute; + top: 400px; + height: 200px; +} +</style> +<body> +<div id="target"></div> +</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/scrollIntoView-container.html b/third_party/blink/web_tests/external/wpt/css/cssom-view/scrollIntoView-container.html new file mode 100644 index 0000000..3b241ea --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/scrollIntoView-container.html
@@ -0,0 +1,84 @@ +<!DOCTYPE html> +<title>CSSOM View - scrollIntoView container option</title> +<meta charset="utf-8"> +<meta name="viewport" content="initial-scale=1"> +<link rel="author" title="Rob Flack" href="mailto:flackr@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollintoview"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +.scroller { + overflow: auto; + height: 200px; +} +.spacer { + height: 400px; +} +#target { + height: 200px; +} +</style> +<script> +let setFrameLoaded = null; +let frameLoaded = new Promise(resolve => { + setFrameLoaded = resolve; +}); +</script> +<div id="outer" class="scroller"> + <div class="spacer"></div> + <div id="inner" class="scroller"> + <div class="spacer"></div> + <div id="target"></div> + <iframe id="frame" height="200" src="resources/scrollIntoView-frame.html" onload="setFrameLoaded()"></iframe> + </div> +</div> +<script> +const outer = document.getElementById('outer'); +const inner = document.getElementById('inner'); +const target = document.getElementById('target'); + +function reset() { + outer.scrollTop = 0; + inner.scrollTop = 0; +} + +test(() => { + reset(); + target.scrollIntoView(); + assert_equals(inner.scrollTop, 400, '#inner scrollTop'); + assert_equals(outer.scrollTop, 400, '#outer scrollTop'); +}, `scrollIntoView() defaults to scrolling ancestors`); + +test(() => { + reset(); + target.scrollIntoView({container: 'all'}); + assert_equals(inner.scrollTop, 400, '#inner scrollTop'); + assert_equals(outer.scrollTop, 400, '#outer scrollTop'); +}, `scrollIntoView({container: 'all'}) scrolls ancestors`); + +test(() => { + reset(); + target.scrollIntoView({container: 'nearest'}); + assert_equals(inner.scrollTop, 400, '#inner scrollTop'); + assert_equals(outer.scrollTop, 0, '#outer scrollTop'); +}, `scrollIntoView({container: 'nearest'}) only scrolls nearest scroll container`); + +test(() => { + reset(); + inner.scrollIntoView({container: 'nearest'}); + assert_equals(outer.scrollTop, 400, '#outer scrollTop'); + assert_equals(inner.scrollTop, 0, '#inner scrollTop'); +}, `scrollIntoView({container: 'nearest'}) doesn't stop at itself`); + +promise_test(async () => { + reset(); + await frameLoaded; + const frameDoc = document.getElementById("frame").contentDocument; + const frameTarget = frameDoc.getElementById("target"); + frameTarget.scrollIntoView({container: 'nearest'}); + assert_equals(frameDoc.scrollingElement.scrollTop, 400, 'frame scrollingElement scrollTop'); + assert_equals(inner.scrollTop, 0, '#inner scrollTop'); + assert_equals(outer.scrollTop, 0, '#outer scrollTop'); +}, `scrollIntoView({container: 'nearest'}) doesn't propagate to outer frames`); + +</script>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/largest-contentful-paint.idl b/third_party/blink/web_tests/external/wpt/interfaces/largest-contentful-paint.idl index 872ba55..d1630cc 100644 --- a/third_party/blink/web_tests/external/wpt/interfaces/largest-contentful-paint.idl +++ b/third_party/blink/web_tests/external/wpt/interfaces/largest-contentful-paint.idl
@@ -5,7 +5,6 @@ [Exposed=Window] interface LargestContentfulPaint : PerformanceEntry { - readonly attribute DOMHighResTimeStamp renderTime; readonly attribute DOMHighResTimeStamp loadTime; readonly attribute unsigned long size; readonly attribute DOMString id; @@ -13,3 +12,5 @@ readonly attribute Element? element; [Default] object toJSON(); }; + +LargestContentfulPaint includes PaintTimingMixin;
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/service-workers.idl b/third_party/blink/web_tests/external/wpt/interfaces/service-workers.idl index d9ff2f6..34af337 100644 --- a/third_party/blink/web_tests/external/wpt/interfaces/service-workers.idl +++ b/third_party/blink/web_tests/external/wpt/interfaces/service-workers.idl
@@ -34,7 +34,7 @@ readonly attribute USVString scope; readonly attribute ServiceWorkerUpdateViaCache updateViaCache; - [NewObject] Promise<undefined> update(); + [NewObject] Promise<ServiceWorkerRegistration> update(); [NewObject] Promise<boolean> unregister(); // event
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webauthn.idl b/third_party/blink/web_tests/external/wpt/interfaces/webauthn.idl index a33c85e..7fbe55e6 100644 --- a/third_party/blink/web_tests/external/wpt/interfaces/webauthn.idl +++ b/third_party/blink/web_tests/external/wpt/interfaces/webauthn.idl
@@ -311,22 +311,37 @@ partial dictionary AuthenticationExtensionsClientInputs { DOMString appid; }; +partial dictionary AuthenticationExtensionsClientInputsJSON { + DOMString appid; +}; partial dictionary AuthenticationExtensionsClientOutputs { boolean appid; }; +partial dictionary AuthenticationExtensionsClientOutputsJSON { + boolean appid; +}; partial dictionary AuthenticationExtensionsClientInputs { DOMString appidExclude; }; +partial dictionary AuthenticationExtensionsClientInputsJSON { + DOMString appidExclude; +}; partial dictionary AuthenticationExtensionsClientOutputs { boolean appidExclude; }; +partial dictionary AuthenticationExtensionsClientOutputsJSON { + boolean appidExclude; +}; partial dictionary AuthenticationExtensionsClientInputs { boolean credProps; }; +partial dictionary AuthenticationExtensionsClientInputsJSON { + boolean credProps; +}; dictionary CredentialPropertiesOutput { boolean rk; @@ -335,33 +350,57 @@ partial dictionary AuthenticationExtensionsClientOutputs { CredentialPropertiesOutput credProps; }; +partial dictionary AuthenticationExtensionsClientOutputsJSON { + CredentialPropertiesOutput credProps; +}; dictionary AuthenticationExtensionsPRFValues { required BufferSource first; BufferSource second; }; +dictionary AuthenticationExtensionsPRFValuesJSON { + required Base64URLString first; + Base64URLString second; +}; dictionary AuthenticationExtensionsPRFInputs { AuthenticationExtensionsPRFValues eval; record<DOMString, AuthenticationExtensionsPRFValues> evalByCredential; }; +dictionary AuthenticationExtensionsPRFInputsJSON { + AuthenticationExtensionsPRFValuesJSON eval; + record<DOMString, AuthenticationExtensionsPRFValuesJSON> evalByCredential; +}; partial dictionary AuthenticationExtensionsClientInputs { AuthenticationExtensionsPRFInputs prf; }; +partial dictionary AuthenticationExtensionsClientInputsJSON { + AuthenticationExtensionsPRFInputsJSON prf; +}; dictionary AuthenticationExtensionsPRFOutputs { boolean enabled; AuthenticationExtensionsPRFValues results; }; +dictionary AuthenticationExtensionsPRFOutputsJSON { + boolean enabled; + AuthenticationExtensionsPRFValuesJSON results; +}; partial dictionary AuthenticationExtensionsClientOutputs { AuthenticationExtensionsPRFOutputs prf; }; +partial dictionary AuthenticationExtensionsClientOutputsJSON { + AuthenticationExtensionsPRFOutputsJSON prf; +}; partial dictionary AuthenticationExtensionsClientInputs { AuthenticationExtensionsLargeBlobInputs largeBlob; }; +partial dictionary AuthenticationExtensionsClientInputsJSON { + AuthenticationExtensionsLargeBlobInputsJSON largeBlob; +}; enum LargeBlobSupport { "required", @@ -373,13 +412,26 @@ boolean read; BufferSource write; }; +dictionary AuthenticationExtensionsLargeBlobInputsJSON { + DOMString support; + boolean read; + Base64URLString write; +}; partial dictionary AuthenticationExtensionsClientOutputs { AuthenticationExtensionsLargeBlobOutputs largeBlob; }; +partial dictionary AuthenticationExtensionsClientOutputsJSON { + AuthenticationExtensionsLargeBlobOutputsJSON largeBlob; +}; dictionary AuthenticationExtensionsLargeBlobOutputs { boolean supported; ArrayBuffer blob; boolean written; }; +dictionary AuthenticationExtensionsLargeBlobOutputsJSON { + boolean supported; + Base64URLString blob; + boolean written; +};
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/idlharness.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/idlharness.html index 5f5d286b..0dd006e 100644 --- a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/idlharness.html +++ b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/idlharness.html
@@ -10,7 +10,7 @@ idl_test( ['largest-contentful-paint'], - ['performance-timeline', 'dom', 'hr-time'], + ['performance-timeline', 'dom', 'hr-time', 'paint-timing'], async (idl_array, t) => { idl_array.add_objects({ LargestContentfulPaint: ['lcp']
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/executor.sub.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/executor.sub.html index d27acfe..975a3e5 100644 --- a/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/executor.sub.html +++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/executor.sub.html
@@ -1,5 +1,8 @@ <!DOCTYPE html> <script src="/common/dispatcher/dispatcher.js" nonce="abc"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> <script src="utils.sub.js" nonce="abc"></script> <script nonce="abc"> // For a given string `str` that is escaped by WPT's `.sub.html` or @@ -45,6 +48,39 @@ } }); +// Add a link to the page in order to use during the test +function add_link(id, url) { + const link_element = document.createElement("a"); + const link_text = document.createTextNode(url); + link_element.setAttribute("href", url); + link_element.setAttribute("id", id); + link_element.appendChild(link_text); + document.body.appendChild(link_element); +} + +// "id" is the id of the link that we need to hover on in order +// to start the prefetch +async function start_non_eager_prefetch_on_hover(id) { + let target = document.getElementById(id); + + test_driver.set_test_context(window.opener); + // Inject the inputs to run this test. + await new test_driver.Actions().addPointer("mouse").pointerMove(0, 0, {origin: target}).send(); +} + +// "id" is the id of the link that we need to press on in order +// to start the prefetch +async function start_non_eager_prefetch_on_pointerdown(id) { + let target = document.getElementById(id); + + test_driver.set_test_context(window.opener); + // Inject the inputs to run this test. + // Move mouse pointer outside of the anchor so that we don't start the + // navigation before making sure the prefetch request started server-side. + await new test_driver.Actions().addPointer("mouse").pointerMove(0, 0, {origin: target}).pointerDown().pointerMove(0, 0).pointerUp().send(); +} + + // The fetch request's URL sent to the server. window.requestUrl = reverse_html_escape( "{{location[server]}}{{location[path]}}{{location[query]}}");
diff --git a/third_party/blink/web_tests/wpt_internal/speculation-rules/speculation-tags/prefetch-eagerness-pointer-down.https.html b/third_party/blink/web_tests/external/wpt/speculation-rules/speculation-tags/prefetch-eagerness-pointer-down.https.html similarity index 91% rename from third_party/blink/web_tests/wpt_internal/speculation-rules/speculation-tags/prefetch-eagerness-pointer-down.https.html rename to third_party/blink/web_tests/external/wpt/speculation-rules/speculation-tags/prefetch-eagerness-pointer-down.https.html index 4809189..cbf69ce 100644 --- a/third_party/blink/web_tests/wpt_internal/speculation-rules/speculation-tags/prefetch-eagerness-pointer-down.https.html +++ b/third_party/blink/web_tests/external/wpt/speculation-rules/speculation-tags/prefetch-eagerness-pointer-down.https.html
@@ -3,6 +3,8 @@ <meta name="timeout" content="long"> <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> <script src="/common/dispatcher/dispatcher.js"></script> <script src="/common/utils.js"></script> <script src="/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"></script> @@ -15,7 +17,7 @@ setup(() => assertSpeculationRulesIsSupported()); promise_test(async t => { - const agent = await spawnWindow(t, {executor:new URL("../prefetch/resources/executor-non-eager.html", document.baseURI)}); + const agent = await spawnWindow(t); const nextUrl = agent.getExecutorURL({ page: 2 }); await agent.forceSpeculationRules({ prefetch: [
diff --git a/third_party/blink/web_tests/wpt_internal/speculation-rules/speculation-tags/prefetch-eagerness-pointer-hover.https.html b/third_party/blink/web_tests/external/wpt/speculation-rules/speculation-tags/prefetch-eagerness-pointer-hover.https.html similarity index 87% rename from third_party/blink/web_tests/wpt_internal/speculation-rules/speculation-tags/prefetch-eagerness-pointer-hover.https.html rename to third_party/blink/web_tests/external/wpt/speculation-rules/speculation-tags/prefetch-eagerness-pointer-hover.https.html index 54e288ef..f5e749b 100644 --- a/third_party/blink/web_tests/wpt_internal/speculation-rules/speculation-tags/prefetch-eagerness-pointer-hover.https.html +++ b/third_party/blink/web_tests/external/wpt/speculation-rules/speculation-tags/prefetch-eagerness-pointer-hover.https.html
@@ -3,6 +3,8 @@ <meta name="timeout" content="long"> <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> <script src="/common/dispatcher/dispatcher.js"></script> <script src="/common/utils.js"></script> <script src="/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"></script> @@ -15,7 +17,7 @@ setup(() => assertSpeculationRulesIsSupported()); promise_test(async t => { - const agent = await spawnWindow(t, {executor:new URL("../prefetch/resources/executor-non-eager.html", document.baseURI)}); + const agent = await spawnWindow(t); const nextUrl = agent.getExecutorURL({ page: 2 }); await agent.forceSpeculationRules({ prefetch: [ @@ -35,7 +37,7 @@ await start_non_eager_prefetch_on_hover(linkId); }, [linkId]); - // TODO(crbug.com/381687257): This is the chromium-specific behavior. Revisit this wait when upstreaming this test. + // TODO(crbug.com/381687257): Remove this when 0ms hover trigger is supported. // Wait for longer than 200 ms for the onhover trigger to fire. await new Promise(resolve => t.step_timeout(resolve, 500)); await agent.navigate(nextUrl);
diff --git a/third_party/boringssl/src b/third_party/boringssl/src index 45cab55..136284f 160000 --- a/third_party/boringssl/src +++ b/third_party/boringssl/src
@@ -1 +1 @@ -Subproject commit 45cab558a838e1a546391406e7cbe1eec9b6e643 +Subproject commit 136284f8548bc7fb43e99e7f69e03fab57168e8b
diff --git a/third_party/catapult b/third_party/catapult index 5255e1a..0c5acc0 160000 --- a/third_party/catapult +++ b/third_party/catapult
@@ -1 +1 @@ -Subproject commit 5255e1a11a4d0bf747f304816a2523f952be9228 +Subproject commit 0c5acc073dd14893f2a75a51819ad81627098551
diff --git a/third_party/dawn b/third_party/dawn index f4cae4a..606e03a 160000 --- a/third_party/dawn +++ b/third_party/dawn
@@ -1 +1 @@ -Subproject commit f4cae4afdd4bca082d36025c66c7c284ed16a67f +Subproject commit 606e03a22bfe5686be18fcc934555f55f987c1e9
diff --git a/third_party/depot_tools b/third_party/depot_tools index d1f9fa6..977c374 160000 --- a/third_party/depot_tools +++ b/third_party/depot_tools
@@ -1 +1 @@ -Subproject commit d1f9fa6c922b20d8034fe4c7f3a62f8a824c561b +Subproject commit 977c37458fda472d8822a8b57e4a83a7bc747471
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src index 4cc0688..c049d05 160000 --- a/third_party/devtools-frontend/src +++ b/third_party/devtools-frontend/src
@@ -1 +1 @@ -Subproject commit 4cc0688b9538f497ae06e3a0f97431ffc86f4181 +Subproject commit c049d058ae5973f47a21fb1e4eb7a224174ef032
diff --git a/third_party/llvm-libc/src b/third_party/llvm-libc/src index 61d7ed3..4a2940b4 160000 --- a/third_party/llvm-libc/src +++ b/third_party/llvm-libc/src
@@ -1 +1 @@ -Subproject commit 61d7ed30110f97d6842304bc9035ee9cdae6b5e5 +Subproject commit 4a2940b40b394ca57312aa9bbc8af430fe9a5340
diff --git a/third_party/perfetto b/third_party/perfetto index 8295644..6ffe623 160000 --- a/third_party/perfetto +++ b/third_party/perfetto
@@ -1 +1 @@ -Subproject commit 829564494ca9d7bc1c8b15998c5f1f3536cdacf8 +Subproject commit 6ffe623d283575c00047cf9abf90c3659505a592
diff --git a/third_party/skia b/third_party/skia index 12dbc34d..0feee17 160000 --- a/third_party/skia +++ b/third_party/skia
@@ -1 +1 @@ -Subproject commit 12dbc34d742ee67f1b267dc276fe7d84631aa856 +Subproject commit 0feee17aeacab6b88ac8be3d8b35ae4c940eeea4
diff --git a/tools/metrics/histograms/metadata/android/enums.xml b/tools/metrics/histograms/metadata/android/enums.xml index a566906..e10afa1b 100644 --- a/tools/metrics/histograms/metadata/android/enums.xml +++ b/tools/metrics/histograms/metadata/android/enums.xml
@@ -1209,6 +1209,12 @@ <int value="2" label="Device lock restored"/> </enum> +<enum name="MissingNavbarInsetsReason"> + <int value="0" label="Other"/> + <int value="1" label="InMultiWindow"/> + <int value="2" label="InDesktopWindow"/> +</enum> + <enum name="MotionEventAction"> <int value="0" label="NONE"/> <int value="1" label="DOWN"/>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml index 704cd94..f92c4521 100644 --- a/tools/metrics/histograms/metadata/android/histograms.xml +++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -1922,6 +1922,18 @@ </summary> </histogram> +<histogram name="Android.EdgeToEdge.DrawToEdgeInUnsupportedConfiguration" + enum="Boolean" expires_after="2025-08-24"> + <owner>clhager@google.com</owner> + <owner>wenyufu@chromium.org</owner> + <owner>edge-to-edge@chromium.org</owner> + <summary> + Records when EdgeToEdgeControllerImpl#drawToEdge gets called on a device + whose configuration does not support edge-to-edge, with the boolean record + indicating whether drawToEdge was called due to a change in window state. + </summary> +</histogram> + <histogram name="Android.EdgeToEdge.Eligible" enum="Boolean" expires_after="2025-08-24"> <owner>lazzzis@google.com</owner> @@ -1943,6 +1955,18 @@ </summary> </histogram> +<histogram name="Android.EdgeToEdge.MissingNavbarInsets" + enum="MissingNavbarInsetsReason" expires_after="2025-12-01"> + <owner>clhager@google.com</owner> + <owner>wenyufu@chromium.org</owner> + <owner>edge-to-edge@chromium.org</owner> + <summary> + Records once during the initialization of the EdgeToEdgeControllerImpl + whether there were no navigation bar insets at all, which may indicate an + incorrect value from the OS. + </summary> +</histogram> + <histogram name="Android.EdgeToEdge.SupportedConfigurationSwitch" enum="SupportedConfigurationSwitch" expires_after="2025-12-01"> <owner>clhager@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/collaboration_service/enums.xml b/tools/metrics/histograms/metadata/collaboration_service/enums.xml index 860054f..0403b0d 100644 --- a/tools/metrics/histograms/metadata/collaboration_service/enums.xml +++ b/tools/metrics/histograms/metadata/collaboration_service/enums.xml
@@ -84,6 +84,13 @@ <enum name="CollaborationServiceLeaveOrDeleteEntryPoint"> <int value="0" label="Unknown"/> + <int value="1" label="AndroidTabGroupContextMenuLeave"/> + <int value="2" label="AndroidTabGroupContextMenuDelete"/> + <int value="3" label="AndroidTabGroupItemMenuLeave"/> + <int value="4" label="AndroidTabGroupItemMenuDelete"/> + <int value="5" label="AndroidTabGroupRow"/> + <int value="6" label="AndroidTabGridDialogLeave"/> + <int value="7" label="AndroidTabGridDialogDelete"/> </enum> <!-- LINT.ThenChange(//components/collaboration/public/collaboration_flow_entry_point.h:CollaborationServiceLeaveOrDeleteEntryPoint) -->
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index 49aa728..ee377702 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@ "full_remote_path": "perfetto-luci-artifacts/v50.1/linux-arm64/trace_processor_shell" }, "win": { - "hash": "b3d955460721019e770ca39601f366f2cf357bff", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/f2e70843f73b1e17fea3de511344f65b4bc8ad9c/trace_processor_shell.exe" + "hash": "b85dcca74e1212a0cc61c2b770824bef6a716fef", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/6ffe623d283575c00047cf9abf90c3659505a592/trace_processor_shell.exe" }, "linux_arm": { "hash": "99f971ca131f6d11c73f4b918099d434bdd8093c", @@ -21,8 +21,8 @@ "full_remote_path": "perfetto-luci-artifacts/v50.1/mac-arm64/trace_processor_shell" }, "linux": { - "hash": "4026dea19671293170bc7f302463b54f0e32b393", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/f2e70843f73b1e17fea3de511344f65b4bc8ad9c/trace_processor_shell" + "hash": "6a2c70dd8ecf91307dc9f297bcb297794666935f", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/6ffe623d283575c00047cf9abf90c3659505a592/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/ui/gl/direct_composition_support.cc b/ui/gl/direct_composition_support.cc index 737f1e1..44cf34d4 100644 --- a/ui/gl/direct_composition_support.cc +++ b/ui/gl/direct_composition_support.cc
@@ -1109,6 +1109,8 @@ features::kDXGIWaitableSwapChainMaxQueuedFrames.Get()); } +std::optional<bool> g_direct_composition_texture_supported; + void SetDirectCompositionOverlayWorkarounds( const DirectCompositionOverlayWorkarounds& workarounds) { // This has to be set before initializing overlay caps. @@ -1121,6 +1123,10 @@ g_force_rgb10a2_overlay_support = workarounds.force_rgb10a2_overlay_support; g_check_ycbcr_studio_g22_left_p709_for_nv12_support = workarounds.check_ycbcr_studio_g22_left_p709_for_nv12_support; + CHECK(!g_direct_composition_texture_supported.has_value()); + if (workarounds.disable_dcomp_texture) { + g_direct_composition_texture_supported = false; + } } void SetDirectCompositionMonitorInfoForTesting( @@ -1130,7 +1136,6 @@ g_primary_monitor_size = primary_monitor_size; } -std::optional<bool> g_direct_composition_texture_supported; bool DirectCompositionTextureSupported() { if (g_direct_composition_texture_supported.has_value()) {
diff --git a/ui/gl/direct_composition_support.h b/ui/gl/direct_composition_support.h index b4b8188b..afc2b71 100644 --- a/ui/gl/direct_composition_support.h +++ b/ui/gl/direct_composition_support.h
@@ -149,6 +149,15 @@ // Enable NV12 overlay support only when // DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 is supported. bool check_ycbcr_studio_g22_left_p709_for_nv12_support = false; + + // Before 10.0.26100.3624, Windows could return PRESENTATION_ERROR_LOST in + // some cases that are potentially recoverable by destroying all the DComp + // textures associated with our DComp device. However, Viz is not + // well-equipped to do this since most DComp textures are owned by pools in + // the renderer processes. This version and beyond, Windows has a fix to only + // return PRESENTATION_ERROR_LOST in truly unrecoverable cases, which we will + // treat the same as context loss. + bool disable_dcomp_texture = false; }; GL_EXPORT void SetDirectCompositionOverlayWorkarounds( const DirectCompositionOverlayWorkarounds& workarounds);
diff --git a/v8 b/v8 index 577adbe..1beea5f 160000 --- a/v8 +++ b/v8
@@ -1 +1 @@ -Subproject commit 577adbe0c15f8fa0643d5f91cc92a06c271c3456 +Subproject commit 1beea5ff6680e267f28e29054239ebcdd4ccb861